diff options
Diffstat (limited to 'libs')
453 files changed, 17945 insertions, 14128 deletions
diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp index 1d14fa0d55f7..70d52164ff74 100644 --- a/libs/androidfw/Android.bp +++ b/libs/androidfw/Android.bp @@ -14,15 +14,27 @@ // libandroidfw is partially built for the host (used by obbtool, aapt, and others) -cc_library { - name: "libandroidfw", - host_supported: true, +cc_defaults { + name: "libandroidfw_defaults", cflags: [ - "-Wall", "-Werror", - "-Wunused", "-Wunreachable-code", ], + target: { + windows: { + // The Windows compiler warns incorrectly for value initialization with {}. + cppflags: ["-Wno-missing-field-initializers"], + }, + host: { + cflags: ["-DSTATIC_ANDROIDFW_FOR_TOOLS"], + }, + }, +} + +cc_library { + name: "libandroidfw", + defaults: ["libandroidfw_defaults"], + host_supported: true, srcs: [ "ApkAssets.cpp", "Asset.cpp", @@ -31,6 +43,7 @@ cc_library { "AssetManager2.cpp", "AttributeResolution.cpp", "ChunkIterator.cpp", + "Idmap.cpp", "LoadedArsc.cpp", "LocaleData.cpp", "misc.cpp", @@ -67,7 +80,6 @@ cc_library { }, }, host: { - cflags: ["-DSTATIC_ANDROIDFW_FOR_TOOLS"], shared: { enabled: false, }, @@ -84,12 +96,87 @@ cc_library { }, windows: { enabled: true, - cppflags: ["-Wno-missing-field-initializers"], // The Windows compiler warns - // incorrectly for value - // initialization with {}. }, }, sanitize: { blacklist: "libandroidfw_blacklist.txt", }, } + +common_test_libs = [ + "libandroidfw", + "libbase", + "libcutils", + "libutils", + "libziparchive", +] + +cc_test { + name: "libandroidfw_tests", + host_supported: true, + defaults: ["libandroidfw_defaults"], + cppflags: [ + // This is to suppress warnings/errors from gtest + "-Wno-unnamed-type-template-args", + ], + srcs: [ + // Helpers/infra for testing. + "tests/CommonHelpers.cpp", + "tests/TestHelpers.cpp", + "tests/TestMain.cpp", + + // Actual tests. + "tests/ApkAssets_test.cpp", + "tests/AppAsLib_test.cpp", + "tests/Asset_test.cpp", + "tests/AssetManager2_test.cpp", + "tests/AttributeFinder_test.cpp", + "tests/AttributeResolution_test.cpp", + "tests/ByteBucketArray_test.cpp", + "tests/Config_test.cpp", + "tests/ConfigLocale_test.cpp", + "tests/Idmap_test.cpp", + "tests/LoadedArsc_test.cpp", + "tests/ResourceUtils_test.cpp", + "tests/ResTable_test.cpp", + "tests/Split_test.cpp", + "tests/StringPiece_test.cpp", + "tests/Theme_test.cpp", + "tests/TypeWrappers_test.cpp", + "tests/ZipUtils_test.cpp", + ], + static_libs: ["libgmock"], + target: { + android: { + srcs: [ + "tests/BackupData_test.cpp", + "tests/ObbFile_test.cpp", + ], + shared_libs: common_test_libs + ["libui"], + }, + host: { + static_libs: common_test_libs + ["liblog", "libz"], + }, + }, + data: ["tests/data/**/*.apk"], +} + +cc_benchmark { + name: "libandroidfw_benchmarks", + defaults: ["libandroidfw_defaults"], + srcs: [ + // Helpers/infra for benchmarking. + "tests/BenchMain.cpp", + "tests/BenchmarkHelpers.cpp", + "tests/CommonHelpers.cpp", + + // Actual benchmarks. + "tests/AssetManager2_bench.cpp", + "tests/AttributeResolution_bench.cpp", + "tests/SparseEntry_bench.cpp", + "tests/Theme_bench.cpp", + ], + shared_libs: common_test_libs, + data: ["tests/data/**/*.apk"], +} + diff --git a/libs/androidfw/Android.mk b/libs/androidfw/Android.mk deleted file mode 100644 index 68c51effd79d..000000000000 --- a/libs/androidfw/Android.mk +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright (C) 2010 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -LOCAL_PATH:= $(call my-dir) - -# Include subdirectory makefiles -# ============================================================ - -# If we're building with ONE_SHOT_MAKEFILE (mm, mmm), then what the framework -# team really wants is to build the stuff defined by this makefile. -ifeq (,$(ONE_SHOT_MAKEFILE)) -include $(call first-makefiles-under,$(LOCAL_PATH)) -endif diff --git a/libs/androidfw/ApkAssets.cpp b/libs/androidfw/ApkAssets.cpp index a5b1d29dbf91..da0205d72125 100644 --- a/libs/androidfw/ApkAssets.cpp +++ b/libs/androidfw/ApkAssets.cpp @@ -20,62 +20,141 @@ #include <algorithm> +#include "android-base/errors.h" +#include "android-base/file.h" #include "android-base/logging.h" +#include "android-base/unique_fd.h" +#include "android-base/utf8.h" +#include "utils/Compat.h" #include "utils/FileMap.h" #include "utils/Trace.h" #include "ziparchive/zip_archive.h" #include "androidfw/Asset.h" +#include "androidfw/Idmap.h" +#include "androidfw/ResourceTypes.h" #include "androidfw/Util.h" namespace android { +using base::SystemErrorCodeToString; +using base::unique_fd; + +static const std::string kResourcesArsc("resources.arsc"); + +ApkAssets::ApkAssets(void* unmanaged_handle, const std::string& path) + : zip_handle_(unmanaged_handle, ::CloseArchive), path_(path) { +} + std::unique_ptr<const ApkAssets> ApkAssets::Load(const std::string& path, bool system) { - return ApkAssets::LoadImpl(path, system, false /*load_as_shared_library*/); + return LoadImpl({} /*fd*/, path, nullptr, nullptr, system, false /*load_as_shared_library*/); } std::unique_ptr<const ApkAssets> ApkAssets::LoadAsSharedLibrary(const std::string& path, bool system) { - return ApkAssets::LoadImpl(path, system, true /*load_as_shared_library*/); + return LoadImpl({} /*fd*/, path, nullptr, nullptr, system, true /*load_as_shared_library*/); +} + +std::unique_ptr<const ApkAssets> ApkAssets::LoadOverlay(const std::string& idmap_path, + bool system) { + std::unique_ptr<Asset> idmap_asset = CreateAssetFromFile(idmap_path); + if (idmap_asset == nullptr) { + return {}; + } + + const StringPiece idmap_data( + reinterpret_cast<const char*>(idmap_asset->getBuffer(true /*wordAligned*/)), + static_cast<size_t>(idmap_asset->getLength())); + std::unique_ptr<const LoadedIdmap> loaded_idmap = LoadedIdmap::Load(idmap_data); + if (loaded_idmap == nullptr) { + LOG(ERROR) << "failed to load IDMAP " << idmap_path; + return {}; + } + return LoadImpl({} /*fd*/, loaded_idmap->OverlayApkPath(), std::move(idmap_asset), + std::move(loaded_idmap), system, false /*load_as_shared_library*/); +} + +std::unique_ptr<const ApkAssets> ApkAssets::LoadFromFd(unique_fd fd, + const std::string& friendly_name, + bool system, bool force_shared_lib) { + return LoadImpl(std::move(fd), friendly_name, nullptr /*idmap_asset*/, nullptr /*loaded_idmap*/, + system, force_shared_lib); +} + +std::unique_ptr<Asset> ApkAssets::CreateAssetFromFile(const std::string& path) { + unique_fd fd(base::utf8::open(path.c_str(), O_RDONLY | O_BINARY | O_CLOEXEC)); + if (fd == -1) { + LOG(ERROR) << "Failed to open file '" << path << "': " << SystemErrorCodeToString(errno); + return {}; + } + + const off64_t file_len = lseek64(fd, 0, SEEK_END); + if (file_len < 0) { + LOG(ERROR) << "Failed to get size of file '" << path << "': " << SystemErrorCodeToString(errno); + return {}; + } + + std::unique_ptr<FileMap> file_map = util::make_unique<FileMap>(); + if (!file_map->create(path.c_str(), fd, 0, static_cast<size_t>(file_len), true /*readOnly*/)) { + LOG(ERROR) << "Failed to mmap file '" << path << "': " << SystemErrorCodeToString(errno); + return {}; + } + return Asset::createFromUncompressedMap(std::move(file_map), Asset::AccessMode::ACCESS_RANDOM); } -std::unique_ptr<const ApkAssets> ApkAssets::LoadImpl(const std::string& path, bool system, - bool load_as_shared_library) { +std::unique_ptr<const ApkAssets> ApkAssets::LoadImpl( + unique_fd fd, const std::string& path, std::unique_ptr<Asset> idmap_asset, + std::unique_ptr<const LoadedIdmap> loaded_idmap, bool system, bool load_as_shared_library) { ATRACE_CALL(); + ::ZipArchiveHandle unmanaged_handle; - int32_t result = ::OpenArchive(path.c_str(), &unmanaged_handle); + int32_t result; + if (fd >= 0) { + result = + ::OpenArchiveFd(fd.release(), path.c_str(), &unmanaged_handle, true /*assume_ownership*/); + } else { + result = ::OpenArchive(path.c_str(), &unmanaged_handle); + } + if (result != 0) { - LOG(ERROR) << ::ErrorCodeString(result); + LOG(ERROR) << "Failed to open APK '" << path << "' " << ::ErrorCodeString(result); return {}; } // Wrap the handle in a unique_ptr so it gets automatically closed. - std::unique_ptr<ApkAssets> loaded_apk(new ApkAssets()); - loaded_apk->zip_handle_.reset(unmanaged_handle); + std::unique_ptr<ApkAssets> loaded_apk(new ApkAssets(unmanaged_handle, path)); - ::ZipString entry_name("resources.arsc"); + // Find the resource table. + ::ZipString entry_name(kResourcesArsc.c_str()); ::ZipEntry entry; result = ::FindEntry(loaded_apk->zip_handle_.get(), entry_name, &entry); if (result != 0) { - LOG(ERROR) << ::ErrorCodeString(result); - return {}; + // There is no resources.arsc, so create an empty LoadedArsc and return. + loaded_apk->loaded_arsc_ = LoadedArsc::CreateEmpty(); + return std::move(loaded_apk); } if (entry.method == kCompressDeflated) { - LOG(WARNING) << "resources.arsc is compressed."; + LOG(WARNING) << kResourcesArsc << " in APK '" << path << "' is compressed."; } - loaded_apk->path_ = path; - loaded_apk->resources_asset_ = - loaded_apk->Open("resources.arsc", Asset::AccessMode::ACCESS_BUFFER); + // Open the resource table via mmap unless it is compressed. This logic is taken care of by Open. + loaded_apk->resources_asset_ = loaded_apk->Open(kResourcesArsc, Asset::AccessMode::ACCESS_BUFFER); if (loaded_apk->resources_asset_ == nullptr) { + LOG(ERROR) << "Failed to open '" << kResourcesArsc << "' in APK '" << path << "'."; return {}; } + // Must retain ownership of the IDMAP Asset so that all pointers to its mmapped data remain valid. + loaded_apk->idmap_asset_ = std::move(idmap_asset); + + const StringPiece data( + reinterpret_cast<const char*>(loaded_apk->resources_asset_->getBuffer(true /*wordAligned*/)), + loaded_apk->resources_asset_->getLength()); loaded_apk->loaded_arsc_ = - LoadedArsc::Load(loaded_apk->resources_asset_->getBuffer(true /*wordAligned*/), - loaded_apk->resources_asset_->getLength(), system, load_as_shared_library); + LoadedArsc::Load(data, loaded_idmap.get(), system, load_as_shared_library); if (loaded_apk->loaded_arsc_ == nullptr) { + LOG(ERROR) << "Failed to load '" << kResourcesArsc << "' in APK '" << path << "'."; return {}; } @@ -91,7 +170,6 @@ std::unique_ptr<Asset> ApkAssets::Open(const std::string& path, Asset::AccessMod ::ZipEntry entry; int32_t result = ::FindEntry(zip_handle_.get(), name, &entry); if (result != 0) { - LOG(ERROR) << "No entry '" << path << "' found in APK '" << path_ << "'"; return {}; } diff --git a/libs/androidfw/AssetManager.cpp b/libs/androidfw/AssetManager.cpp index 5603508eaf09..0485625e81e8 100644 --- a/libs/androidfw/AssetManager.cpp +++ b/libs/androidfw/AssetManager.cpp @@ -148,6 +148,18 @@ AssetManager::~AssetManager() { int count = android_atomic_dec(&gCount); if (kIsDebug) { ALOGI("Destroying AssetManager in %p #%d\n", this, count); + } else { + ALOGV("Destroying AssetManager in %p #%d\n", this, count); + } + + // Manually close any fd paths for which we have not yet opened their zip (which + // will take ownership of the fd and close it when done). + for (size_t i=0; i<mAssetPaths.size(); i++) { + ALOGV("Cleaning path #%d: fd=%d, zip=%p", (int)i, mAssetPaths[i].rawFd, + mAssetPaths[i].zip.get()); + if (mAssetPaths[i].rawFd >= 0 && mAssetPaths[i].zip == NULL) { + close(mAssetPaths[i].rawFd); + } } delete mConfig; @@ -194,7 +206,7 @@ bool AssetManager::addAssetPath( ap.type == kFileTypeDirectory ? "dir" : "zip", ap.path.string()); ap.isSystemAsset = isSystemAsset; - mAssetPaths.add(ap); + ssize_t apPos = mAssetPaths.add(ap); // new paths are always added at the end if (cookie) { @@ -211,7 +223,7 @@ bool AssetManager::addAssetPath( #endif if (mResources != NULL) { - appendPathToResTable(ap, appAsLib); + appendPathToResTable(mAssetPaths.editItemAt(apPos), appAsLib); } return true; @@ -280,7 +292,35 @@ bool AssetManager::addOverlayPath(const String8& packagePath, int32_t* cookie) } return true; - } +} + +bool AssetManager::addAssetFd( + int fd, const String8& debugPathName, int32_t* cookie, bool appAsLib, + bool assume_ownership) { + AutoMutex _l(mLock); + + asset_path ap; + + ap.path = debugPathName; + ap.rawFd = fd; + ap.type = kFileTypeRegular; + ap.assumeOwnership = assume_ownership; + + ALOGV("In %p Asset fd %d name: %s", this, fd, ap.path.string()); + + ssize_t apPos = mAssetPaths.add(ap); + + // new paths are always added at the end + if (cookie) { + *cookie = static_cast<int32_t>(mAssetPaths.size()); + } + + if (mResources != NULL) { + appendPathToResTable(mAssetPaths.editItemAt(apPos), appAsLib); + } + + return true; +} bool AssetManager::createIdmap(const char* targetApkPath, const char* overlayApkPath, uint32_t targetCrc, uint32_t overlayCrc, uint32_t** outData, size_t* outSize) @@ -406,7 +446,8 @@ Asset* AssetManager::open(const char* fileName, AccessMode mode) i--; ALOGV("Looking for asset '%s' in '%s'\n", assetName.string(), mAssetPaths.itemAt(i).path.string()); - Asset* pAsset = openNonAssetInPathLocked(assetName.string(), mode, mAssetPaths.itemAt(i)); + Asset* pAsset = openNonAssetInPathLocked(assetName.string(), mode, + mAssetPaths.editItemAt(i)); if (pAsset != NULL) { return pAsset != kExcludedAsset ? pAsset : NULL; } @@ -435,7 +476,7 @@ Asset* AssetManager::openNonAsset(const char* fileName, AccessMode mode, int32_t i--; ALOGV("Looking for non-asset '%s' in '%s'\n", fileName, mAssetPaths.itemAt(i).path.string()); Asset* pAsset = openNonAssetInPathLocked( - fileName, mode, mAssetPaths.itemAt(i)); + fileName, mode, mAssetPaths.editItemAt(i)); if (pAsset != NULL) { if (outCookie != NULL) *outCookie = static_cast<int32_t>(i + 1); return pAsset != kExcludedAsset ? pAsset : NULL; @@ -457,7 +498,7 @@ Asset* AssetManager::openNonAsset(const int32_t cookie, const char* fileName, Ac ALOGV("Looking for non-asset '%s' in '%s'\n", fileName, mAssetPaths.itemAt(which).path.string()); Asset* pAsset = openNonAssetInPathLocked( - fileName, mode, mAssetPaths.itemAt(which)); + fileName, mode, mAssetPaths.editItemAt(which)); if (pAsset != NULL) { return pAsset != kExcludedAsset ? pAsset : NULL; } @@ -491,7 +532,7 @@ FileType AssetManager::getFileType(const char* fileName) } } -bool AssetManager::appendPathToResTable(const asset_path& ap, bool appAsLib) const { +bool AssetManager::appendPathToResTable(asset_path& ap, bool appAsLib) const { // skip those ap's that correspond to system overlays if (ap.isSystemOverlay) { return true; @@ -505,7 +546,7 @@ bool AssetManager::appendPathToResTable(const asset_path& ap, bool appAsLib) con Asset* idmap = openIdmapLocked(ap); size_t nextEntryIdx = mResources->getTableCount(); ALOGV("Looking for resource asset in '%s'\n", ap.path.string()); - if (ap.type != kFileTypeDirectory) { + if (ap.type != kFileTypeDirectory && ap.rawFd < 0) { if (nextEntryIdx == 0) { // The first item is typically the framework resources, // which we want to avoid parsing every time. @@ -609,7 +650,8 @@ const ResTable* AssetManager::getResTable(bool required) const bool onlyEmptyResources = true; const size_t N = mAssetPaths.size(); for (size_t i=0; i<N; i++) { - bool empty = appendPathToResTable(mAssetPaths.itemAt(i)); + bool empty = appendPathToResTable( + const_cast<AssetManager*>(this)->mAssetPaths.editItemAt(i)); onlyEmptyResources = onlyEmptyResources && empty; } @@ -734,10 +776,12 @@ void AssetManager::getLocales(Vector<String8>* locales, bool includeSystemLocale * be used. */ Asset* AssetManager::openNonAssetInPathLocked(const char* fileName, AccessMode mode, - const asset_path& ap) + asset_path& ap) { Asset* pAsset = NULL; + ALOGV("openNonAssetInPath: name=%s type=%d fd=%d", fileName, ap.type, ap.rawFd); + /* look at the filesystem on disk */ if (ap.type == kFileTypeDirectory) { String8 path(ap.path); @@ -752,7 +796,7 @@ Asset* AssetManager::openNonAssetInPathLocked(const char* fileName, AccessMode m } if (pAsset != NULL) { - //printf("FOUND NA '%s' on disk\n", fileName); + ALOGV("FOUND NA '%s' on disk", fileName); pAsset->setAssetSource(path); } @@ -763,10 +807,10 @@ Asset* AssetManager::openNonAssetInPathLocked(const char* fileName, AccessMode m /* check the appropriate Zip file */ ZipFileRO* pZip = getZipFileLocked(ap); if (pZip != NULL) { - //printf("GOT zip, checking NA '%s'\n", (const char*) path); + ALOGV("GOT zip, checking NA '%s'", (const char*) path); ZipEntryRO entry = pZip->findEntryByName(path.string()); if (entry != NULL) { - //printf("FOUND NA in Zip file for %s\n", appName ? appName : kAppCommon); + ALOGV("FOUND NA in Zip file for %s", (const char*) path); pAsset = openAssetFromZipLocked(pZip, entry, mode, path); pZip->releaseEntry(entry); } @@ -813,11 +857,23 @@ String8 AssetManager::createPathNameLocked(const asset_path& ap, const char* roo * Return a pointer to one of our open Zip archives. Returns NULL if no * matching Zip file exists. */ -ZipFileRO* AssetManager::getZipFileLocked(const asset_path& ap) +ZipFileRO* AssetManager::getZipFileLocked(asset_path& ap) { - ALOGV("getZipFileLocked() in %p\n", this); + ALOGV("getZipFileLocked() in %p: ap=%p zip=%p", this, &ap, ap.zip.get()); + + if (ap.zip != NULL) { + return ap.zip->getZip(); + } + + if (ap.rawFd < 0) { + ALOGV("getZipFileLocked: Creating new zip from path %s", ap.path.string()); + ap.zip = mZipSet.getSharedZip(ap.path); + } else { + ALOGV("getZipFileLocked: Creating new zip from fd %d", ap.rawFd); + ap.zip = SharedZip::create(ap.rawFd, ap.path); - return mZipSet.getZip(ap.path); + } + return ap.zip != NULL ? ap.zip->getZip() : NULL; } /* @@ -1374,6 +1430,21 @@ AssetManager::SharedZip::SharedZip(const String8& path, time_t modWhen) } } +AssetManager::SharedZip::SharedZip(int fd, const String8& path) + : mPath(path), mZipFile(NULL), mModWhen(0), + mResourceTableAsset(NULL), mResourceTable(NULL) +{ + if (kIsDebug) { + ALOGI("Creating SharedZip %p fd=%d %s\n", this, fd, (const char*)mPath); + } + ALOGV("+++ opening zip fd=%d '%s'\n", fd, mPath.string()); + mZipFile = ZipFileRO::openFd(fd, mPath.string()); + if (mZipFile == NULL) { + ::close(fd); + ALOGD("failed to open Zip archive fd=%d '%s'\n", fd, mPath.string()); + } +} + sp<AssetManager::SharedZip> AssetManager::SharedZip::get(const String8& path, bool createIfNotPresent) { @@ -1389,7 +1460,11 @@ sp<AssetManager::SharedZip> AssetManager::SharedZip::get(const String8& path, zip = new SharedZip(path, modWhen); gOpen.add(path, zip); return zip; +} +sp<AssetManager::SharedZip> AssetManager::SharedZip::create(int fd, const String8& path) +{ + return new SharedZip(fd, path); } ZipFileRO* AssetManager::SharedZip::getZip() @@ -1500,19 +1575,23 @@ void AssetManager::ZipSet::closeZip(int idx) mZipFile.editItemAt(idx) = NULL; } - /* * Retrieve the appropriate Zip file from the set. */ ZipFileRO* AssetManager::ZipSet::getZip(const String8& path) { + return getSharedZip(path)->getZip(); +} + +const sp<AssetManager::SharedZip> AssetManager::ZipSet::getSharedZip(const String8& path) +{ int idx = getIndex(path); sp<SharedZip> zip = mZipFile[idx]; if (zip == NULL) { zip = SharedZip::get(path); mZipFile.editItemAt(idx) = zip; } - return zip->getZip(); + return zip; } Asset* AssetManager::ZipSet::getZipResourceTableAsset(const String8& path) diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp index 5667f9283241..a558ff7ccfc1 100644 --- a/libs/androidfw/AssetManager2.cpp +++ b/libs/androidfw/AssetManager2.cpp @@ -18,6 +18,7 @@ #include "androidfw/AssetManager2.h" +#include <iterator> #include <set> #include "android-base/logging.h" @@ -35,12 +36,40 @@ namespace android { -AssetManager2::AssetManager2() { memset(&configuration_, 0, sizeof(configuration_)); } +struct FindEntryResult { + // A pointer to the resource table entry for this resource. + // If the size of the entry is > sizeof(ResTable_entry), it can be cast to + // a ResTable_map_entry and processed as a bag/map. + const ResTable_entry* entry; + + // The configuration for which the resulting entry was defined. This is already swapped to host + // endianness. + ResTable_config config; + + // The bitmask of configuration axis with which the resource value varies. + uint32_t type_flags; + + // The dynamic package ID map for the package from which this resource came from. + const DynamicRefTable* dynamic_ref_table; + + // The string pool reference to the type's name. This uses a different string pool than + // the global string pool, but this is hidden from the caller. + StringPoolRef type_string_ref; + + // The string pool reference to the entry's name. This uses a different string pool than + // the global string pool, but this is hidden from the caller. + StringPoolRef entry_string_ref; +}; + +AssetManager2::AssetManager2() { + memset(&configuration_, 0, sizeof(configuration_)); +} bool AssetManager2::SetApkAssets(const std::vector<const ApkAssets*>& apk_assets, bool invalidate_caches) { apk_assets_ = apk_assets; BuildDynamicRefTable(); + RebuildFilterList(); if (invalidate_caches) { InvalidateCaches(static_cast<uint32_t>(-1)); } @@ -55,9 +84,9 @@ void AssetManager2::BuildDynamicRefTable() { int next_package_id = 0x02; const size_t apk_assets_count = apk_assets_.size(); for (size_t i = 0; i < apk_assets_count; i++) { - const ApkAssets* apk_asset = apk_assets_[i]; - for (const std::unique_ptr<const LoadedPackage>& package : - apk_asset->GetLoadedArsc()->GetPackages()) { + const LoadedArsc* loaded_arsc = apk_assets_[i]->GetLoadedArsc(); + + for (const std::unique_ptr<const LoadedPackage>& package : loaded_arsc->GetPackages()) { // Get the package ID or assign one if a shared library. int package_id; if (package->IsDynamic()) { @@ -76,7 +105,7 @@ void AssetManager2::BuildDynamicRefTable() { PackageGroup* package_group = &package_groups_[idx]; // Add the package and to the set of packages with the same ID. - package_group->packages_.push_back(package.get()); + package_group->packages_.push_back(ConfiguredPackage{package.get(), {}}); package_group->cookies_.push_back(static_cast<ApkAssetsCookie>(i)); // Add the package name -> build time ID mappings. @@ -91,7 +120,7 @@ void AssetManager2::BuildDynamicRefTable() { // Now assign the runtime IDs so that we have a build-time to runtime ID map. const auto package_groups_end = package_groups_.end(); for (auto iter = package_groups_.begin(); iter != package_groups_end; ++iter) { - const std::string& package_name = iter->packages_[0]->GetPackageName(); + const std::string& package_name = iter->packages_[0].loaded_package_->GetPackageName(); for (auto iter2 = package_groups_.begin(); iter2 != package_groups_end; ++iter2) { iter2->dynamic_ref_table.addMapping(String16(package_name.c_str(), package_name.size()), iter->dynamic_ref_table.mAssignedPackageId); @@ -105,17 +134,20 @@ void AssetManager2::DumpToLog() const { std::string list; for (size_t i = 0; i < package_ids_.size(); i++) { if (package_ids_[i] != 0xff) { - base::StringAppendF(&list, "%02x -> %d, ", (int) i, package_ids_[i]); + base::StringAppendF(&list, "%02x -> %d, ", (int)i, package_ids_[i]); } } LOG(INFO) << "Package ID map: " << list; - for (const auto& package_group: package_groups_) { - list = ""; - for (const auto& package : package_group.packages_) { - base::StringAppendF(&list, "%s(%02x), ", package->GetPackageName().c_str(), package->GetPackageId()); - } - LOG(INFO) << base::StringPrintf("PG (%02x): ", package_group.dynamic_ref_table.mAssignedPackageId) << list; + for (const auto& package_group : package_groups_) { + list = ""; + for (const auto& package : package_group.packages_) { + base::StringAppendF(&list, "%s(%02x), ", package.loaded_package_->GetPackageName().c_str(), + package.loaded_package_->GetPackageId()); + } + LOG(INFO) << base::StringPrintf("PG (%02x): ", + package_group.dynamic_ref_table.mAssignedPackageId) + << list; } } @@ -154,52 +186,54 @@ void AssetManager2::SetConfiguration(const ResTable_config& configuration) { configuration_ = configuration; if (diff) { + RebuildFilterList(); InvalidateCaches(static_cast<uint32_t>(diff)); } } std::set<ResTable_config> AssetManager2::GetResourceConfigurations(bool exclude_system, - bool exclude_mipmap) { + bool exclude_mipmap) const { ATRACE_CALL(); std::set<ResTable_config> configurations; for (const PackageGroup& package_group : package_groups_) { - for (const LoadedPackage* package : package_group.packages_) { - if (exclude_system && package->IsSystem()) { + for (const ConfiguredPackage& package : package_group.packages_) { + if (exclude_system && package.loaded_package_->IsSystem()) { continue; } - package->CollectConfigurations(exclude_mipmap, &configurations); + package.loaded_package_->CollectConfigurations(exclude_mipmap, &configurations); } } return configurations; } std::set<std::string> AssetManager2::GetResourceLocales(bool exclude_system, - bool merge_equivalent_languages) { + bool merge_equivalent_languages) const { ATRACE_CALL(); std::set<std::string> locales; for (const PackageGroup& package_group : package_groups_) { - for (const LoadedPackage* package : package_group.packages_) { - if (exclude_system && package->IsSystem()) { + for (const ConfiguredPackage& package : package_group.packages_) { + if (exclude_system && package.loaded_package_->IsSystem()) { continue; } - package->CollectLocales(merge_equivalent_languages, &locales); + package.loaded_package_->CollectLocales(merge_equivalent_languages, &locales); } } return locales; } -std::unique_ptr<Asset> AssetManager2::Open(const std::string& filename, Asset::AccessMode mode) { +std::unique_ptr<Asset> AssetManager2::Open(const std::string& filename, + Asset::AccessMode mode) const { const std::string new_path = "assets/" + filename; return OpenNonAsset(new_path, mode); } std::unique_ptr<Asset> AssetManager2::Open(const std::string& filename, ApkAssetsCookie cookie, - Asset::AccessMode mode) { + Asset::AccessMode mode) const { const std::string new_path = "assets/" + filename; return OpenNonAsset(new_path, cookie, mode); } -std::unique_ptr<AssetDir> AssetManager2::OpenDir(const std::string& dirname) { +std::unique_ptr<AssetDir> AssetManager2::OpenDir(const std::string& dirname) const { ATRACE_CALL(); std::string full_path = "assets/" + dirname; @@ -233,7 +267,7 @@ std::unique_ptr<AssetDir> AssetManager2::OpenDir(const std::string& dirname) { // is inconsistent for split APKs. std::unique_ptr<Asset> AssetManager2::OpenNonAsset(const std::string& filename, Asset::AccessMode mode, - ApkAssetsCookie* out_cookie) { + ApkAssetsCookie* out_cookie) const { ATRACE_CALL(); for (int32_t i = apk_assets_.size() - 1; i >= 0; i--) { std::unique_ptr<Asset> asset = apk_assets_[i]->Open(filename, mode); @@ -252,7 +286,8 @@ std::unique_ptr<Asset> AssetManager2::OpenNonAsset(const std::string& filename, } std::unique_ptr<Asset> AssetManager2::OpenNonAsset(const std::string& filename, - ApkAssetsCookie cookie, Asset::AccessMode mode) { + ApkAssetsCookie cookie, + Asset::AccessMode mode) const { ATRACE_CALL(); if (cookie < 0 || static_cast<size_t>(cookie) >= apk_assets_.size()) { return {}; @@ -261,16 +296,13 @@ std::unique_ptr<Asset> AssetManager2::OpenNonAsset(const std::string& filename, } ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_override, - bool stop_at_first_match, LoadedArscEntry* out_entry, - ResTable_config* out_selected_config, - uint32_t* out_flags) { - ATRACE_CALL(); - + bool /*stop_at_first_match*/, + FindEntryResult* out_entry) const { // Might use this if density_override != 0. ResTable_config density_override_config; // Select our configuration or generate a density override configuration. - ResTable_config* desired_config = &configuration_; + const ResTable_config* desired_config = &configuration_; if (density_override != 0 && density_override != configuration_.density) { density_override_config = configuration_; density_override_config.density = density_override; @@ -284,68 +316,146 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri const uint32_t package_id = get_package_id(resid); const uint8_t type_idx = get_type_id(resid) - 1; - const uint16_t entry_id = get_entry_id(resid); + const uint16_t entry_idx = get_entry_id(resid); - const uint8_t idx = package_ids_[package_id]; - if (idx == 0xff) { + const uint8_t package_idx = package_ids_[package_id]; + if (package_idx == 0xff) { LOG(ERROR) << base::StringPrintf("No package ID %02x found for ID 0x%08x.", package_id, resid); return kInvalidCookie; } - LoadedArscEntry best_entry; - ResTable_config best_config; - ApkAssetsCookie best_cookie = kInvalidCookie; - uint32_t cumulated_flags = 0u; - - const PackageGroup& package_group = package_groups_[idx]; + const PackageGroup& package_group = package_groups_[package_idx]; const size_t package_count = package_group.packages_.size(); - for (size_t i = 0; i < package_count; i++) { - LoadedArscEntry current_entry; - ResTable_config current_config; - uint32_t current_flags = 0; - - const LoadedPackage* loaded_package = package_group.packages_[i]; - if (!loaded_package->FindEntry(type_idx, entry_id, *desired_config, ¤t_entry, - ¤t_config, ¤t_flags)) { + + ApkAssetsCookie best_cookie = kInvalidCookie; + const LoadedPackage* best_package = nullptr; + const ResTable_type* best_type = nullptr; + const ResTable_config* best_config = nullptr; + ResTable_config best_config_copy; + uint32_t best_offset = 0u; + uint32_t type_flags = 0u; + + // If desired_config is the same as the set configuration, then we can use our filtered list + // and we don't need to match the configurations, since they already matched. + const bool use_fast_path = desired_config == &configuration_; + + for (size_t pi = 0; pi < package_count; pi++) { + const ConfiguredPackage& loaded_package_impl = package_group.packages_[pi]; + const LoadedPackage* loaded_package = loaded_package_impl.loaded_package_; + ApkAssetsCookie cookie = package_group.cookies_[pi]; + + // If the type IDs are offset in this package, we need to take that into account when searching + // for a type. + const TypeSpec* type_spec = loaded_package->GetTypeSpecByTypeIndex(type_idx); + if (UNLIKELY(type_spec == nullptr)) { continue; } - cumulated_flags |= current_flags; + uint16_t local_entry_idx = entry_idx; - if (best_cookie == kInvalidCookie || current_config.isBetterThan(best_config, desired_config)) { - best_entry = current_entry; - best_config = current_config; - best_cookie = package_group.cookies_[i]; - if (stop_at_first_match) { - break; + // If there is an IDMAP supplied with this package, translate the entry ID. + if (type_spec->idmap_entries != nullptr) { + if (!LoadedIdmap::Lookup(type_spec->idmap_entries, local_entry_idx, &local_entry_idx)) { + // There is no mapping, so the resource is not meant to be in this overlay package. + continue; + } + } + + type_flags |= type_spec->GetFlagsForEntryIndex(local_entry_idx); + + // If the package is an overlay, then even configurations that are the same MUST be chosen. + const bool package_is_overlay = loaded_package->IsOverlay(); + + const FilteredConfigGroup& filtered_group = loaded_package_impl.filtered_configs_[type_idx]; + if (use_fast_path) { + const std::vector<ResTable_config>& candidate_configs = filtered_group.configurations; + const size_t type_count = candidate_configs.size(); + for (uint32_t i = 0; i < type_count; i++) { + const ResTable_config& this_config = candidate_configs[i]; + + // We can skip calling ResTable_config::match() because we know that all candidate + // configurations that do NOT match have been filtered-out. + if ((best_config == nullptr || this_config.isBetterThan(*best_config, desired_config)) || + (package_is_overlay && this_config.compare(*best_config) == 0)) { + // The configuration matches and is better than the previous selection. + // Find the entry value if it exists for this configuration. + const ResTable_type* type_chunk = filtered_group.types[i]; + const uint32_t offset = LoadedPackage::GetEntryOffset(type_chunk, local_entry_idx); + if (offset == ResTable_type::NO_ENTRY) { + continue; + } + + best_cookie = cookie; + best_package = loaded_package; + best_type = type_chunk; + best_config = &this_config; + best_offset = offset; + } + } + } else { + // This is the slower path, which doesn't use the filtered list of configurations. + // Here we must read the ResTable_config from the mmapped APK, convert it to host endianness + // and fill in any new fields that did not exist when the APK was compiled. + // Furthermore when selecting configurations we can't just record the pointer to the + // ResTable_config, we must copy it. + const auto iter_end = type_spec->types + type_spec->type_count; + for (auto iter = type_spec->types; iter != iter_end; ++iter) { + ResTable_config this_config; + this_config.copyFromDtoH((*iter)->config); + + if (this_config.match(*desired_config)) { + if ((best_config == nullptr || this_config.isBetterThan(*best_config, desired_config)) || + (package_is_overlay && this_config.compare(*best_config) == 0)) { + // The configuration matches and is better than the previous selection. + // Find the entry value if it exists for this configuration. + const uint32_t offset = LoadedPackage::GetEntryOffset(*iter, local_entry_idx); + if (offset == ResTable_type::NO_ENTRY) { + continue; + } + + best_cookie = cookie; + best_package = loaded_package; + best_type = *iter; + best_config_copy = this_config; + best_config = &best_config_copy; + best_offset = offset; + } + } } } } - if (best_cookie == kInvalidCookie) { + if (UNLIKELY(best_cookie == kInvalidCookie)) { + return kInvalidCookie; + } + + const ResTable_entry* best_entry = LoadedPackage::GetEntryFromOffset(best_type, best_offset); + if (UNLIKELY(best_entry == nullptr)) { return kInvalidCookie; } - *out_entry = best_entry; + out_entry->entry = best_entry; + out_entry->config = *best_config; + out_entry->type_flags = type_flags; + out_entry->type_string_ref = StringPoolRef(best_package->GetTypeStringPool(), best_type->id - 1); + out_entry->entry_string_ref = + StringPoolRef(best_package->GetKeyStringPool(), best_entry->key.index); out_entry->dynamic_ref_table = &package_group.dynamic_ref_table; - *out_selected_config = best_config; - *out_flags = cumulated_flags; return best_cookie; } -bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) { +bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) const { ATRACE_CALL(); - LoadedArscEntry entry; - ResTable_config config; - uint32_t flags = 0u; - ApkAssetsCookie cookie = FindEntry(resid, 0u /* density_override */, - true /* stop_at_first_match */, &entry, &config, &flags); + FindEntryResult entry; + ApkAssetsCookie cookie = + FindEntry(resid, 0u /* density_override */, true /* stop_at_first_match */, &entry); if (cookie == kInvalidCookie) { return false; } - const LoadedPackage* package = apk_assets_[cookie]->GetLoadedArsc()->GetPackageForId(resid); + const LoadedPackage* package = + apk_assets_[cookie]->GetLoadedArsc()->GetPackageById(get_package_id(resid)); if (package == nullptr) { return false; } @@ -373,30 +483,31 @@ bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) { return true; } -bool AssetManager2::GetResourceFlags(uint32_t resid, uint32_t* out_flags) { - LoadedArscEntry entry; - ResTable_config config; - ApkAssetsCookie cookie = FindEntry(resid, 0u /* density_override */, - false /* stop_at_first_match */, &entry, &config, out_flags); - return cookie != kInvalidCookie; +bool AssetManager2::GetResourceFlags(uint32_t resid, uint32_t* out_flags) const { + FindEntryResult entry; + ApkAssetsCookie cookie = + FindEntry(resid, 0u /* density_override */, false /* stop_at_first_match */, &entry); + if (cookie != kInvalidCookie) { + *out_flags = entry.type_flags; + return cookie; + } + return kInvalidCookie; } ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag, uint16_t density_override, Res_value* out_value, ResTable_config* out_selected_config, - uint32_t* out_flags) { + uint32_t* out_flags) const { ATRACE_CALL(); - LoadedArscEntry entry; - ResTable_config config; - uint32_t flags = 0u; + FindEntryResult entry; ApkAssetsCookie cookie = - FindEntry(resid, density_override, false /* stop_at_first_match */, &entry, &config, &flags); + FindEntry(resid, density_override, false /* stop_at_first_match */, &entry); if (cookie == kInvalidCookie) { return kInvalidCookie; } - if (dtohl(entry.entry->flags) & ResTable_entry::FLAG_COMPLEX) { + if (dtohs(entry.entry->flags) & ResTable_entry::FLAG_COMPLEX) { if (!may_be_bag) { LOG(ERROR) << base::StringPrintf("Resource %08x is a complex map type.", resid); return kInvalidCookie; @@ -405,8 +516,8 @@ ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag, // Create a reference since we can't represent this complex type as a Res_value. out_value->dataType = Res_value::TYPE_REFERENCE; out_value->data = resid; - *out_selected_config = config; - *out_flags = flags; + *out_selected_config = entry.config; + *out_flags = entry.type_flags; return cookie; } @@ -417,25 +528,22 @@ ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag, // Convert the package ID to the runtime assigned package ID. entry.dynamic_ref_table->lookupResourceValue(out_value); - *out_selected_config = config; - *out_flags = flags; + *out_selected_config = entry.config; + *out_flags = entry.type_flags; return cookie; } ApkAssetsCookie AssetManager2::ResolveReference(ApkAssetsCookie cookie, Res_value* in_out_value, ResTable_config* in_out_selected_config, uint32_t* in_out_flags, - uint32_t* out_last_reference) { + uint32_t* out_last_reference) const { ATRACE_CALL(); constexpr const int kMaxIterations = 20; - *out_last_reference = 0u; for (size_t iteration = 0u; in_out_value->dataType == Res_value::TYPE_REFERENCE && in_out_value->data != 0u && iteration < kMaxIterations; iteration++) { - if (out_last_reference != nullptr) { - *out_last_reference = in_out_value->data; - } + *out_last_reference = in_out_value->data; uint32_t new_flags = 0u; cookie = GetResource(in_out_value->data, true /*may_be_bag*/, 0u /*density_override*/, in_out_value, in_out_selected_config, &new_flags); @@ -461,11 +569,9 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid) { return cached_iter->second.get(); } - LoadedArscEntry entry; - ResTable_config config; - uint32_t flags = 0u; - ApkAssetsCookie cookie = FindEntry(resid, 0u /* density_override */, - false /* stop_at_first_match */, &entry, &config, &flags); + FindEntryResult entry; + ApkAssetsCookie cookie = + FindEntry(resid, 0u /* density_override */, false /* stop_at_first_match */, &entry); if (cookie == kInvalidCookie) { return nullptr; } @@ -498,18 +604,26 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid) { // Attributes, arrays, etc don't have a resource id as the name. They specify // other data, which would be wrong to change via a lookup. if (entry.dynamic_ref_table->lookupResourceId(&new_key) != NO_ERROR) { - LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", new_key, resid); + LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", new_key, + resid); return nullptr; } } new_entry->cookie = cookie; - new_entry->value.copyFrom_dtoh(map_entry->value); new_entry->key = new_key; new_entry->key_pool = nullptr; new_entry->type_pool = nullptr; + new_entry->value.copyFrom_dtoh(map_entry->value); + status_t err = entry.dynamic_ref_table->lookupResourceValue(&new_entry->value); + if (err != NO_ERROR) { + LOG(ERROR) << base::StringPrintf( + "Failed to resolve value t=0x%02x d=0x%08x for key 0x%08x.", new_entry->value.dataType, + new_entry->value.data, new_key); + return nullptr; + } ++new_entry; } - new_bag->type_spec_flags = flags; + new_bag->type_spec_flags = entry.type_flags; new_bag->entry_count = static_cast<uint32_t>(entry_count); ResolvedBag* result = new_bag.get(); cached_bags_[resid] = std::move(new_bag); @@ -523,18 +637,16 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid) { const ResolvedBag* parent_bag = GetBag(parent_resid); if (parent_bag == nullptr) { // Failed to get the parent that should exist. - LOG(ERROR) << base::StringPrintf("Failed to find parent 0x%08x of bag 0x%08x.", parent_resid, resid); + LOG(ERROR) << base::StringPrintf("Failed to find parent 0x%08x of bag 0x%08x.", parent_resid, + resid); return nullptr; } - // Combine flags from the parent and our own bag. - flags |= parent_bag->type_spec_flags; - // Create the max possible entries we can make. Once we construct the bag, // we will realloc to fit to size. const size_t max_count = parent_bag->entry_count + dtohl(map->count); - ResolvedBag* new_bag = reinterpret_cast<ResolvedBag*>( - malloc(sizeof(ResolvedBag) + (max_count * sizeof(ResolvedBag::Entry)))); + util::unique_cptr<ResolvedBag> new_bag{reinterpret_cast<ResolvedBag*>( + malloc(sizeof(ResolvedBag) + (max_count * sizeof(ResolvedBag::Entry))))}; ResolvedBag::Entry* new_entry = new_bag->entries; const ResolvedBag::Entry* parent_entry = parent_bag->entries; @@ -545,7 +657,8 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid) { uint32_t child_key = dtohl(map_entry->name.ident); if (!is_internal_resid(child_key)) { if (entry.dynamic_ref_table->lookupResourceId(&child_key) != NO_ERROR) { - LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", child_key, resid); + LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", child_key, + resid); return nullptr; } } @@ -554,10 +667,17 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid) { // Use the child key if it comes before the parent // or is equal to the parent (overrides). new_entry->cookie = cookie; - new_entry->value.copyFrom_dtoh(map_entry->value); new_entry->key = child_key; new_entry->key_pool = nullptr; new_entry->type_pool = nullptr; + new_entry->value.copyFrom_dtoh(map_entry->value); + status_t err = entry.dynamic_ref_table->lookupResourceValue(&new_entry->value); + if (err != NO_ERROR) { + LOG(ERROR) << base::StringPrintf( + "Failed to resolve value t=0x%02x d=0x%08x for key 0x%08x.", new_entry->value.dataType, + new_entry->value.data, child_key); + return nullptr; + } ++map_entry; } else { // Take the parent entry as-is. @@ -577,15 +697,22 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid) { uint32_t new_key = dtohl(map_entry->name.ident); if (!is_internal_resid(new_key)) { if (entry.dynamic_ref_table->lookupResourceId(&new_key) != NO_ERROR) { - LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", new_key, resid); + LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", new_key, + resid); return nullptr; } } new_entry->cookie = cookie; - new_entry->value.copyFrom_dtoh(map_entry->value); new_entry->key = new_key; new_entry->key_pool = nullptr; new_entry->type_pool = nullptr; + new_entry->value.copyFrom_dtoh(map_entry->value); + status_t err = entry.dynamic_ref_table->lookupResourceValue(&new_entry->value); + if (err != NO_ERROR) { + LOG(ERROR) << base::StringPrintf("Failed to resolve value t=0x%02x d=0x%08x for key 0x%08x.", + new_entry->value.dataType, new_entry->value.data, new_key); + return nullptr; + } ++map_entry; ++new_entry; } @@ -601,15 +728,15 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid) { // Resize the resulting array to fit. const size_t actual_count = new_entry - new_bag->entries; if (actual_count != max_count) { - new_bag = reinterpret_cast<ResolvedBag*>( - realloc(new_bag, sizeof(ResolvedBag) + (actual_count * sizeof(ResolvedBag::Entry)))); + new_bag.reset(reinterpret_cast<ResolvedBag*>(realloc( + new_bag.release(), sizeof(ResolvedBag) + (actual_count * sizeof(ResolvedBag::Entry))))); } - util::unique_cptr<ResolvedBag> final_bag{new_bag}; - final_bag->type_spec_flags = flags; - final_bag->entry_count = static_cast<uint32_t>(actual_count); - ResolvedBag* result = final_bag.get(); - cached_bags_[resid] = std::move(final_bag); + // Combine flags from the parent and our own bag. + new_bag->type_spec_flags = entry.type_flags | parent_bag->type_spec_flags; + new_bag->entry_count = static_cast<uint32_t>(actual_count); + ResolvedBag* result = new_bag.get(); + cached_bags_[resid] = std::move(new_bag); return result; } @@ -627,7 +754,7 @@ static bool Utf8ToUtf16(const StringPiece& str, std::u16string* out) { uint32_t AssetManager2::GetResourceId(const std::string& resource_name, const std::string& fallback_type, - const std::string& fallback_package) { + const std::string& fallback_package) const { StringPiece package_name, type, entry; if (!ExtractResourceName(resource_name, &package_name, &type, &entry)) { return 0u; @@ -659,7 +786,8 @@ uint32_t AssetManager2::GetResourceId(const std::string& resource_name, const static std::u16string kAttrPrivate16 = u"^attr-private"; for (const PackageGroup& package_group : package_groups_) { - for (const LoadedPackage* package : package_group.packages_) { + for (const ConfiguredPackage& package_impl : package_group.packages_) { + const LoadedPackage* package = package_impl.loaded_package_; if (package_name != package->GetPackageName()) { // All packages in the same group are expected to have the same package name. break; @@ -681,6 +809,32 @@ uint32_t AssetManager2::GetResourceId(const std::string& resource_name, return 0u; } +void AssetManager2::RebuildFilterList() { + for (PackageGroup& group : package_groups_) { + for (ConfiguredPackage& impl : group.packages_) { + // Destroy it. + impl.filtered_configs_.~ByteBucketArray(); + + // Re-create it. + new (&impl.filtered_configs_) ByteBucketArray<FilteredConfigGroup>(); + + // Create the filters here. + impl.loaded_package_->ForEachTypeSpec([&](const TypeSpec* spec, uint8_t type_index) { + FilteredConfigGroup& group = impl.filtered_configs_.editItemAt(type_index); + const auto iter_end = spec->types + spec->type_count; + for (auto iter = spec->types; iter != iter_end; ++iter) { + ResTable_config this_config; + this_config.copyFromDtoH((*iter)->config); + if (this_config.match(configuration_)) { + group.configurations.push_back(this_config); + group.types.push_back(*iter); + } + } + }); + } + } +} + void AssetManager2::InvalidateCaches(uint32_t diff) { if (diff == 0xffffffffu) { // Everything must go. @@ -699,7 +853,37 @@ void AssetManager2::InvalidateCaches(uint32_t diff) { } } -std::unique_ptr<Theme> AssetManager2::NewTheme() { return std::unique_ptr<Theme>(new Theme(this)); } +std::unique_ptr<Theme> AssetManager2::NewTheme() { + return std::unique_ptr<Theme>(new Theme(this)); +} + +Theme::Theme(AssetManager2* asset_manager) : asset_manager_(asset_manager) { +} + +Theme::~Theme() = default; + +namespace { + +struct ThemeEntry { + ApkAssetsCookie cookie; + uint32_t type_spec_flags; + Res_value value; +}; + +struct ThemeType { + int entry_count; + ThemeEntry entries[0]; +}; + +constexpr size_t kTypeCount = std::numeric_limits<uint8_t>::max() + 1; + +} // namespace + +struct Theme::Package { + // Each element of Type will be a dynamically sized object + // allocated to have the entries stored contiguously with the Type. + std::array<util::unique_cptr<ThemeType>, kTypeCount> types; +}; bool Theme::ApplyStyle(uint32_t resid, bool force) { ATRACE_CALL(); @@ -712,71 +896,69 @@ bool Theme::ApplyStyle(uint32_t resid, bool force) { // Merge the flags from this style. type_spec_flags_ |= bag->type_spec_flags; - // On the first iteration, verify the attribute IDs and - // update the entry count in each type. - const auto bag_iter_end = end(bag); - for (auto bag_iter = begin(bag); bag_iter != bag_iter_end; ++bag_iter) { + int last_type_idx = -1; + int last_package_idx = -1; + Package* last_package = nullptr; + ThemeType* last_type = nullptr; + + // Iterate backwards, because each bag is sorted in ascending key ID order, meaning we will only + // need to perform one resize per type. + using reverse_bag_iterator = std::reverse_iterator<const ResolvedBag::Entry*>; + const auto bag_iter_end = reverse_bag_iterator(begin(bag)); + for (auto bag_iter = reverse_bag_iterator(end(bag)); bag_iter != bag_iter_end; ++bag_iter) { const uint32_t attr_resid = bag_iter->key; - // If the resource ID passed in is not a style, the key can be - // some other identifier that is not a resource ID. + // If the resource ID passed in is not a style, the key can be some other identifier that is not + // a resource ID. We should fail fast instead of operating with strange resource IDs. if (!is_valid_resid(attr_resid)) { return false; } - const uint32_t package_idx = get_package_id(attr_resid); - - // The type ID is 1-based, so subtract 1 to get an index. - const uint32_t type_idx = get_type_id(attr_resid) - 1; - const uint32_t entry_idx = get_entry_id(attr_resid); - - std::unique_ptr<Package>& package = packages_[package_idx]; - if (package == nullptr) { - package.reset(new Package()); - } - - util::unique_cptr<Type>& type = package->types[type_idx]; - if (type == nullptr) { - // Set the initial capacity to take up a total amount of 1024 bytes. - constexpr uint32_t kInitialCapacity = (1024u - sizeof(Type)) / sizeof(Entry); - const uint32_t initial_capacity = std::max(entry_idx, kInitialCapacity); - type.reset( - reinterpret_cast<Type*>(calloc(sizeof(Type) + (initial_capacity * sizeof(Entry)), 1))); - type->entry_capacity = initial_capacity; - } - - // Set the entry_count to include this entry. We will populate - // and resize the array as necessary in the next pass. - if (entry_idx + 1 > type->entry_count) { - // Increase the entry count to include this. - type->entry_count = entry_idx + 1; + // We don't use the 0-based index for the type so that we can avoid doing ID validation + // upon lookup. Instead, we keep space for the type ID 0 in our data structures. Since + // the construction of this type is guarded with a resource ID check, it will never be + // populated, and querying type ID 0 will always fail. + const int package_idx = get_package_id(attr_resid); + const int type_idx = get_type_id(attr_resid); + const int entry_idx = get_entry_id(attr_resid); + + if (last_package_idx != package_idx) { + std::unique_ptr<Package>& package = packages_[package_idx]; + if (package == nullptr) { + package.reset(new Package()); + } + last_package_idx = package_idx; + last_package = package.get(); + last_type_idx = -1; } - } - // On the second pass, we will realloc to fit the entry counts - // and populate the structures. - for (auto bag_iter = begin(bag); bag_iter != bag_iter_end; ++bag_iter) { - const uint32_t attr_resid = bag_iter->key; - const uint32_t package_idx = get_package_id(attr_resid); - const uint32_t type_idx = get_type_id(attr_resid) - 1; - const uint32_t entry_idx = get_entry_id(attr_resid); - Package* package = packages_[package_idx].get(); - util::unique_cptr<Type>& type = package->types[type_idx]; - if (type->entry_count != type->entry_capacity) { - // Resize to fit the actual entries that will be included. - Type* type_ptr = type.release(); - type.reset(reinterpret_cast<Type*>( - realloc(type_ptr, sizeof(Type) + (type_ptr->entry_count * sizeof(Entry))))); - if (type->entry_capacity < type->entry_count) { - // Clear the newly allocated memory (which does not get zero initialized). - // We need to do this because we |= type_spec_flags. - memset(type->entries + type->entry_capacity, 0, - sizeof(Entry) * (type->entry_count - type->entry_capacity)); + if (last_type_idx != type_idx) { + util::unique_cptr<ThemeType>& type = last_package->types[type_idx]; + if (type == nullptr) { + // Allocate enough memory to contain this entry_idx. Since we're iterating in reverse over + // a sorted list of attributes, this shouldn't be resized again during this method call. + type.reset(reinterpret_cast<ThemeType*>( + calloc(sizeof(ThemeType) + (entry_idx + 1) * sizeof(ThemeEntry), 1))); + type->entry_count = entry_idx + 1; + } else if (entry_idx >= type->entry_count) { + // Reallocate the memory to contain this entry_idx. Since we're iterating in reverse over + // a sorted list of attributes, this shouldn't be resized again during this method call. + const int new_count = entry_idx + 1; + type.reset(reinterpret_cast<ThemeType*>( + realloc(type.release(), sizeof(ThemeType) + (new_count * sizeof(ThemeEntry))))); + + // Clear out the newly allocated space (which isn't zeroed). + memset(type->entries + type->entry_count, 0, + (new_count - type->entry_count) * sizeof(ThemeEntry)); + type->entry_count = new_count; } - type->entry_capacity = type->entry_count; + last_type_idx = type_idx; + last_type = type.get(); } - Entry& entry = type->entries[entry_idx]; - if (force || entry.value.dataType == Res_value::TYPE_NULL) { + + ThemeEntry& entry = last_type->entries[entry_idx]; + if (force || (entry.value.dataType == Res_value::TYPE_NULL && + entry.value.data != Res_value::DATA_NULL_EMPTY)) { entry.cookie = bag_iter->cookie; entry.type_spec_flags |= bag->type_spec_flags; entry.value = bag_iter->value; @@ -787,95 +969,53 @@ bool Theme::ApplyStyle(uint32_t resid, bool force) { ApkAssetsCookie Theme::GetAttribute(uint32_t resid, Res_value* out_value, uint32_t* out_flags) const { - constexpr const int kMaxIterations = 20; + int cnt = 20; uint32_t type_spec_flags = 0u; - for (int iterations_left = kMaxIterations; iterations_left > 0; iterations_left--) { - if (!is_valid_resid(resid)) { - return kInvalidCookie; - } - - const uint32_t package_idx = get_package_id(resid); - - // Type ID is 1-based, subtract 1 to get the index. - const uint32_t type_idx = get_type_id(resid) - 1; - const uint32_t entry_idx = get_entry_id(resid); - + do { + const int package_idx = get_package_id(resid); const Package* package = packages_[package_idx].get(); - if (package == nullptr) { - return kInvalidCookie; - } - - const Type* type = package->types[type_idx].get(); - if (type == nullptr) { - return kInvalidCookie; - } - - if (entry_idx >= type->entry_count) { - return kInvalidCookie; - } - - const Entry& entry = type->entries[entry_idx]; - type_spec_flags |= entry.type_spec_flags; - - switch (entry.value.dataType) { - case Res_value::TYPE_NULL: - return kInvalidCookie; - - case Res_value::TYPE_ATTRIBUTE: - resid = entry.value.data; - break; - - case Res_value::TYPE_DYNAMIC_ATTRIBUTE: { - // Resolve the dynamic attribute to a normal attribute - // (with the right package ID). - resid = entry.value.data; - const DynamicRefTable* ref_table = - asset_manager_->GetDynamicRefTableForPackage(package_idx); - if (ref_table == nullptr || ref_table->lookupResourceId(&resid) != NO_ERROR) { - LOG(ERROR) << base::StringPrintf("Failed to resolve dynamic attribute 0x%08x", resid); - return kInvalidCookie; - } - } break; - - case Res_value::TYPE_DYNAMIC_REFERENCE: { - // Resolve the dynamic reference to a normal reference - // (with the right package ID). - out_value->dataType = Res_value::TYPE_REFERENCE; - out_value->data = entry.value.data; - const DynamicRefTable* ref_table = - asset_manager_->GetDynamicRefTableForPackage(package_idx); - if (ref_table == nullptr || ref_table->lookupResourceId(&out_value->data) != NO_ERROR) { - LOG(ERROR) << base::StringPrintf("Failed to resolve dynamic reference 0x%08x", - out_value->data); - return kInvalidCookie; - } - - if (out_flags != nullptr) { + if (package != nullptr) { + // The themes are constructed with a 1-based type ID, so no need to decrement here. + const int type_idx = get_type_id(resid); + const ThemeType* type = package->types[type_idx].get(); + if (type != nullptr) { + const int entry_idx = get_entry_id(resid); + if (entry_idx < type->entry_count) { + const ThemeEntry& entry = type->entries[entry_idx]; + type_spec_flags |= entry.type_spec_flags; + + if (entry.value.dataType == Res_value::TYPE_ATTRIBUTE) { + if (cnt > 0) { + cnt--; + resid = entry.value.data; + continue; + } + return kInvalidCookie; + } + + // @null is different than @empty. + if (entry.value.dataType == Res_value::TYPE_NULL && + entry.value.data != Res_value::DATA_NULL_EMPTY) { + return kInvalidCookie; + } + + *out_value = entry.value; *out_flags = type_spec_flags; + return entry.cookie; } - return entry.cookie; } - - default: - *out_value = entry.value; - if (out_flags != nullptr) { - *out_flags = type_spec_flags; - } - return entry.cookie; } - } - - LOG(WARNING) << base::StringPrintf("Too many (%d) attribute references, stopped at: 0x%08x", - kMaxIterations, resid); + break; + } while (true); return kInvalidCookie; } ApkAssetsCookie Theme::ResolveAttributeReference(ApkAssetsCookie cookie, Res_value* in_out_value, ResTable_config* in_out_selected_config, uint32_t* in_out_type_spec_flags, - uint32_t* out_last_ref) { + uint32_t* out_last_ref) const { if (in_out_value->dataType == Res_value::TYPE_ATTRIBUTE) { uint32_t new_flags; cookie = GetAttribute(in_out_value->data, in_out_value, &new_flags); @@ -903,30 +1043,36 @@ bool Theme::SetTo(const Theme& o) { return true; } - if (asset_manager_ != o.asset_manager_) { - return false; - } - type_spec_flags_ = o.type_spec_flags_; + const bool copy_only_system = asset_manager_ != o.asset_manager_; + for (size_t p = 0; p < packages_.size(); p++) { const Package* package = o.packages_[p].get(); - if (package == nullptr) { + if (package == nullptr || (copy_only_system && p != 0x01)) { + // The other theme doesn't have this package, clear ours. packages_[p].reset(); continue; } + if (packages_[p] == nullptr) { + // The other theme has this package, but we don't. Make one. + packages_[p].reset(new Package()); + } + for (size_t t = 0; t < package->types.size(); t++) { - const Type* type = package->types[t].get(); + const ThemeType* type = package->types[t].get(); if (type == nullptr) { + // The other theme doesn't have this type, clear ours. packages_[p]->types[t].reset(); continue; } - const size_t type_alloc_size = sizeof(Type) + (type->entry_capacity * sizeof(Entry)); + // Create a new type and update it to theirs. + const size_t type_alloc_size = sizeof(ThemeType) + (type->entry_count * sizeof(ThemeEntry)); void* copied_data = malloc(type_alloc_size); memcpy(copied_data, type, type_alloc_size); - packages_[p]->types[t].reset(reinterpret_cast<Type*>(copied_data)); + packages_[p]->types[t].reset(reinterpret_cast<ThemeType*>(copied_data)); } } return true; diff --git a/libs/androidfw/AttributeResolution.cpp b/libs/androidfw/AttributeResolution.cpp index 60e3845d98a9..f912af4f7190 100644 --- a/libs/androidfw/AttributeResolution.cpp +++ b/libs/androidfw/AttributeResolution.cpp @@ -20,13 +20,18 @@ #include <log/log.h> +#include "androidfw/AssetManager2.h" #include "androidfw/AttributeFinder.h" -#include "androidfw/ResourceTypes.h" constexpr bool kDebugStyles = false; namespace android { +// Java asset cookies have 0 as an invalid cookie, but TypedArray expects < 0. +static uint32_t ApkAssetsCookieToJavaCookie(ApkAssetsCookie cookie) { + return cookie != kInvalidCookie ? static_cast<uint32_t>(cookie + 1) : static_cast<uint32_t>(-1); +} + class XmlAttributeFinder : public BackTrackingAttributeFinder<XmlAttributeFinder, size_t> { public: @@ -44,58 +49,53 @@ class XmlAttributeFinder }; class BagAttributeFinder - : public BackTrackingAttributeFinder<BagAttributeFinder, const ResTable::bag_entry*> { + : public BackTrackingAttributeFinder<BagAttributeFinder, const ResolvedBag::Entry*> { public: - BagAttributeFinder(const ResTable::bag_entry* start, - const ResTable::bag_entry* end) - : BackTrackingAttributeFinder(start, end) {} + BagAttributeFinder(const ResolvedBag* bag) + : BackTrackingAttributeFinder(bag != nullptr ? bag->entries : nullptr, + bag != nullptr ? bag->entries + bag->entry_count : nullptr) { + } - inline uint32_t GetAttribute(const ResTable::bag_entry* entry) const { - return entry->map.name.ident; + inline uint32_t GetAttribute(const ResolvedBag::Entry* entry) const { + return entry->key; } }; -bool ResolveAttrs(ResTable::Theme* theme, uint32_t def_style_attr, - uint32_t def_style_res, uint32_t* src_values, - size_t src_values_length, uint32_t* attrs, - size_t attrs_length, uint32_t* out_values, - uint32_t* out_indices) { +bool ResolveAttrs(Theme* theme, uint32_t def_style_attr, uint32_t def_style_res, + uint32_t* src_values, size_t src_values_length, uint32_t* attrs, + size_t attrs_length, uint32_t* out_values, uint32_t* out_indices) { if (kDebugStyles) { ALOGI("APPLY STYLE: theme=0x%p defStyleAttr=0x%x defStyleRes=0x%x", theme, def_style_attr, def_style_res); } - const ResTable& res = theme->getResTable(); + AssetManager2* assetmanager = theme->GetAssetManager(); ResTable_config config; Res_value value; int indices_idx = 0; // Load default style from attribute, if specified... - uint32_t def_style_bag_type_set_flags = 0; + uint32_t def_style_flags = 0u; if (def_style_attr != 0) { Res_value value; - if (theme->getAttribute(def_style_attr, &value, &def_style_bag_type_set_flags) >= 0) { + if (theme->GetAttribute(def_style_attr, &value, &def_style_flags) != kInvalidCookie) { if (value.dataType == Res_value::TYPE_REFERENCE) { def_style_res = value.data; } } } - // Now lock down the resource object and start pulling stuff from it. - res.lock(); - // Retrieve the default style bag, if requested. - const ResTable::bag_entry* def_style_start = nullptr; - uint32_t def_style_type_set_flags = 0; - ssize_t bag_off = def_style_res != 0 - ? res.getBagLocked(def_style_res, &def_style_start, - &def_style_type_set_flags) - : -1; - def_style_type_set_flags |= def_style_bag_type_set_flags; - const ResTable::bag_entry* const def_style_end = - def_style_start + (bag_off >= 0 ? bag_off : 0); - BagAttributeFinder def_style_attr_finder(def_style_start, def_style_end); + const ResolvedBag* default_style_bag = nullptr; + if (def_style_res != 0) { + default_style_bag = assetmanager->GetBag(def_style_res); + if (default_style_bag != nullptr) { + def_style_flags |= default_style_bag->type_spec_flags; + } + } + + BagAttributeFinder def_style_attr_finder(default_style_bag); // Now iterate through all of the attributes that the client has requested, // filling in each with whatever data we can find. @@ -106,7 +106,7 @@ bool ResolveAttrs(ResTable::Theme* theme, uint32_t def_style_attr, ALOGI("RETRIEVING ATTR 0x%08x...", cur_ident); } - ssize_t block = -1; + ApkAssetsCookie cookie = kInvalidCookie; uint32_t type_set_flags = 0; value.dataType = Res_value::TYPE_NULL; @@ -122,15 +122,14 @@ bool ResolveAttrs(ResTable::Theme* theme, uint32_t def_style_attr, value.dataType = Res_value::TYPE_ATTRIBUTE; value.data = src_values[ii]; if (kDebugStyles) { - ALOGI("-> From values: type=0x%x, data=0x%08x", value.dataType, - value.data); + ALOGI("-> From values: type=0x%x, data=0x%08x", value.dataType, value.data); } } else { - const ResTable::bag_entry* const def_style_entry = def_style_attr_finder.Find(cur_ident); - if (def_style_entry != def_style_end) { - block = def_style_entry->stringBlock; - type_set_flags = def_style_type_set_flags; - value = def_style_entry->map.value; + const ResolvedBag::Entry* const entry = def_style_attr_finder.Find(cur_ident); + if (entry != def_style_attr_finder.end()) { + cookie = entry->cookie; + type_set_flags = def_style_flags; + value = entry->value; if (kDebugStyles) { ALOGI("-> From def style: type=0x%x, data=0x%08x", value.dataType, value.data); } @@ -140,22 +139,26 @@ bool ResolveAttrs(ResTable::Theme* theme, uint32_t def_style_attr, uint32_t resid = 0; if (value.dataType != Res_value::TYPE_NULL) { // Take care of resolving the found resource to its final value. - ssize_t new_block = - theme->resolveAttributeReference(&value, block, &resid, &type_set_flags, &config); - if (new_block >= 0) block = new_block; + ApkAssetsCookie new_cookie = + theme->ResolveAttributeReference(cookie, &value, &config, &type_set_flags, &resid); + if (new_cookie != kInvalidCookie) { + cookie = new_cookie; + } if (kDebugStyles) { ALOGI("-> Resolved attr: type=0x%x, data=0x%08x", value.dataType, value.data); } } else if (value.data != Res_value::DATA_NULL_EMPTY) { - // If we still don't have a value for this attribute, try to find - // it in the theme! - ssize_t new_block = theme->getAttribute(cur_ident, &value, &type_set_flags); - if (new_block >= 0) { + // If we still don't have a value for this attribute, try to find it in the theme! + ApkAssetsCookie new_cookie = theme->GetAttribute(cur_ident, &value, &type_set_flags); + if (new_cookie != kInvalidCookie) { if (kDebugStyles) { ALOGI("-> From theme: type=0x%x, data=0x%08x", value.dataType, value.data); } - new_block = res.resolveReference(&value, new_block, &resid, &type_set_flags, &config); - if (new_block >= 0) block = new_block; + new_cookie = + assetmanager->ResolveReference(new_cookie, &value, &config, &type_set_flags, &resid); + if (new_cookie != kInvalidCookie) { + cookie = new_cookie; + } if (kDebugStyles) { ALOGI("-> Resolved theme: type=0x%x, data=0x%08x", value.dataType, value.data); } @@ -169,7 +172,7 @@ bool ResolveAttrs(ResTable::Theme* theme, uint32_t def_style_attr, } value.dataType = Res_value::TYPE_NULL; value.data = Res_value::DATA_NULL_UNDEFINED; - block = -1; + cookie = kInvalidCookie; } if (kDebugStyles) { @@ -179,9 +182,7 @@ bool ResolveAttrs(ResTable::Theme* theme, uint32_t def_style_attr, // Write the final value back to Java. out_values[STYLE_TYPE] = value.dataType; out_values[STYLE_DATA] = value.data; - out_values[STYLE_ASSET_COOKIE] = - block != -1 ? static_cast<uint32_t>(res.getTableCookie(block)) - : static_cast<uint32_t>(-1); + out_values[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie); out_values[STYLE_RESOURCE_ID] = resid; out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags; out_values[STYLE_DENSITY] = config.density; @@ -195,90 +196,80 @@ bool ResolveAttrs(ResTable::Theme* theme, uint32_t def_style_attr, out_values += STYLE_NUM_ENTRIES; } - res.unlock(); - if (out_indices != nullptr) { out_indices[0] = indices_idx; } return true; } -void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, - uint32_t def_style_res, const uint32_t* attrs, size_t attrs_length, +void ApplyStyle(Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, + uint32_t def_style_resid, const uint32_t* attrs, size_t attrs_length, uint32_t* out_values, uint32_t* out_indices) { if (kDebugStyles) { - ALOGI("APPLY STYLE: theme=0x%p defStyleAttr=0x%x defStyleRes=0x%x xml=0x%p", - theme, def_style_attr, def_style_res, xml_parser); + ALOGI("APPLY STYLE: theme=0x%p defStyleAttr=0x%x defStyleRes=0x%x xml=0x%p", theme, + def_style_attr, def_style_resid, xml_parser); } - const ResTable& res = theme->getResTable(); + AssetManager2* assetmanager = theme->GetAssetManager(); ResTable_config config; Res_value value; int indices_idx = 0; // Load default style from attribute, if specified... - uint32_t def_style_bag_type_set_flags = 0; + uint32_t def_style_flags = 0u; if (def_style_attr != 0) { Res_value value; - if (theme->getAttribute(def_style_attr, &value, - &def_style_bag_type_set_flags) >= 0) { + if (theme->GetAttribute(def_style_attr, &value, &def_style_flags) != kInvalidCookie) { if (value.dataType == Res_value::TYPE_REFERENCE) { - def_style_res = value.data; + def_style_resid = value.data; } } } - // Retrieve the style class associated with the current XML tag. - int style = 0; - uint32_t style_bag_type_set_flags = 0; + // Retrieve the style resource ID associated with the current XML tag's style attribute. + uint32_t style_resid = 0u; + uint32_t style_flags = 0u; if (xml_parser != nullptr) { ssize_t idx = xml_parser->indexOfStyle(); if (idx >= 0 && xml_parser->getAttributeValue(idx, &value) >= 0) { if (value.dataType == value.TYPE_ATTRIBUTE) { - if (theme->getAttribute(value.data, &value, &style_bag_type_set_flags) < 0) { + // Resolve the attribute with out theme. + if (theme->GetAttribute(value.data, &value, &style_flags) == kInvalidCookie) { value.dataType = Res_value::TYPE_NULL; } } + if (value.dataType == value.TYPE_REFERENCE) { - style = value.data; + style_resid = value.data; } } } - // Now lock down the resource object and start pulling stuff from it. - res.lock(); - // Retrieve the default style bag, if requested. - const ResTable::bag_entry* def_style_attr_start = nullptr; - uint32_t def_style_type_set_flags = 0; - ssize_t bag_off = def_style_res != 0 - ? res.getBagLocked(def_style_res, &def_style_attr_start, - &def_style_type_set_flags) - : -1; - def_style_type_set_flags |= def_style_bag_type_set_flags; - const ResTable::bag_entry* const def_style_attr_end = - def_style_attr_start + (bag_off >= 0 ? bag_off : 0); - BagAttributeFinder def_style_attr_finder(def_style_attr_start, - def_style_attr_end); + const ResolvedBag* default_style_bag = nullptr; + if (def_style_resid != 0) { + default_style_bag = assetmanager->GetBag(def_style_resid); + if (default_style_bag != nullptr) { + def_style_flags |= default_style_bag->type_spec_flags; + } + } + + BagAttributeFinder def_style_attr_finder(default_style_bag); // Retrieve the style class bag, if requested. - const ResTable::bag_entry* style_attr_start = nullptr; - uint32_t style_type_set_flags = 0; - bag_off = - style != 0 - ? res.getBagLocked(style, &style_attr_start, &style_type_set_flags) - : -1; - style_type_set_flags |= style_bag_type_set_flags; - const ResTable::bag_entry* const style_attr_end = - style_attr_start + (bag_off >= 0 ? bag_off : 0); - BagAttributeFinder style_attr_finder(style_attr_start, style_attr_end); + const ResolvedBag* xml_style_bag = nullptr; + if (style_resid != 0) { + xml_style_bag = assetmanager->GetBag(style_resid); + if (xml_style_bag != nullptr) { + style_flags |= xml_style_bag->type_spec_flags; + } + } + + BagAttributeFinder xml_style_attr_finder(xml_style_bag); // Retrieve the XML attributes, if requested. - static const ssize_t kXmlBlock = 0x10000000; XmlAttributeFinder xml_attr_finder(xml_parser); - const size_t xml_attr_end = - xml_parser != nullptr ? xml_parser->getAttributeCount() : 0; // Now iterate through all of the attributes that the client has requested, // filling in each with whatever data we can find. @@ -289,8 +280,8 @@ void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_s ALOGI("RETRIEVING ATTR 0x%08x...", cur_ident); } - ssize_t block = kXmlBlock; - uint32_t type_set_flags = 0; + ApkAssetsCookie cookie = kInvalidCookie; + uint32_t type_set_flags = 0u; value.dataType = Res_value::TYPE_NULL; value.data = Res_value::DATA_NULL_UNDEFINED; @@ -302,7 +293,7 @@ void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_s // Walk through the xml attributes looking for the requested attribute. const size_t xml_attr_idx = xml_attr_finder.Find(cur_ident); - if (xml_attr_idx != xml_attr_end) { + if (xml_attr_idx != xml_attr_finder.end()) { // We found the attribute we were looking for. xml_parser->getAttributeValue(xml_attr_idx, &value); if (kDebugStyles) { @@ -312,12 +303,12 @@ void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_s if (value.dataType == Res_value::TYPE_NULL && value.data != Res_value::DATA_NULL_EMPTY) { // Walk through the style class values looking for the requested attribute. - const ResTable::bag_entry* const style_attr_entry = style_attr_finder.Find(cur_ident); - if (style_attr_entry != style_attr_end) { + const ResolvedBag::Entry* entry = xml_style_attr_finder.Find(cur_ident); + if (entry != xml_style_attr_finder.end()) { // We found the attribute we were looking for. - block = style_attr_entry->stringBlock; - type_set_flags = style_type_set_flags; - value = style_attr_entry->map.value; + cookie = entry->cookie; + type_set_flags = style_flags; + value = entry->value; if (kDebugStyles) { ALOGI("-> From style: type=0x%x, data=0x%08x", value.dataType, value.data); } @@ -326,25 +317,25 @@ void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_s if (value.dataType == Res_value::TYPE_NULL && value.data != Res_value::DATA_NULL_EMPTY) { // Walk through the default style values looking for the requested attribute. - const ResTable::bag_entry* const def_style_attr_entry = def_style_attr_finder.Find(cur_ident); - if (def_style_attr_entry != def_style_attr_end) { + const ResolvedBag::Entry* entry = def_style_attr_finder.Find(cur_ident); + if (entry != def_style_attr_finder.end()) { // We found the attribute we were looking for. - block = def_style_attr_entry->stringBlock; - type_set_flags = style_type_set_flags; - value = def_style_attr_entry->map.value; + cookie = entry->cookie; + type_set_flags = def_style_flags; + value = entry->value; if (kDebugStyles) { ALOGI("-> From def style: type=0x%x, data=0x%08x", value.dataType, value.data); } } } - uint32_t resid = 0; + uint32_t resid = 0u; if (value.dataType != Res_value::TYPE_NULL) { // Take care of resolving the found resource to its final value. - ssize_t new_block = - theme->resolveAttributeReference(&value, block, &resid, &type_set_flags, &config); - if (new_block >= 0) { - block = new_block; + ApkAssetsCookie new_cookie = + theme->ResolveAttributeReference(cookie, &value, &config, &type_set_flags, &resid); + if (new_cookie != kInvalidCookie) { + cookie = new_cookie; } if (kDebugStyles) { @@ -352,14 +343,15 @@ void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_s } } else if (value.data != Res_value::DATA_NULL_EMPTY) { // If we still don't have a value for this attribute, try to find it in the theme! - ssize_t new_block = theme->getAttribute(cur_ident, &value, &type_set_flags); - if (new_block >= 0) { + ApkAssetsCookie new_cookie = theme->GetAttribute(cur_ident, &value, &type_set_flags); + if (new_cookie != kInvalidCookie) { if (kDebugStyles) { ALOGI("-> From theme: type=0x%x, data=0x%08x", value.dataType, value.data); } - new_block = res.resolveReference(&value, new_block, &resid, &type_set_flags, &config); - if (new_block >= 0) { - block = new_block; + new_cookie = + assetmanager->ResolveReference(new_cookie, &value, &config, &type_set_flags, &resid); + if (new_cookie != kInvalidCookie) { + cookie = new_cookie; } if (kDebugStyles) { @@ -375,7 +367,7 @@ void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_s } value.dataType = Res_value::TYPE_NULL; value.data = Res_value::DATA_NULL_UNDEFINED; - block = kXmlBlock; + cookie = kInvalidCookie; } if (kDebugStyles) { @@ -385,9 +377,7 @@ void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_s // Write the final value back to Java. out_values[STYLE_TYPE] = value.dataType; out_values[STYLE_DATA] = value.data; - out_values[STYLE_ASSET_COOKIE] = - block != kXmlBlock ? static_cast<uint32_t>(res.getTableCookie(block)) - : static_cast<uint32_t>(-1); + out_values[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie); out_values[STYLE_RESOURCE_ID] = resid; out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags; out_values[STYLE_DENSITY] = config.density; @@ -402,36 +392,28 @@ void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_s out_values += STYLE_NUM_ENTRIES; } - res.unlock(); - // out_indices must NOT be nullptr. out_indices[0] = indices_idx; } -bool RetrieveAttributes(const ResTable* res, ResXMLParser* xml_parser, - uint32_t* attrs, size_t attrs_length, - uint32_t* out_values, uint32_t* out_indices) { +bool RetrieveAttributes(AssetManager2* assetmanager, ResXMLParser* xml_parser, uint32_t* attrs, + size_t attrs_length, uint32_t* out_values, uint32_t* out_indices) { ResTable_config config; Res_value value; int indices_idx = 0; - // Now lock down the resource object and start pulling stuff from it. - res->lock(); - // Retrieve the XML attributes, if requested. const size_t xml_attr_count = xml_parser->getAttributeCount(); size_t ix = 0; uint32_t cur_xml_attr = xml_parser->getAttributeNameResID(ix); - static const ssize_t kXmlBlock = 0x10000000; - // Now iterate through all of the attributes that the client has requested, // filling in each with whatever data we can find. for (size_t ii = 0; ii < attrs_length; ii++) { const uint32_t cur_ident = attrs[ii]; - ssize_t block = kXmlBlock; - uint32_t type_set_flags = 0; + ApkAssetsCookie cookie = kInvalidCookie; + uint32_t type_set_flags = 0u; value.dataType = Res_value::TYPE_NULL; value.data = Res_value::DATA_NULL_UNDEFINED; @@ -450,28 +432,27 @@ bool RetrieveAttributes(const ResTable* res, ResXMLParser* xml_parser, cur_xml_attr = xml_parser->getAttributeNameResID(ix); } - uint32_t resid = 0; + uint32_t resid = 0u; if (value.dataType != Res_value::TYPE_NULL) { // Take care of resolving the found resource to its final value. - // printf("Resolving attribute reference\n"); - ssize_t new_block = res->resolveReference(&value, block, &resid, - &type_set_flags, &config); - if (new_block >= 0) block = new_block; + ApkAssetsCookie new_cookie = + assetmanager->ResolveReference(cookie, &value, &config, &type_set_flags, &resid); + if (new_cookie != kInvalidCookie) { + cookie = new_cookie; + } } // Deal with the special @null value -- it turns back to TYPE_NULL. if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) { value.dataType = Res_value::TYPE_NULL; value.data = Res_value::DATA_NULL_UNDEFINED; - block = kXmlBlock; + cookie = kInvalidCookie; } // Write the final value back to Java. out_values[STYLE_TYPE] = value.dataType; out_values[STYLE_DATA] = value.data; - out_values[STYLE_ASSET_COOKIE] = - block != kXmlBlock ? static_cast<uint32_t>(res->getTableCookie(block)) - : static_cast<uint32_t>(-1); + out_values[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie); out_values[STYLE_RESOURCE_ID] = resid; out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags; out_values[STYLE_DENSITY] = config.density; @@ -485,8 +466,6 @@ bool RetrieveAttributes(const ResTable* res, ResXMLParser* xml_parser, out_values += STYLE_NUM_ENTRIES; } - res->unlock(); - if (out_indices != nullptr) { out_indices[0] = indices_idx; } diff --git a/libs/androidfw/Idmap.cpp b/libs/androidfw/Idmap.cpp new file mode 100644 index 000000000000..7c1ee5cd7cfa --- /dev/null +++ b/libs/androidfw/Idmap.cpp @@ -0,0 +1,190 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define ATRACE_TAG ATRACE_TAG_RESOURCES + +#include "androidfw/Idmap.h" + +#include "android-base/logging.h" +#include "android-base/stringprintf.h" +#include "utils/ByteOrder.h" +#include "utils/Trace.h" + +#ifdef _WIN32 +#ifdef ERROR +#undef ERROR +#endif +#endif + +#include "androidfw/ResourceTypes.h" + +using ::android::base::StringPrintf; + +namespace android { + +constexpr static inline bool is_valid_package_id(uint16_t id) { + return id != 0 && id <= 255; +} + +constexpr static inline bool is_valid_type_id(uint16_t id) { + // Type IDs and package IDs have the same constraints in the IDMAP. + return is_valid_package_id(id); +} + +bool LoadedIdmap::Lookup(const IdmapEntry_header* header, uint16_t input_entry_id, + uint16_t* output_entry_id) { + if (input_entry_id < dtohs(header->entry_id_offset)) { + // After applying the offset, the entry is not present. + return false; + } + + input_entry_id -= dtohs(header->entry_id_offset); + if (input_entry_id >= dtohs(header->entry_count)) { + // The entry is not present. + return false; + } + + uint32_t result = dtohl(header->entries[input_entry_id]); + if (result == 0xffffffffu) { + return false; + } + *output_entry_id = static_cast<uint16_t>(result); + return true; +} + +static bool is_word_aligned(const void* data) { + return (reinterpret_cast<uintptr_t>(data) & 0x03) == 0; +} + +static bool IsValidIdmapHeader(const StringPiece& data) { + if (!is_word_aligned(data.data())) { + LOG(ERROR) << "Idmap header is not word aligned."; + return false; + } + + if (data.size() < sizeof(Idmap_header)) { + LOG(ERROR) << "Idmap header is too small."; + return false; + } + + const Idmap_header* header = reinterpret_cast<const Idmap_header*>(data.data()); + if (dtohl(header->magic) != kIdmapMagic) { + LOG(ERROR) << StringPrintf("Invalid Idmap file: bad magic value (was 0x%08x, expected 0x%08x)", + dtohl(header->magic), kIdmapMagic); + return false; + } + + if (dtohl(header->version) != kIdmapCurrentVersion) { + // We are strict about versions because files with this format are auto-generated and don't need + // backwards compatibility. + LOG(ERROR) << StringPrintf("Version mismatch in Idmap (was 0x%08x, expected 0x%08x)", + dtohl(header->version), kIdmapCurrentVersion); + return false; + } + + if (!is_valid_package_id(dtohs(header->target_package_id))) { + LOG(ERROR) << StringPrintf("Target package ID in Idmap is invalid: 0x%02x", + dtohs(header->target_package_id)); + return false; + } + + if (dtohs(header->type_count) > 255) { + LOG(ERROR) << StringPrintf("Idmap has too many type mappings (was %d, max 255)", + (int)dtohs(header->type_count)); + return false; + } + return true; +} + +LoadedIdmap::LoadedIdmap(const Idmap_header* header) : header_(header) { + size_t length = strnlen(reinterpret_cast<const char*>(header_->overlay_path), + arraysize(header_->overlay_path)); + overlay_apk_path_.assign(reinterpret_cast<const char*>(header_->overlay_path), length); +} + +std::unique_ptr<const LoadedIdmap> LoadedIdmap::Load(const StringPiece& idmap_data) { + ATRACE_CALL(); + if (!IsValidIdmapHeader(idmap_data)) { + return {}; + } + + const Idmap_header* header = reinterpret_cast<const Idmap_header*>(idmap_data.data()); + + // Can't use make_unique because LoadedImpl constructor is private. + std::unique_ptr<LoadedIdmap> loaded_idmap = std::unique_ptr<LoadedIdmap>(new LoadedIdmap(header)); + + const uint8_t* data_ptr = reinterpret_cast<const uint8_t*>(idmap_data.data()) + sizeof(*header); + size_t data_size = idmap_data.size() - sizeof(*header); + + size_t type_maps_encountered = 0u; + while (data_size >= sizeof(IdmapEntry_header)) { + if (!is_word_aligned(data_ptr)) { + LOG(ERROR) << "Type mapping in Idmap is not word aligned"; + return {}; + } + + // Validate the type IDs. + const IdmapEntry_header* entry_header = reinterpret_cast<const IdmapEntry_header*>(data_ptr); + if (!is_valid_type_id(dtohs(entry_header->target_type_id)) || !is_valid_type_id(dtohs(entry_header->overlay_type_id))) { + LOG(ERROR) << StringPrintf("Invalid type map (0x%02x -> 0x%02x)", + dtohs(entry_header->target_type_id), + dtohs(entry_header->overlay_type_id)); + return {}; + } + + // Make sure there is enough space for the entries declared in the header. + if ((data_size - sizeof(*entry_header)) / sizeof(uint32_t) < + static_cast<size_t>(dtohs(entry_header->entry_count))) { + LOG(ERROR) << StringPrintf("Idmap too small for the number of entries (%d)", + (int)dtohs(entry_header->entry_count)); + return {}; + } + + // Only add a non-empty overlay. + if (dtohs(entry_header->entry_count != 0)) { + loaded_idmap->type_map_[static_cast<uint8_t>(dtohs(entry_header->overlay_type_id))] = + entry_header; + } + + const size_t entry_size_bytes = + sizeof(*entry_header) + (dtohs(entry_header->entry_count) * sizeof(uint32_t)); + data_ptr += entry_size_bytes; + data_size -= entry_size_bytes; + type_maps_encountered++; + } + + // Verify that we parsed all the type maps. + if (type_maps_encountered != static_cast<size_t>(dtohs(header->type_count))) { + LOG(ERROR) << "Parsed " << type_maps_encountered << " type maps but expected " + << (int)dtohs(header->type_count); + return {}; + } + return std::move(loaded_idmap); +} + +uint8_t LoadedIdmap::TargetPackageId() const { + return static_cast<uint8_t>(dtohs(header_->target_package_id)); +} + +const IdmapEntry_header* LoadedIdmap::GetEntryMapForType(uint8_t type_id) const { + auto iter = type_map_.find(type_id); + if (iter != type_map_.end()) { + return iter->second; + } + return nullptr; +} + +} // namespace android diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp index bd7b80469ddc..1d2c597c4c8c 100644 --- a/libs/androidfw/LoadedArsc.cpp +++ b/libs/androidfw/LoadedArsc.cpp @@ -18,6 +18,7 @@ #include "androidfw/LoadedArsc.h" +#include <algorithm> #include <cstddef> #include <limits> @@ -37,46 +38,12 @@ #include "androidfw/ResourceUtils.h" #include "androidfw/Util.h" -using android::base::StringPrintf; +using ::android::base::StringPrintf; namespace android { constexpr const static int kAppPackageId = 0x7f; -// Element of a TypeSpec array. See TypeSpec. -struct Type { - // The configuration for which this type defines entries. - // This is already converted to host endianness. - ResTable_config configuration; - - // Pointer to the mmapped data where entry definitions are kept. - const ResTable_type* type; -}; - -// TypeSpec is going to be immediately proceeded by -// an array of Type structs, all in the same block of memory. -struct TypeSpec { - // Pointer to the mmapped data where flags are kept. - // Flags denote whether the resource entry is public - // and under which configurations it varies. - const ResTable_typeSpec* type_spec; - - // The number of types that follow this struct. - // There is a type for each configuration - // that entries are defined for. - size_t type_count; - - // Trick to easily access a variable number of Type structs - // proceeding this struct, and to ensure their alignment. - const Type types[0]; -}; - -// TypeSpecPtr points to the block of memory that holds -// a TypeSpec struct, followed by an array of Type structs. -// TypeSpecPtr is a managed pointer that knows how to delete -// itself. -using TypeSpecPtr = util::unique_cptr<TypeSpec>; - namespace { // Builder that helps accumulate Type structs and then create a single @@ -84,23 +51,28 @@ namespace { // the Type structs. class TypeSpecPtrBuilder { public: - TypeSpecPtrBuilder(const ResTable_typeSpec* header) : header_(header) {} + explicit TypeSpecPtrBuilder(const ResTable_typeSpec* header, + const IdmapEntry_header* idmap_header) + : header_(header), idmap_header_(idmap_header) { + } void AddType(const ResTable_type* type) { - ResTable_config config; - config.copyFromDtoH(type->config); - types_.push_back(Type{config, type}); + types_.push_back(type); } TypeSpecPtr Build() { // Check for overflow. - if ((std::numeric_limits<size_t>::max() - sizeof(TypeSpec)) / sizeof(Type) < types_.size()) { + using ElementType = const ResTable_type*; + if ((std::numeric_limits<size_t>::max() - sizeof(TypeSpec)) / sizeof(ElementType) < + types_.size()) { return {}; } - TypeSpec* type_spec = (TypeSpec*)::malloc(sizeof(TypeSpec) + (types_.size() * sizeof(Type))); + TypeSpec* type_spec = + (TypeSpec*)::malloc(sizeof(TypeSpec) + (types_.size() * sizeof(ElementType))); type_spec->type_spec = header_; + type_spec->idmap_entries = idmap_header_; type_spec->type_count = types_.size(); - memcpy(type_spec + 1, types_.data(), types_.size() * sizeof(Type)); + memcpy(type_spec + 1, types_.data(), types_.size() * sizeof(ElementType)); return TypeSpecPtr(type_spec); } @@ -108,218 +80,186 @@ class TypeSpecPtrBuilder { DISALLOW_COPY_AND_ASSIGN(TypeSpecPtrBuilder); const ResTable_typeSpec* header_; - std::vector<Type> types_; + const IdmapEntry_header* idmap_header_; + std::vector<const ResTable_type*> types_; }; } // namespace -bool LoadedPackage::FindEntry(uint8_t type_idx, uint16_t entry_idx, const ResTable_config& config, - LoadedArscEntry* out_entry, ResTable_config* out_selected_config, - uint32_t* out_flags) const { - ATRACE_CALL(); +LoadedPackage::LoadedPackage() = default; +LoadedPackage::~LoadedPackage() = default; - // If the type IDs are offset in this package, we need to take that into account when searching - // for a type. - const TypeSpecPtr& ptr = type_specs_[type_idx - type_id_offset_]; - if (ptr == nullptr) { +// Precondition: The header passed in has already been verified, so reading any fields and trusting +// the ResChunk_header is safe. +static bool VerifyResTableType(const ResTable_type* header) { + if (header->id == 0) { + LOG(ERROR) << "RES_TABLE_TYPE_TYPE has invalid ID 0."; return false; } - // Don't bother checking if the entry ID is larger than - // the number of entries. - if (entry_idx >= dtohl(ptr->type_spec->entryCount)) { + const size_t entry_count = dtohl(header->entryCount); + if (entry_count > std::numeric_limits<uint16_t>::max()) { + LOG(ERROR) << "RES_TABLE_TYPE_TYPE has too many entries (" << entry_count << ")."; return false; } - const ResTable_config* best_config = nullptr; - const ResTable_type* best_type = nullptr; - uint32_t best_offset = 0; - - for (uint32_t i = 0; i < ptr->type_count; i++) { - const Type* type = &ptr->types[i]; - - if (type->configuration.match(config) && - (best_config == nullptr || type->configuration.isBetterThan(*best_config, &config))) { - // The configuration matches and is better than the previous selection. - // Find the entry value if it exists for this configuration. - size_t entry_count = dtohl(type->type->entryCount); - if (entry_idx < entry_count) { - const uint32_t* entry_offsets = reinterpret_cast<const uint32_t*>( - reinterpret_cast<const uint8_t*>(type->type) + dtohs(type->type->header.headerSize)); - const uint32_t offset = dtohl(entry_offsets[entry_idx]); - if (offset != ResTable_type::NO_ENTRY) { - // There is an entry for this resource, record it. - best_config = &type->configuration; - best_type = type->type; - best_offset = offset + dtohl(type->type->entriesStart); - } - } - } - } + // Make sure that there is enough room for the entry offsets. + const size_t offsets_offset = dtohs(header->header.headerSize); + const size_t entries_offset = dtohl(header->entriesStart); + const size_t offsets_length = sizeof(uint32_t) * entry_count; - if (best_type == nullptr) { + if (offsets_offset > entries_offset || entries_offset - offsets_offset < offsets_length) { + LOG(ERROR) << "RES_TABLE_TYPE_TYPE entry offsets overlap actual entry data."; return false; } - const uint32_t* flags = reinterpret_cast<const uint32_t*>(ptr->type_spec + 1); - *out_flags = dtohl(flags[entry_idx]); - *out_selected_config = *best_config; - - const ResTable_entry* best_entry = reinterpret_cast<const ResTable_entry*>( - reinterpret_cast<const uint8_t*>(best_type) + best_offset); - out_entry->entry = best_entry; - out_entry->type_string_ref = StringPoolRef(&type_string_pool_, best_type->id - 1); - out_entry->entry_string_ref = StringPoolRef(&key_string_pool_, dtohl(best_entry->key.index)); - return true; -} - -// The destructor gets generated into arbitrary translation units -// if left implicit, which causes the compiler to complain about -// forward declarations and incomplete types. -LoadedArsc::~LoadedArsc() {} - -bool LoadedArsc::FindEntry(uint32_t resid, const ResTable_config& config, - LoadedArscEntry* out_entry, ResTable_config* out_selected_config, - uint32_t* out_flags) const { - ATRACE_CALL(); - const uint8_t package_id = get_package_id(resid); - const uint8_t type_id = get_type_id(resid); - const uint16_t entry_id = get_entry_id(resid); - - if (type_id == 0) { - LOG(ERROR) << "Invalid ID 0x" << std::hex << resid << std::dec << "."; + if (entries_offset > dtohl(header->header.size)) { + LOG(ERROR) << "RES_TABLE_TYPE_TYPE entry offsets extend beyond chunk."; return false; } - for (const auto& loaded_package : packages_) { - if (loaded_package->package_id_ == package_id) { - return loaded_package->FindEntry(type_id - 1, entry_id, config, out_entry, - out_selected_config, out_flags); - } + if (entries_offset & 0x03) { + LOG(ERROR) << "RES_TABLE_TYPE_TYPE entries start at unaligned address."; + return false; } - return false; + return true; } -const LoadedPackage* LoadedArsc::GetPackageForId(uint32_t resid) const { - const uint8_t package_id = get_package_id(resid); - for (const auto& loaded_package : packages_) { - if (loaded_package->package_id_ == package_id) { - return loaded_package.get(); - } +static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset) { + // Check that the offset is aligned. + if (entry_offset & 0x03) { + LOG(ERROR) << "Entry at offset " << entry_offset << " is not 4-byte aligned."; + return false; } - return nullptr; -} - -static bool VerifyType(const Chunk& chunk) { - ATRACE_CALL(); - const ResTable_type* header = chunk.header<ResTable_type, kResTableTypeMinSize>(); - const size_t entry_count = dtohl(header->entryCount); - if (entry_count > std::numeric_limits<uint16_t>::max()) { - LOG(ERROR) << "Too many entries in RES_TABLE_TYPE_TYPE."; + // Check that the offset doesn't overflow. + if (entry_offset > std::numeric_limits<uint32_t>::max() - dtohl(type->entriesStart)) { + // Overflow in offset. + LOG(ERROR) << "Entry at offset " << entry_offset << " is too large."; return false; } - // Make sure that there is enough room for the entry offsets. - const size_t offsets_offset = chunk.header_size(); - const size_t entries_offset = dtohl(header->entriesStart); - const size_t offsets_length = sizeof(uint32_t) * entry_count; + const size_t chunk_size = dtohl(type->header.size); - if (offsets_offset + offsets_length > entries_offset) { - LOG(ERROR) << "Entry offsets overlap actual entry data."; + entry_offset += dtohl(type->entriesStart); + if (entry_offset > chunk_size - sizeof(ResTable_entry)) { + LOG(ERROR) << "Entry at offset " << entry_offset + << " is too large. No room for ResTable_entry."; return false; } - if (entries_offset > chunk.size()) { - LOG(ERROR) << "Entry offsets extend beyond chunk."; + const ResTable_entry* entry = reinterpret_cast<const ResTable_entry*>( + reinterpret_cast<const uint8_t*>(type) + entry_offset); + + const size_t entry_size = dtohs(entry->size); + if (entry_size < sizeof(*entry)) { + LOG(ERROR) << "ResTable_entry size " << entry_size << " at offset " << entry_offset + << " is too small."; return false; } - if (entries_offset & 0x03) { - LOG(ERROR) << "Entries start at unaligned address."; + if (entry_size > chunk_size || entry_offset > chunk_size - entry_size) { + LOG(ERROR) << "ResTable_entry size " << entry_size << " at offset " << entry_offset + << " is too large."; return false; } - // Check each entry offset. - const uint32_t* offsets = - reinterpret_cast<const uint32_t*>(reinterpret_cast<const uint8_t*>(header) + offsets_offset); - for (size_t i = 0; i < entry_count; i++) { - uint32_t offset = dtohl(offsets[i]); - if (offset != ResTable_type::NO_ENTRY) { - // Check that the offset is aligned. - if (offset & 0x03) { - LOG(ERROR) << "Entry offset at index " << i << " is not 4-byte aligned."; - return false; - } - - // Check that the offset doesn't overflow. - if (offset > std::numeric_limits<uint32_t>::max() - entries_offset) { - // Overflow in offset. - LOG(ERROR) << "Entry offset at index " << i << " is too large."; - return false; - } + if (entry_size < sizeof(ResTable_map_entry)) { + // There needs to be room for one Res_value struct. + if (entry_offset + entry_size > chunk_size - sizeof(Res_value)) { + LOG(ERROR) << "No room for Res_value after ResTable_entry at offset " << entry_offset + << " for type " << (int)type->id << "."; + return false; + } - offset += entries_offset; - if (offset > chunk.size() - sizeof(ResTable_entry)) { - LOG(ERROR) << "Entry offset at index " << i << " is too large. No room for ResTable_entry."; - return false; - } + const Res_value* value = + reinterpret_cast<const Res_value*>(reinterpret_cast<const uint8_t*>(entry) + entry_size); + const size_t value_size = dtohs(value->size); + if (value_size < sizeof(Res_value)) { + LOG(ERROR) << "Res_value at offset " << entry_offset << " is too small."; + return false; + } - const ResTable_entry* entry = reinterpret_cast<const ResTable_entry*>( - reinterpret_cast<const uint8_t*>(header) + offset); - const size_t entry_size = dtohs(entry->size); - if (entry_size < sizeof(*entry)) { - LOG(ERROR) << "ResTable_entry size " << entry_size << " is too small."; - return false; - } + if (value_size > chunk_size || entry_offset + entry_size > chunk_size - value_size) { + LOG(ERROR) << "Res_value size " << value_size << " at offset " << entry_offset + << " is too large."; + return false; + } + } else { + const ResTable_map_entry* map = reinterpret_cast<const ResTable_map_entry*>(entry); + const size_t map_entry_count = dtohl(map->count); + size_t map_entries_start = entry_offset + entry_size; + if (map_entries_start & 0x03) { + LOG(ERROR) << "Map entries at offset " << entry_offset << " start at unaligned offset."; + return false; + } - // Check the declared entrySize. - if (entry_size > chunk.size() || offset > chunk.size() - entry_size) { - LOG(ERROR) << "ResTable_entry size " << entry_size << " is too large."; - return false; - } + // Each entry is sizeof(ResTable_map) big. + if (map_entry_count > ((chunk_size - map_entries_start) / sizeof(ResTable_map))) { + LOG(ERROR) << "Too many map entries in ResTable_map_entry at offset " << entry_offset << "."; + return false; + } + } + return true; +} - // If this is a map entry, then keep validating. - if (entry_size >= sizeof(ResTable_map_entry)) { - const ResTable_map_entry* map = reinterpret_cast<const ResTable_map_entry*>(entry); - const size_t map_entry_count = dtohl(map->count); +const ResTable_entry* LoadedPackage::GetEntry(const ResTable_type* type_chunk, + uint16_t entry_index) { + uint32_t entry_offset = GetEntryOffset(type_chunk, entry_index); + if (entry_offset == ResTable_type::NO_ENTRY) { + return nullptr; + } + return GetEntryFromOffset(type_chunk, entry_offset); +} - size_t map_entries_start = offset + entry_size; - if (map_entries_start & 0x03) { - LOG(ERROR) << "Map entries start at unaligned offset."; - return false; - } +uint32_t LoadedPackage::GetEntryOffset(const ResTable_type* type_chunk, uint16_t entry_index) { + // The configuration matches and is better than the previous selection. + // Find the entry value if it exists for this configuration. + const size_t entry_count = dtohl(type_chunk->entryCount); + const size_t offsets_offset = dtohs(type_chunk->header.headerSize); + + // Check if there is the desired entry in this type. + + if (type_chunk->flags & ResTable_type::FLAG_SPARSE) { + // This is encoded as a sparse map, so perform a binary search. + const ResTable_sparseTypeEntry* sparse_indices = + reinterpret_cast<const ResTable_sparseTypeEntry*>( + reinterpret_cast<const uint8_t*>(type_chunk) + offsets_offset); + const ResTable_sparseTypeEntry* sparse_indices_end = sparse_indices + entry_count; + const ResTable_sparseTypeEntry* result = + std::lower_bound(sparse_indices, sparse_indices_end, entry_index, + [](const ResTable_sparseTypeEntry& entry, uint16_t entry_idx) { + return dtohs(entry.idx) < entry_idx; + }); + + if (result == sparse_indices_end || dtohs(result->idx) != entry_index) { + // No entry found. + return ResTable_type::NO_ENTRY; + } - // Each entry is sizeof(ResTable_map) big. - if (map_entry_count > ((chunk.size() - map_entries_start) / sizeof(ResTable_map))) { - LOG(ERROR) << "Too many map entries in ResTable_map_entry."; - return false; - } + // Extract the offset from the entry. Each offset must be a multiple of 4 so we store it as + // the real offset divided by 4. + return uint32_t{dtohs(result->offset)} * 4u; + } - // Great, all the map entries fit!. - } else { - // There needs to be room for one Res_value struct. - if (offset + entry_size > chunk.size() - sizeof(Res_value)) { - LOG(ERROR) << "No room for Res_value after ResTable_entry."; - return false; - } + // This type is encoded as a dense array. + if (entry_index >= entry_count) { + // This entry cannot be here. + return ResTable_type::NO_ENTRY; + } - const Res_value* value = reinterpret_cast<const Res_value*>( - reinterpret_cast<const uint8_t*>(entry) + entry_size); - const size_t value_size = dtohs(value->size); - if (value_size < sizeof(Res_value)) { - LOG(ERROR) << "Res_value is too small."; - return false; - } + const uint32_t* entry_offsets = reinterpret_cast<const uint32_t*>( + reinterpret_cast<const uint8_t*>(type_chunk) + offsets_offset); + return dtohl(entry_offsets[entry_index]); +} - if (value_size > chunk.size() || offset + entry_size > chunk.size() - value_size) { - LOG(ERROR) << "Res_value size is too large."; - return false; - } - } - } +const ResTable_entry* LoadedPackage::GetEntryFromOffset(const ResTable_type* type_chunk, + uint32_t offset) { + if (UNLIKELY(!VerifyResTableEntry(type_chunk, offset))) { + return nullptr; } - return true; + return reinterpret_cast<const ResTable_entry*>(reinterpret_cast<const uint8_t*>(type_chunk) + + offset + dtohl(type_chunk->entriesStart)); } void LoadedPackage::CollectConfigurations(bool exclude_mipmap, @@ -327,7 +267,7 @@ void LoadedPackage::CollectConfigurations(bool exclude_mipmap, const static std::u16string kMipMap = u"mipmap"; const size_t type_count = type_specs_.size(); for (size_t i = 0; i < type_count; i++) { - const util::unique_cptr<TypeSpec>& type_spec = type_specs_[i]; + const TypeSpecPtr& type_spec = type_specs_[i]; if (type_spec != nullptr) { if (exclude_mipmap) { const int type_idx = type_spec->type_spec->id - 1; @@ -348,8 +288,11 @@ void LoadedPackage::CollectConfigurations(bool exclude_mipmap, } } - for (size_t j = 0; j < type_spec->type_count; j++) { - out_configs->insert(type_spec->types[j].configuration); + const auto iter_end = type_spec->types + type_spec->type_count; + for (auto iter = type_spec->types; iter != iter_end; ++iter) { + ResTable_config config; + config.copyFromDtoH((*iter)->config); + out_configs->insert(config); } } } @@ -359,10 +302,12 @@ void LoadedPackage::CollectLocales(bool canonicalize, std::set<std::string>* out char temp_locale[RESTABLE_MAX_LOCALE_LEN]; const size_t type_count = type_specs_.size(); for (size_t i = 0; i < type_count; i++) { - const util::unique_cptr<TypeSpec>& type_spec = type_specs_[i]; + const TypeSpecPtr& type_spec = type_specs_[i]; if (type_spec != nullptr) { - for (size_t j = 0; j < type_spec->type_count; j++) { - const ResTable_config& configuration = type_spec->types[j].configuration; + const auto iter_end = type_spec->types + type_spec->type_count; + for (auto iter = type_spec->types; iter != iter_end; ++iter) { + ResTable_config configuration; + configuration.copyFromDtoH((*iter)->config); if (configuration.locale != 0) { configuration.getBcp47Locale(temp_locale, canonicalize); std::string locale(temp_locale); @@ -390,17 +335,17 @@ uint32_t LoadedPackage::FindEntryByName(const std::u16string& type_name, return 0u; } - for (size_t ti = 0; ti < type_spec->type_count; ti++) { - const Type* type = &type_spec->types[ti]; - size_t entry_count = dtohl(type->type->entryCount); + const auto iter_end = type_spec->types + type_spec->type_count; + for (auto iter = type_spec->types; iter != iter_end; ++iter) { + const ResTable_type* type = *iter; + size_t entry_count = dtohl(type->entryCount); for (size_t entry_idx = 0; entry_idx < entry_count; entry_idx++) { const uint32_t* entry_offsets = reinterpret_cast<const uint32_t*>( - reinterpret_cast<const uint8_t*>(type->type) + dtohs(type->type->header.headerSize)); + reinterpret_cast<const uint8_t*>(type) + dtohs(type->header.headerSize)); const uint32_t offset = dtohl(entry_offsets[entry_idx]); if (offset != ResTable_type::NO_ENTRY) { - const ResTable_entry* entry = - reinterpret_cast<const ResTable_entry*>(reinterpret_cast<const uint8_t*>(type->type) + - dtohl(type->type->entriesStart) + offset); + const ResTable_entry* entry = reinterpret_cast<const ResTable_entry*>( + reinterpret_cast<const uint8_t*>(type) + dtohl(type->entriesStart) + offset); if (dtohl(entry->key.index) == static_cast<uint32_t>(key_idx)) { // The package ID will be overridden by the caller (due to runtime assignment of package // IDs for shared libraries). @@ -412,28 +357,50 @@ uint32_t LoadedPackage::FindEntryByName(const std::u16string& type_name, return 0u; } -std::unique_ptr<LoadedPackage> LoadedPackage::Load(const Chunk& chunk) { +const LoadedPackage* LoadedArsc::GetPackageById(uint8_t package_id) const { + for (const auto& loaded_package : packages_) { + if (loaded_package->GetPackageId() == package_id) { + return loaded_package.get(); + } + } + return nullptr; +} + +std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk, + const LoadedIdmap* loaded_idmap, + bool system, bool load_as_shared_library) { ATRACE_CALL(); - std::unique_ptr<LoadedPackage> loaded_package{new LoadedPackage()}; + std::unique_ptr<LoadedPackage> loaded_package(new LoadedPackage()); + // typeIdOffset was added at some point, but we still must recognize apps built before this + // was added. constexpr size_t kMinPackageSize = sizeof(ResTable_package) - sizeof(ResTable_package::typeIdOffset); const ResTable_package* header = chunk.header<ResTable_package, kMinPackageSize>(); if (header == nullptr) { - LOG(ERROR) << "Chunk RES_TABLE_PACKAGE_TYPE is too small."; + LOG(ERROR) << "RES_TABLE_PACKAGE_TYPE too small."; return {}; } + loaded_package->system_ = system; + loaded_package->package_id_ = dtohl(header->id); - if (loaded_package->package_id_ == 0) { + if (loaded_package->package_id_ == 0 || + (loaded_package->package_id_ == kAppPackageId && load_as_shared_library)) { // Package ID of 0 means this is a shared library. loaded_package->dynamic_ = true; } + if (loaded_idmap != nullptr) { + // This is an overlay and so it needs to pretend to be the target package. + loaded_package->package_id_ = loaded_idmap->TargetPackageId(); + loaded_package->overlay_ = true; + } + if (header->header.headerSize >= sizeof(ResTable_package)) { uint32_t type_id_offset = dtohl(header->typeIdOffset); if (type_id_offset > std::numeric_limits<uint8_t>::max()) { - LOG(ERROR) << "Type ID offset in RES_TABLE_PACKAGE_TYPE is too large."; + LOG(ERROR) << "RES_TABLE_PACKAGE_TYPE type ID offset too large."; return {}; } loaded_package->type_id_offset_ = static_cast<int>(type_id_offset); @@ -464,7 +431,7 @@ std::unique_ptr<LoadedPackage> LoadedPackage::Load(const Chunk& chunk) { status_t err = loaded_package->type_string_pool_.setTo( child_chunk.header<ResStringPool_header>(), child_chunk.size()); if (err != NO_ERROR) { - LOG(ERROR) << "Corrupt package type string pool."; + LOG(ERROR) << "RES_STRING_POOL_TYPE for types corrupt."; return {}; } } else if (pool_address == header_address + dtohl(header->keyStrings)) { @@ -472,11 +439,11 @@ std::unique_ptr<LoadedPackage> LoadedPackage::Load(const Chunk& chunk) { status_t err = loaded_package->key_string_pool_.setTo( child_chunk.header<ResStringPool_header>(), child_chunk.size()); if (err != NO_ERROR) { - LOG(ERROR) << "Corrupt package key string pool."; + LOG(ERROR) << "RES_STRING_POOL_TYPE for keys corrupt."; return {}; } } else { - LOG(WARNING) << "Too many string pool chunks found in package."; + LOG(WARNING) << "Too many RES_STRING_POOL_TYPEs found in RES_TABLE_PACKAGE_TYPE."; } } break; @@ -490,7 +457,16 @@ std::unique_ptr<LoadedPackage> LoadedPackage::Load(const Chunk& chunk) { LOG(ERROR) << "Too many type configurations, overflow detected."; return {}; } - loaded_package->type_specs_.editItemAt(last_type_idx) = std::move(type_spec_ptr); + + // We only add the type to the package if there is no IDMAP, or if the type is + // overlaying something. + if (loaded_idmap == nullptr || type_spec_ptr->idmap_entries != nullptr) { + // If this is an overlay, insert it at the target type ID. + if (type_spec_ptr->idmap_entries != nullptr) { + last_type_idx = dtohs(type_spec_ptr->idmap_entries->target_type_id) - 1; + } + loaded_package->type_specs_.editItemAt(last_type_idx) = std::move(type_spec_ptr); + } types_builder = {}; last_type_idx = 0; @@ -498,18 +474,18 @@ std::unique_ptr<LoadedPackage> LoadedPackage::Load(const Chunk& chunk) { const ResTable_typeSpec* type_spec = child_chunk.header<ResTable_typeSpec>(); if (type_spec == nullptr) { - LOG(ERROR) << "Chunk RES_TABLE_TYPE_SPEC_TYPE is too small."; + LOG(ERROR) << "RES_TABLE_TYPE_SPEC_TYPE too small."; return {}; } if (type_spec->id == 0) { - LOG(ERROR) << "Chunk RES_TABLE_TYPE_SPEC_TYPE has invalid ID 0."; + LOG(ERROR) << "RES_TABLE_TYPE_SPEC_TYPE has invalid ID 0."; return {}; } if (loaded_package->type_id_offset_ + static_cast<int>(type_spec->id) > std::numeric_limits<uint8_t>::max()) { - LOG(ERROR) << "Chunk RES_TABLE_TYPE_SPEC_TYPE has out of range ID."; + LOG(ERROR) << "RES_TABLE_TYPE_SPEC_TYPE has out of range ID."; return {}; } @@ -521,39 +497,41 @@ std::unique_ptr<LoadedPackage> LoadedPackage::Load(const Chunk& chunk) { // There can only be 2^16 entries in a type, because that is the ID // space for entries (EEEE) in the resource ID 0xPPTTEEEE. if (entry_count > std::numeric_limits<uint16_t>::max()) { - LOG(ERROR) << "Too many entries in RES_TABLE_TYPE_SPEC_TYPE: " << entry_count << "."; + LOG(ERROR) << "RES_TABLE_TYPE_SPEC_TYPE has too many entries (" << entry_count << ")."; return {}; } if (entry_count * sizeof(uint32_t) > chunk.data_size()) { - LOG(ERROR) << "Chunk too small to hold entries in RES_TABLE_TYPE_SPEC_TYPE."; + LOG(ERROR) << "RES_TABLE_TYPE_SPEC_TYPE too small to hold entries."; return {}; } last_type_idx = type_spec->id - 1; - types_builder = util::make_unique<TypeSpecPtrBuilder>(type_spec); + + // If this is an overlay, associate the mapping of this type to the target type + // from the IDMAP. + const IdmapEntry_header* idmap_entry_header = nullptr; + if (loaded_idmap != nullptr) { + idmap_entry_header = loaded_idmap->GetEntryMapForType(type_spec->id); + } + + types_builder = util::make_unique<TypeSpecPtrBuilder>(type_spec, idmap_entry_header); } break; case RES_TABLE_TYPE_TYPE: { const ResTable_type* type = child_chunk.header<ResTable_type, kResTableTypeMinSize>(); if (type == nullptr) { - LOG(ERROR) << "Chunk RES_TABLE_TYPE_TYPE is too small."; + LOG(ERROR) << "RES_TABLE_TYPE_TYPE too small."; return {}; } - if (type->id == 0) { - LOG(ERROR) << "Chunk RES_TABLE_TYPE_TYPE has invalid ID 0."; + if (!VerifyResTableType(type)) { return {}; } // Type chunks must be preceded by their TypeSpec chunks. if (!types_builder || type->id - 1 != last_type_idx) { - LOG(ERROR) << "Found RES_TABLE_TYPE_TYPE chunk without " - "RES_TABLE_TYPE_SPEC_TYPE."; - return {}; - } - - if (!VerifyType(child_chunk)) { + LOG(ERROR) << "RES_TABLE_TYPE_TYPE found without preceding RES_TABLE_TYPE_SPEC_TYPE."; return {}; } @@ -563,12 +541,12 @@ std::unique_ptr<LoadedPackage> LoadedPackage::Load(const Chunk& chunk) { case RES_TABLE_LIBRARY_TYPE: { const ResTable_lib_header* lib = child_chunk.header<ResTable_lib_header>(); if (lib == nullptr) { - LOG(ERROR) << "Chunk RES_TABLE_LIBRARY_TYPE is too small."; + LOG(ERROR) << "RES_TABLE_LIBRARY_TYPE too small."; return {}; } if (child_chunk.data_size() / sizeof(ResTable_lib_entry) < dtohl(lib->count)) { - LOG(ERROR) << "Chunk too small to hold entries in RES_TABLE_LIBRARY_TYPE."; + LOG(ERROR) << "RES_TABLE_LIBRARY_TYPE too small to hold entries."; return {}; } @@ -608,21 +586,32 @@ std::unique_ptr<LoadedPackage> LoadedPackage::Load(const Chunk& chunk) { LOG(ERROR) << "Too many type configurations, overflow detected."; return {}; } - loaded_package->type_specs_.editItemAt(last_type_idx) = std::move(type_spec_ptr); + + // We only add the type to the package if there is no IDMAP, or if the type is + // overlaying something. + if (loaded_idmap == nullptr || type_spec_ptr->idmap_entries != nullptr) { + // If this is an overlay, insert it at the target type ID. + if (type_spec_ptr->idmap_entries != nullptr) { + last_type_idx = dtohs(type_spec_ptr->idmap_entries->target_type_id) - 1; + } + loaded_package->type_specs_.editItemAt(last_type_idx) = std::move(type_spec_ptr); + } } if (iter.HadError()) { LOG(ERROR) << iter.GetLastError(); return {}; } - return loaded_package; + return std::move(loaded_package); } -bool LoadedArsc::LoadTable(const Chunk& chunk, bool load_as_shared_library) { + +bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap, + bool load_as_shared_library) { ATRACE_CALL(); const ResTable_header* header = chunk.header<ResTable_header>(); if (header == nullptr) { - LOG(ERROR) << "Chunk RES_TABLE_TYPE is too small."; + LOG(ERROR) << "RES_TABLE_TYPE too small."; return false; } @@ -641,33 +630,27 @@ bool LoadedArsc::LoadTable(const Chunk& chunk, bool load_as_shared_library) { status_t err = global_string_pool_.setTo(child_chunk.header<ResStringPool_header>(), child_chunk.size()); if (err != NO_ERROR) { - LOG(ERROR) << "Corrupt string pool."; + LOG(ERROR) << "RES_STRING_POOL_TYPE corrupt."; return false; } } else { - LOG(WARNING) << "Multiple string pool chunks found in resource table."; + LOG(WARNING) << "Multiple RES_STRING_POOL_TYPEs found in RES_TABLE_TYPE."; } break; case RES_TABLE_PACKAGE_TYPE: { if (packages_seen + 1 > package_count) { LOG(ERROR) << "More package chunks were found than the " << package_count - << " declared in the " - "header."; + << " declared in the header."; return false; } packages_seen++; - std::unique_ptr<LoadedPackage> loaded_package = LoadedPackage::Load(child_chunk); + std::unique_ptr<const LoadedPackage> loaded_package = + LoadedPackage::Load(child_chunk, loaded_idmap, system_, load_as_shared_library); if (!loaded_package) { return false; } - - // Mark the package as dynamic if we are forcefully loading the Apk as a shared library. - if (loaded_package->package_id_ == kAppPackageId) { - loaded_package->dynamic_ = load_as_shared_library; - } - loaded_package->system_ = system_; packages_.push_back(std::move(loaded_package)); } break; @@ -684,7 +667,8 @@ bool LoadedArsc::LoadTable(const Chunk& chunk, bool load_as_shared_library) { return true; } -std::unique_ptr<const LoadedArsc> LoadedArsc::Load(const void* data, size_t len, bool system, +std::unique_ptr<const LoadedArsc> LoadedArsc::Load(const StringPiece& data, + const LoadedIdmap* loaded_idmap, bool system, bool load_as_shared_library) { ATRACE_CALL(); @@ -692,12 +676,12 @@ std::unique_ptr<const LoadedArsc> LoadedArsc::Load(const void* data, size_t len, std::unique_ptr<LoadedArsc> loaded_arsc(new LoadedArsc()); loaded_arsc->system_ = system; - ChunkIterator iter(data, len); + ChunkIterator iter(data.data(), data.size()); while (iter.HasNext()) { const Chunk chunk = iter.Next(); switch (chunk.type()) { case RES_TABLE_TYPE: - if (!loaded_arsc->LoadTable(chunk, load_as_shared_library)) { + if (!loaded_arsc->LoadTable(chunk, loaded_idmap, load_as_shared_library)) { return {}; } break; @@ -717,4 +701,8 @@ std::unique_ptr<const LoadedArsc> LoadedArsc::Load(const void* data, size_t len, return std::move(loaded_arsc); } +std::unique_ptr<const LoadedArsc> LoadedArsc::CreateEmpty() { + return std::unique_ptr<LoadedArsc>(new LoadedArsc()); +} + } // namespace android diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp index 7a0ef2b770ce..696a00c7b2c0 100644 --- a/libs/androidfw/ResourceTypes.cpp +++ b/libs/androidfw/ResourceTypes.cpp @@ -812,7 +812,13 @@ const char* ResStringPool::string8At(size_t idx, size_t* outLen) const *outLen = encLen; if ((uint32_t)(str+encLen-strings) < mStringPoolSize) { - return (const char*)str; + // Reject malformed (non null-terminated) strings + if (str[encLen] != 0x00) { + ALOGW("Bad string block: string #%d is not null-terminated", + (int)idx); + return NULL; + } + return (const char*)str; } else { ALOGW("Bad string block: string #%d extends to %d, past end at %d\n", (int)idx, (int)(str+encLen-strings), (int)mStringPoolSize); @@ -1868,52 +1874,76 @@ void ResTable_config::swapHtoD() { /* static */ inline int compareLocales(const ResTable_config &l, const ResTable_config &r) { if (l.locale != r.locale) { - // NOTE: This is the old behaviour with respect to comparison orders. - // The diff value here doesn't make much sense (given our bit packing scheme) - // but it's stable, and that's all we need. - return l.locale - r.locale; + return (l.locale > r.locale) ? 1 : -1; } - // The language & region are equal, so compare the scripts and variants. + // The language & region are equal, so compare the scripts, variants and + // numbering systms in this order. Comparison of variants and numbering + // systems should happen very infrequently (if at all.) + // The comparison code relies on memcmp low-level optimizations that make it + // more efficient than strncmp. const char emptyScript[sizeof(l.localeScript)] = {'\0', '\0', '\0', '\0'}; const char *lScript = l.localeScriptWasComputed ? emptyScript : l.localeScript; const char *rScript = r.localeScriptWasComputed ? emptyScript : r.localeScript; + int script = memcmp(lScript, rScript, sizeof(l.localeScript)); if (script) { return script; } - // The language, region and script are equal, so compare variants. - // - // This should happen very infrequently (if at all.) - return memcmp(l.localeVariant, r.localeVariant, sizeof(l.localeVariant)); + int variant = memcmp(l.localeVariant, r.localeVariant, sizeof(l.localeVariant)); + if (variant) { + return variant; + } + + return memcmp(l.localeNumberingSystem, r.localeNumberingSystem, + sizeof(l.localeNumberingSystem)); } int ResTable_config::compare(const ResTable_config& o) const { - int32_t diff = (int32_t)(imsi - o.imsi); - if (diff != 0) return diff; - diff = compareLocales(*this, o); - if (diff != 0) return diff; - diff = (int32_t)(screenType - o.screenType); - if (diff != 0) return diff; - diff = (int32_t)(input - o.input); - if (diff != 0) return diff; - diff = (int32_t)(screenSize - o.screenSize); - if (diff != 0) return diff; - diff = (int32_t)(version - o.version); - if (diff != 0) return diff; - diff = (int32_t)(screenLayout - o.screenLayout); - if (diff != 0) return diff; - diff = (int32_t)(screenLayout2 - o.screenLayout2); - if (diff != 0) return diff; - diff = (int32_t)(colorMode - o.colorMode); - if (diff != 0) return diff; - diff = (int32_t)(uiMode - o.uiMode); - if (diff != 0) return diff; - diff = (int32_t)(smallestScreenWidthDp - o.smallestScreenWidthDp); - if (diff != 0) return diff; - diff = (int32_t)(screenSizeDp - o.screenSizeDp); - return (int)diff; + if (imsi != o.imsi) { + return (imsi > o.imsi) ? 1 : -1; + } + + int32_t diff = compareLocales(*this, o); + if (diff < 0) { + return -1; + } + if (diff > 0) { + return 1; + } + + if (screenType != o.screenType) { + return (screenType > o.screenType) ? 1 : -1; + } + if (input != o.input) { + return (input > o.input) ? 1 : -1; + } + if (screenSize != o.screenSize) { + return (screenSize > o.screenSize) ? 1 : -1; + } + if (version != o.version) { + return (version > o.version) ? 1 : -1; + } + if (screenLayout != o.screenLayout) { + return (screenLayout > o.screenLayout) ? 1 : -1; + } + if (screenLayout2 != o.screenLayout2) { + return (screenLayout2 > o.screenLayout2) ? 1 : -1; + } + if (colorMode != o.colorMode) { + return (colorMode > o.colorMode) ? 1 : -1; + } + if (uiMode != o.uiMode) { + return (uiMode > o.uiMode) ? 1 : -1; + } + if (smallestScreenWidthDp != o.smallestScreenWidthDp) { + return (smallestScreenWidthDp > o.smallestScreenWidthDp) ? 1 : -1; + } + if (screenSizeDp != o.screenSizeDp) { + return (screenSizeDp > o.screenSizeDp) ? 1 : -1; + } + return 0; } int ResTable_config::compareLogical(const ResTable_config& o) const { @@ -2008,6 +2038,22 @@ int ResTable_config::diff(const ResTable_config& o) const { return diffs; } +// There isn't a well specified "importance" order between variants and +// scripts. We can't easily tell whether, say "en-Latn-US" is more or less +// specific than "en-US-POSIX". +// +// We therefore arbitrarily decide to give priority to variants over +// scripts since it seems more useful to do so. We will consider +// "en-US-POSIX" to be more specific than "en-Latn-US". +// +// Unicode extension keywords are considered to be less important than +// scripts and variants. +inline int ResTable_config::getImportanceScoreOfLocale() const { + return (localeVariant[0] ? 4 : 0) + + (localeScript[0] && !localeScriptWasComputed ? 2: 0) + + (localeNumberingSystem[0] ? 1: 0); +} + int ResTable_config::isLocaleMoreSpecificThan(const ResTable_config& o) const { if (locale || o.locale) { if (language[0] != o.language[0]) { @@ -2021,21 +2067,7 @@ int ResTable_config::isLocaleMoreSpecificThan(const ResTable_config& o) const { } } - // There isn't a well specified "importance" order between variants and - // scripts. We can't easily tell whether, say "en-Latn-US" is more or less - // specific than "en-US-POSIX". - // - // We therefore arbitrarily decide to give priority to variants over - // scripts since it seems more useful to do so. We will consider - // "en-US-POSIX" to be more specific than "en-Latn-US". - - const int score = ((localeScript[0] != '\0' && !localeScriptWasComputed) ? 1 : 0) + - ((localeVariant[0] != '\0') ? 2 : 0); - - const int oScore = (o.localeScript[0] != '\0' && !o.localeScriptWasComputed ? 1 : 0) + - ((o.localeVariant[0] != '\0') ? 2 : 0); - - return score - oScore; + return getImportanceScoreOfLocale() - o.getImportanceScoreOfLocale(); } bool ResTable_config::isMoreSpecificThan(const ResTable_config& o) const { @@ -2292,6 +2324,17 @@ bool ResTable_config::isLocaleBetterThan(const ResTable_config& o, return localeMatches; } + // The variants are the same, try numbering system. + const bool localeNumsysMatches = strncmp(localeNumberingSystem, + requested->localeNumberingSystem, + sizeof(localeNumberingSystem)) == 0; + const bool otherNumsysMatches = strncmp(o.localeNumberingSystem, + requested->localeNumberingSystem, + sizeof(localeNumberingSystem)) == 0; + if (localeNumsysMatches != otherNumsysMatches) { + return localeNumsysMatches; + } + // Finally, the languages, although equivalent, may still be different // (like for Tagalog and Filipino). Identical is better than just // equivalent. @@ -2759,7 +2802,7 @@ void ResTable_config::appendDirLocale(String8& out) const { return; } const bool scriptWasProvided = localeScript[0] != '\0' && !localeScriptWasComputed; - if (!scriptWasProvided && !localeVariant[0]) { + if (!scriptWasProvided && !localeVariant[0] && !localeNumberingSystem[0]) { // Legacy format. if (out.size() > 0) { out.append("-"); @@ -2804,6 +2847,12 @@ void ResTable_config::appendDirLocale(String8& out) const { out.append("+"); out.append(localeVariant, strnlen(localeVariant, sizeof(localeVariant))); } + + if (localeNumberingSystem[0]) { + out.append("+u+nu+"); + out.append(localeNumberingSystem, + strnlen(localeNumberingSystem, sizeof(localeNumberingSystem))); + } } void ResTable_config::getBcp47Locale(char str[RESTABLE_MAX_LOCALE_LEN], bool canonicalize) const { @@ -2846,15 +2895,119 @@ void ResTable_config::getBcp47Locale(char str[RESTABLE_MAX_LOCALE_LEN], bool can str[charsWritten++] = '-'; } memcpy(str + charsWritten, localeVariant, sizeof(localeVariant)); - } -} + charsWritten += strnlen(str + charsWritten, sizeof(localeVariant)); + } + + // Add Unicode extension only if at least one other locale component is present + if (localeNumberingSystem[0] != '\0' && charsWritten > 0) { + static constexpr char NU_PREFIX[] = "-u-nu-"; + static constexpr size_t NU_PREFIX_LEN = sizeof(NU_PREFIX) - 1; + memcpy(str + charsWritten, NU_PREFIX, NU_PREFIX_LEN); + charsWritten += NU_PREFIX_LEN; + memcpy(str + charsWritten, localeNumberingSystem, sizeof(localeNumberingSystem)); + } +} + +struct LocaleParserState { + enum State : uint8_t { + BASE, UNICODE_EXTENSION, IGNORE_THE_REST + } parserState; + enum UnicodeState : uint8_t { + /* Initial state after the Unicode singleton is detected. Either a keyword + * or an attribute is expected. */ + NO_KEY, + /* Unicode extension key (but not attribute) is expected. Next states: + * NO_KEY, IGNORE_KEY or NUMBERING_SYSTEM. */ + EXPECT_KEY, + /* A key is detected, however it is not supported for now. Ignore its + * value. Next states: IGNORE_KEY or NUMBERING_SYSTEM. */ + IGNORE_KEY, + /* Numbering system key was detected. Store its value in the configuration + * localeNumberingSystem field. Next state: EXPECT_KEY */ + NUMBERING_SYSTEM + } unicodeState; + + LocaleParserState(): parserState(BASE), unicodeState(NO_KEY) {} +}; -/* static */ inline bool assignLocaleComponent(ResTable_config* config, - const char* start, size_t size) { +/* static */ inline LocaleParserState assignLocaleComponent(ResTable_config* config, + const char* start, size_t size, LocaleParserState state) { + + /* It is assumed that this function is not invoked with state.parserState + * set to IGNORE_THE_REST. The condition is checked by setBcp47Locale + * function. */ + + if (state.parserState == LocaleParserState::UNICODE_EXTENSION) { + switch (size) { + case 1: + /* Other BCP 47 extensions are not supported at the moment */ + state.parserState = LocaleParserState::IGNORE_THE_REST; + break; + case 2: + if (state.unicodeState == LocaleParserState::NO_KEY || + state.unicodeState == LocaleParserState::EXPECT_KEY) { + /* Analyze Unicode extension key. Currently only 'nu' + * (numbering system) is supported.*/ + if ((start[0] == 'n' || start[0] == 'N') && + (start[1] == 'u' || start[1] == 'U')) { + state.unicodeState = LocaleParserState::NUMBERING_SYSTEM; + } else { + state.unicodeState = LocaleParserState::IGNORE_KEY; + } + } else { + /* Keys are not allowed in other state allowed, ignore the rest. */ + state.parserState = LocaleParserState::IGNORE_THE_REST; + } + break; + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + switch (state.unicodeState) { + case LocaleParserState::NUMBERING_SYSTEM: + /* Accept only the first occurrence of the numbering system. */ + if (config->localeNumberingSystem[0] == '\0') { + for (size_t i = 0; i < size; ++i) { + config->localeNumberingSystem[i] = tolower(start[i]); + } + state.unicodeState = LocaleParserState::EXPECT_KEY; + } else { + state.parserState = LocaleParserState::IGNORE_THE_REST; + } + break; + case LocaleParserState::IGNORE_KEY: + /* Unsupported Unicode keyword. Ignore. */ + state.unicodeState = LocaleParserState::EXPECT_KEY; + break; + case LocaleParserState::EXPECT_KEY: + /* A keyword followed by an attribute is not allowed. */ + state.parserState = LocaleParserState::IGNORE_THE_REST; + break; + case LocaleParserState::NO_KEY: + /* Extension attribute. Do nothing. */ + break; + default: + break; + } + break; + default: + /* Unexpected field length - ignore the rest and treat as an error */ + state.parserState = LocaleParserState::IGNORE_THE_REST; + } + return state; + } switch (size) { case 0: - return false; + state.parserState = LocaleParserState::IGNORE_THE_REST; + break; + case 1: + state.parserState = (start[0] == 'u' || start[0] == 'U') + ? LocaleParserState::UNICODE_EXTENSION + : LocaleParserState::IGNORE_THE_REST; + break; case 2: case 3: config->language[0] ? config->packRegion(start) : config->packLanguage(start); @@ -2878,30 +3031,32 @@ void ResTable_config::getBcp47Locale(char str[RESTABLE_MAX_LOCALE_LEN], bool can } break; default: - return false; + state.parserState = LocaleParserState::IGNORE_THE_REST; } - return true; + return state; } void ResTable_config::setBcp47Locale(const char* in) { - locale = 0; - memset(localeScript, 0, sizeof(localeScript)); - memset(localeVariant, 0, sizeof(localeVariant)); + clearLocale(); - const char* separator = in; const char* start = in; - while ((separator = strchr(start, '-')) != NULL) { + LocaleParserState state; + while (const char* separator = strchr(start, '-')) { const size_t size = separator - start; - if (!assignLocaleComponent(this, start, size)) { - fprintf(stderr, "Invalid BCP-47 locale string: %s", in); + state = assignLocaleComponent(this, start, size, state); + if (state.parserState == LocaleParserState::IGNORE_THE_REST) { + fprintf(stderr, "Invalid BCP-47 locale string: %s\n", in); + break; } - start = (separator + 1); } - const size_t size = in + strlen(in) - start; - assignLocaleComponent(this, start, size); + if (state.parserState != LocaleParserState::IGNORE_THE_REST) { + const size_t size = strlen(start); + assignLocaleComponent(this, start, size, state); + } + localeScriptWasComputed = (localeScript[0] == '\0'); if (localeScriptWasComputed) { computeScript(); @@ -6014,9 +6169,6 @@ void ResTable::getLocales(Vector<String8>* locales, bool includeSystemLocales, StringPoolRef::StringPoolRef(const ResStringPool* pool, uint32_t index) : mPool(pool), mIndex(index) {} -StringPoolRef::StringPoolRef() - : mPool(NULL), mIndex(0) {} - const char* StringPoolRef::string8(size_t* outLen) const { if (mPool != NULL) { return mPool->string8At(mIndex, outLen); @@ -6759,6 +6911,9 @@ status_t ResTable::createIdmap(const ResTable& overlay, return UNKNOWN_ERROR; } + // The number of resources overlaid that were not explicitly marked overlayable. + size_t forcedOverlayCount = 0u; + KeyedVector<uint8_t, IdmapTypeMap> map; // overlaid packages are assumed to contain only one package group @@ -6798,6 +6953,7 @@ status_t ResTable::createIdmap(const ResTable& overlay, continue; } + uint32_t typeSpecFlags = 0u; const String16 overlayType(resName.type, resName.typeLen); const String16 overlayName(resName.name, resName.nameLen); uint32_t overlayResID = overlay.identifierForName(overlayName.string(), @@ -6805,14 +6961,23 @@ status_t ResTable::createIdmap(const ResTable& overlay, overlayType.string(), overlayType.size(), overlayPackage.string(), - overlayPackage.size()); + overlayPackage.size(), + &typeSpecFlags); if (overlayResID == 0) { + // No such target resource was found. if (typeMap.entryMap.isEmpty()) { typeMap.entryOffset++; } continue; } + // Now that we know this is being overlaid, check if it can be, and emit a warning if + // it can't. + if ((dtohl(typeConfigs->typeSpecFlags[entryIndex]) & + ResTable_typeSpec::SPEC_OVERLAYABLE) == 0) { + forcedOverlayCount++; + } + if (typeMap.overlayTypeId == -1) { typeMap.overlayTypeId = Res_GETTYPE(overlayResID) + 1; } @@ -6891,6 +7056,10 @@ status_t ResTable::createIdmap(const ResTable& overlay, typeData += entryCount * 2; } + if (forcedOverlayCount > 0) { + ALOGW("idmap: overlaid %zu resources not marked overlayable", forcedOverlayCount); + } + return NO_ERROR; } diff --git a/libs/androidfw/ZipFileRO.cpp b/libs/androidfw/ZipFileRO.cpp index 49fe8a261178..6e2ca60cc3d3 100644 --- a/libs/androidfw/ZipFileRO.cpp +++ b/libs/androidfw/ZipFileRO.cpp @@ -55,7 +55,9 @@ private: ZipFileRO::~ZipFileRO() { CloseArchive(mHandle); - free(mFileName); + if (mFileName != NULL) { + free(mFileName); + } } /* @@ -76,6 +78,20 @@ ZipFileRO::~ZipFileRO() { } +/* static */ ZipFileRO* ZipFileRO::openFd(int fd, const char* debugFileName, + bool assume_ownership) +{ + ZipArchiveHandle handle; + const int32_t error = OpenArchiveFd(fd, debugFileName, &handle, assume_ownership); + if (error) { + ALOGW("Error opening archive fd %d %s: %s", fd, debugFileName, ErrorCodeString(error)); + CloseArchive(handle); + return NULL; + } + + return new ZipFileRO(handle, strdup(debugFileName)); +} + ZipEntryRO ZipFileRO::findEntryByName(const char* entryName) const { _ZipEntryRO* data = new _ZipEntryRO; @@ -139,7 +155,8 @@ bool ZipFileRO::startIteration(void** cookie, const char* prefix, const char* su prefix ? &pe : NULL, suffix ? &se : NULL); if (error) { - ALOGW("Could not start iteration over %s: %s", mFileName, ErrorCodeString(error)); + ALOGW("Could not start iteration over %s: %s", mFileName != NULL ? mFileName : "<null>", + ErrorCodeString(error)); delete ze; return false; } @@ -154,7 +171,8 @@ ZipEntryRO ZipFileRO::nextEntry(void* cookie) int32_t error = Next(ze->cookie, &(ze->entry), &(ze->name)); if (error) { if (error != -1) { - ALOGW("Error iteration over %s: %s", mFileName, ErrorCodeString(error)); + ALOGW("Error iteration over %s: %s", mFileName != NULL ? mFileName : "<null>", + ErrorCodeString(error)); } return NULL; } diff --git a/libs/androidfw/include/androidfw/ApkAssets.h b/libs/androidfw/include/androidfw/ApkAssets.h index b7e66fb68be5..69702e314442 100644 --- a/libs/androidfw/include/androidfw/ApkAssets.h +++ b/libs/androidfw/include/androidfw/ApkAssets.h @@ -21,7 +21,7 @@ #include <string> #include "android-base/macros.h" -#include "ziparchive/zip_archive.h" +#include "android-base/unique_fd.h" #include "androidfw/Asset.h" #include "androidfw/LoadedArsc.h" @@ -29,41 +29,73 @@ namespace android { +class LoadedIdmap; + // Holds an APK. class ApkAssets { public: + // Creates an ApkAssets. + // If `system` is true, the package is marked as a system package, and allows some functions to + // filter out this package when computing what configurations/resources are available. static std::unique_ptr<const ApkAssets> Load(const std::string& path, bool system = false); + + // Creates an ApkAssets, but forces any package with ID 0x7f to be loaded as a shared library. + // If `system` is true, the package is marked as a system package, and allows some functions to + // filter out this package when computing what configurations/resources are available. static std::unique_ptr<const ApkAssets> LoadAsSharedLibrary(const std::string& path, bool system = false); + // Creates an ApkAssets from an IDMAP, which contains the original APK path, and the overlay + // data. + // If `system` is true, the package is marked as a system package, and allows some functions to + // filter out this package when computing what configurations/resources are available. + static std::unique_ptr<const ApkAssets> LoadOverlay(const std::string& idmap_path, + bool system = false); + + // Creates an ApkAssets from the given file descriptor, and takes ownership of the file + // descriptor. The `friendly_name` is some name that will be used to identify the source of + // this ApkAssets in log messages and other debug scenarios. + // If `system` is true, the package is marked as a system package, and allows some functions to + // filter out this package when computing what configurations/resources are available. + // If `force_shared_lib` is true, any package with ID 0x7f is loaded as a shared library. + static std::unique_ptr<const ApkAssets> LoadFromFd(base::unique_fd fd, + const std::string& friendly_name, bool system, + bool force_shared_lib); + std::unique_ptr<Asset> Open(const std::string& path, Asset::AccessMode mode = Asset::AccessMode::ACCESS_RANDOM) const; bool ForEachFile(const std::string& path, const std::function<void(const StringPiece&, FileType)>& f) const; - inline const std::string& GetPath() const { return path_; } + inline const std::string& GetPath() const { + return path_; + } - inline const LoadedArsc* GetLoadedArsc() const { return loaded_arsc_.get(); } + // This is never nullptr. + inline const LoadedArsc* GetLoadedArsc() const { + return loaded_arsc_.get(); + } private: DISALLOW_COPY_AND_ASSIGN(ApkAssets); - static std::unique_ptr<const ApkAssets> LoadImpl(const std::string& path, bool system, - bool load_as_shared_library); + static std::unique_ptr<const ApkAssets> LoadImpl(base::unique_fd fd, const std::string& path, + std::unique_ptr<Asset> idmap_asset, + std::unique_ptr<const LoadedIdmap> loaded_idmap, + bool system, bool load_as_shared_library); - ApkAssets() = default; + // Creates an Asset from any file on the file system. + static std::unique_ptr<Asset> CreateAssetFromFile(const std::string& path); - struct ZipArchivePtrCloser { - void operator()(::ZipArchiveHandle handle) { ::CloseArchive(handle); } - }; + ApkAssets(void* unmanaged_handle, const std::string& path); - using ZipArchivePtr = - std::unique_ptr<typename std::remove_pointer<::ZipArchiveHandle>::type, ZipArchivePtrCloser>; + using ZipArchivePtr = std::unique_ptr<void, void(*)(void*)>; ZipArchivePtr zip_handle_; - std::string path_; + const std::string path_; std::unique_ptr<Asset> resources_asset_; + std::unique_ptr<Asset> idmap_asset_; std::unique_ptr<const LoadedArsc> loaded_arsc_; }; diff --git a/libs/androidfw/include/androidfw/AssetManager.h b/libs/androidfw/include/androidfw/AssetManager.h index 0441b9d789e2..ecc5dc1ad331 100644 --- a/libs/androidfw/include/androidfw/AssetManager.h +++ b/libs/androidfw/include/androidfw/AssetManager.h @@ -92,6 +92,20 @@ public: bool addOverlayPath(const String8& path, int32_t* cookie); /* + * Add a new source for assets from an already open file descriptor. + * This does not give full AssetManager functionality for these assets, + * since the origin of the file is not known for purposes of sharing, + * overlay resolution, and other features. However it does allow you + * to do simple access to the contents of the given fd as an apk file. + * + * Returns "true" on success, "false" on failure. If 'cookie' is non-NULL, + * then on success, *cookie is set to the value corresponding to the + * newly-added asset source. + */ + bool addAssetFd(int fd, const String8& debugPathName, int32_t* cookie, + bool appAsLib=false, bool assume_ownership=true); + + /* * Convenience for adding the standard system assets. Uses the * ANDROID_ROOT environment variable to find them. */ @@ -195,24 +209,29 @@ public: uint32_t targetCrc, uint32_t overlayCrc, uint32_t** outData, size_t* outSize); private: + class SharedZip; + struct asset_path { - asset_path() : path(""), type(kFileTypeRegular), idmap(""), - isSystemOverlay(false), isSystemAsset(false) {} + asset_path() : path(""), rawFd(-1), type(kFileTypeRegular), idmap(""), + isSystemOverlay(false), isSystemAsset(false), assumeOwnership(false) {} String8 path; + int rawFd; FileType type; String8 idmap; bool isSystemOverlay; bool isSystemAsset; + bool assumeOwnership; + sp<SharedZip> zip; }; Asset* openNonAssetInPathLocked(const char* fileName, AccessMode mode, - const asset_path& path); + asset_path& path); String8 createPathNameLocked(const asset_path& path, const char* rootDir); String8 createZipSourceNameLocked(const String8& zipFileName, const String8& dirName, const String8& fileName); - ZipFileRO* getZipFileLocked(const asset_path& path); + ZipFileRO* getZipFileLocked(asset_path& path); Asset* openAssetFromFileLocked(const String8& fileName, AccessMode mode); Asset* openAssetFromZipLocked(const ZipFileRO* pZipFile, const ZipEntryRO entry, AccessMode mode, const String8& entryName); @@ -228,7 +247,7 @@ private: const ResTable* getResTable(bool required = true) const; void setLocaleLocked(const char* locale); void updateResourceParamsLocked() const; - bool appendPathToResTable(const asset_path& ap, bool appAsLib=false) const; + bool appendPathToResTable(asset_path& ap, bool appAsLib=false) const; Asset* openIdmapLocked(const struct asset_path& ap) const; @@ -238,6 +257,7 @@ private: class SharedZip : public RefBase { public: static sp<SharedZip> get(const String8& path, bool createIfNotPresent = true); + static sp<SharedZip> create(int fd, const String8& path); ZipFileRO* getZip(); @@ -257,6 +277,7 @@ private: private: SharedZip(const String8& path, time_t modWhen); + SharedZip(int fd, const String8& path); SharedZip(); // <-- not implemented String8 mPath; @@ -290,6 +311,8 @@ private: */ ZipFileRO* getZip(const String8& path); + const sp<SharedZip> getSharedZip(const String8& path); + Asset* getZipResourceTableAsset(const String8& path); Asset* setZipResourceTableAsset(const String8& path, Asset* asset); diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h index d2bc6ee45576..ef08897d997a 100644 --- a/libs/androidfw/include/androidfw/AssetManager2.h +++ b/libs/androidfw/include/androidfw/AssetManager2.h @@ -69,10 +69,11 @@ struct ResolvedBag { Entry entries[0]; }; +struct FindEntryResult; + // AssetManager2 is the main entry point for accessing assets and resources. -// AssetManager2 provides caching of resources retrieved via the underlying -// ApkAssets. -class AssetManager2 : public ::AAssetManager { +// AssetManager2 provides caching of resources retrieved via the underlying ApkAssets. +class AssetManager2 { public: struct ResourceName { const char* package = nullptr; @@ -97,24 +98,29 @@ class AssetManager2 : public ::AAssetManager { // new resource IDs. bool SetApkAssets(const std::vector<const ApkAssets*>& apk_assets, bool invalidate_caches = true); - inline const std::vector<const ApkAssets*> GetApkAssets() const { return apk_assets_; } + inline const std::vector<const ApkAssets*> GetApkAssets() const { + return apk_assets_; + } // Returns the string pool for the given asset cookie. - // Use the string pool returned here with a valid Res_value object of - // type Res_value::TYPE_STRING. + // Use the string pool returned here with a valid Res_value object of type Res_value::TYPE_STRING. const ResStringPool* GetStringPoolForCookie(ApkAssetsCookie cookie) const; // Returns the DynamicRefTable for the given package ID. + // This may be nullptr if the APK represented by `cookie` has no resource table. const DynamicRefTable* GetDynamicRefTableForPackage(uint32_t package_id) const; // Returns the DynamicRefTable for the ApkAssets represented by the cookie. + // This may be nullptr if the APK represented by `cookie` has no resource table. const DynamicRefTable* GetDynamicRefTableForCookie(ApkAssetsCookie cookie) const; // Sets/resets the configuration for this AssetManager. This will cause all // caches that are related to the configuration change to be invalidated. void SetConfiguration(const ResTable_config& configuration); - inline const ResTable_config& GetConfiguration() const { return configuration_; } + inline const ResTable_config& GetConfiguration() const { + return configuration_; + } // Returns all configurations for which there are resources defined. This includes resource // configurations in all the ApkAssets set for this AssetManager. @@ -123,7 +129,7 @@ class AssetManager2 : public ::AAssetManager { // If `exclude_mipmap` is set to true, resource configurations defined for resource type 'mipmap' // will be excluded from the list. std::set<ResTable_config> GetResourceConfigurations(bool exclude_system = false, - bool exclude_mipmap = false); + bool exclude_mipmap = false) const; // Returns all the locales for which there are resources defined. This includes resource // locales in all the ApkAssets set for this AssetManager. @@ -132,24 +138,24 @@ class AssetManager2 : public ::AAssetManager { // If `merge_equivalent_languages` is set to true, resource locales will be canonicalized // and de-duped in the resulting list. std::set<std::string> GetResourceLocales(bool exclude_system = false, - bool merge_equivalent_languages = false); + bool merge_equivalent_languages = false) const; // Searches the set of APKs loaded by this AssetManager and opens the first one found located // in the assets/ directory. // `mode` controls how the file is opened. // // NOTE: The loaded APKs are searched in reverse order. - std::unique_ptr<Asset> Open(const std::string& filename, Asset::AccessMode mode); + std::unique_ptr<Asset> Open(const std::string& filename, Asset::AccessMode mode) const; // Opens a file within the assets/ directory of the APK specified by `cookie`. // `mode` controls how the file is opened. std::unique_ptr<Asset> Open(const std::string& filename, ApkAssetsCookie cookie, - Asset::AccessMode mode); + Asset::AccessMode mode) const; // Opens the directory specified by `dirname`. The result is an AssetDir that is the combination // of all directories matching `dirname` under the assets/ directory of every ApkAssets loaded. // The entries are sorted by their ASCII name. - std::unique_ptr<AssetDir> OpenDir(const std::string& dirname); + std::unique_ptr<AssetDir> OpenDir(const std::string& dirname) const; // Searches the set of APKs loaded by this AssetManager and opens the first one found. // `mode` controls how the file is opened. @@ -157,24 +163,24 @@ class AssetManager2 : public ::AAssetManager { // // NOTE: The loaded APKs are searched in reverse order. std::unique_ptr<Asset> OpenNonAsset(const std::string& filename, Asset::AccessMode mode, - ApkAssetsCookie* out_cookie = nullptr); + ApkAssetsCookie* out_cookie = nullptr) const; // Opens a file in the APK specified by `cookie`. `mode` controls how the file is opened. // This is typically used to open a specific AndroidManifest.xml, or a binary XML file // referenced by a resource lookup with GetResource(). std::unique_ptr<Asset> OpenNonAsset(const std::string& filename, ApkAssetsCookie cookie, - Asset::AccessMode mode); + Asset::AccessMode mode) const; // Populates the `out_name` parameter with resource name information. // Utf8 strings are preferred, and only if they are unavailable are // the Utf16 variants populated. // Returns false if the resource was not found or the name was missing/corrupt. - bool GetResourceName(uint32_t resid, ResourceName* out_name); + bool GetResourceName(uint32_t resid, ResourceName* out_name) const; // Populates `out_flags` with the bitmask of configuration axis that this resource varies with. // See ResTable_config for the list of configuration axis. // Returns false if the resource was not found. - bool GetResourceFlags(uint32_t resid, uint32_t* out_flags); + bool GetResourceFlags(uint32_t resid, uint32_t* out_flags) const; // Finds the resource ID assigned to `resource_name`. // `resource_name` must be of the form '[package:][type/]entry'. @@ -182,7 +188,7 @@ class AssetManager2 : public ::AAssetManager { // If no type is specified in `resource_name`, then `fallback_type` is used as the type. // Returns 0x0 if no resource by that name was found. uint32_t GetResourceId(const std::string& resource_name, const std::string& fallback_type = {}, - const std::string& fallback_package = {}); + const std::string& fallback_package = {}) const; // Retrieves the best matching resource with ID `resid`. The resource value is filled into // `out_value` and the configuration for the selected value is populated in `out_selected_config`. @@ -195,7 +201,7 @@ class AssetManager2 : public ::AAssetManager { // this function logs if the resource was a map/bag type before returning kInvalidCookie. ApkAssetsCookie GetResource(uint32_t resid, bool may_be_bag, uint16_t density_override, Res_value* out_value, ResTable_config* out_selected_config, - uint32_t* out_flags); + uint32_t* out_flags) const; // Resolves the resource reference in `in_out_value` if the data type is // Res_value::TYPE_REFERENCE. @@ -206,12 +212,12 @@ class AssetManager2 : public ::AAssetManager { // are OR'd together with `in_out_flags`. // `in_out_config` is populated with the configuration for which the resolved value was defined. // `out_last_reference` is populated with the last reference ID before resolving to an actual - // value. + // value. This is only initialized if the passed in `in_out_value` is a reference. // Returns the cookie of the APK the resolved resource was defined in, or kInvalidCookie if // it was not found. ApkAssetsCookie ResolveReference(ApkAssetsCookie cookie, Res_value* in_out_value, ResTable_config* in_out_selected_config, uint32_t* in_out_flags, - uint32_t* out_last_reference); + uint32_t* out_last_reference) const; // Retrieves the best matching bag/map resource with ID `resid`. // This method will resolve all parent references for this bag and merge keys with the child. @@ -228,26 +234,35 @@ class AssetManager2 : public ::AAssetManager { // Creates a new Theme from this AssetManager. std::unique_ptr<Theme> NewTheme(); + template <typename Func> + void ForEachPackage(Func func) const { + for (const PackageGroup& package_group : package_groups_) { + func(package_group.packages_.front().loaded_package_->GetPackageName(), + package_group.dynamic_ref_table.mAssignedPackageId); + } + } + void DumpToLog() const; private: DISALLOW_COPY_AND_ASSIGN(AssetManager2); - // Finds the best entry for `resid` amongst all the ApkAssets. The entry can be a simple - // Res_value, or a complex map/bag type. + // Finds the best entry for `resid` from the set of ApkAssets. The entry can be a simple + // Res_value, or a complex map/bag type. If successful, it is available in `out_entry`. + // Returns kInvalidCookie on failure. Otherwise, the return value is the cookie associated with + // the ApkAssets in which the entry was found. // // `density_override` overrides the density of the current configuration when doing a search. // // When `stop_at_first_match` is true, the first match found is selected and the search // terminates. This is useful for methods that just look up the name of a resource and don't - // care about the value. In this case, the value of `out_flags` is incomplete and should not - // be used. + // care about the value. In this case, the value of `FindEntryResult::type_flags` is incomplete + // and should not be used. // - // `out_flags` stores the resulting bitmask of configuration axis with which the resource - // value varies. + // NOTE: FindEntry takes care of ensuring that structs within FindEntryResult have been properly + // bounds-checked. Callers of FindEntry are free to trust the data if this method succeeds. ApkAssetsCookie FindEntry(uint32_t resid, uint16_t density_override, bool stop_at_first_match, - LoadedArscEntry* out_entry, ResTable_config* out_selected_config, - uint32_t* out_flags); + FindEntryResult* out_entry) const; // Assigns package IDs to all shared library ApkAssets. // Should be called whenever the ApkAssets are changed. @@ -257,13 +272,43 @@ class AssetManager2 : public ::AAssetManager { // bitmask `diff`. void InvalidateCaches(uint32_t diff); + // Triggers the re-construction of lists of types that match the set configuration. + // This should always be called when mutating the AssetManager's configuration or ApkAssets set. + void RebuildFilterList(); + // The ordered list of ApkAssets to search. These are not owned by the AssetManager, and must // have a longer lifetime. std::vector<const ApkAssets*> apk_assets_; + // A collection of configurations and their associated ResTable_type that match the current + // AssetManager configuration. + struct FilteredConfigGroup { + std::vector<ResTable_config> configurations; + std::vector<const ResTable_type*> types; + }; + + // Represents an single package. + struct ConfiguredPackage { + // A pointer to the immutable, loaded package info. + const LoadedPackage* loaded_package_; + + // A mutable AssetManager-specific list of configurations that match the AssetManager's + // current configuration. This is used as an optimization to avoid checking every single + // candidate configuration when looking up resources. + ByteBucketArray<FilteredConfigGroup> filtered_configs_; + }; + + // Represents a logical package, which can be made up of many individual packages. Each package + // in a PackageGroup shares the same package name and package ID. struct PackageGroup { - std::vector<const LoadedPackage*> packages_; + // The set of packages that make-up this group. + std::vector<ConfiguredPackage> packages_; + + // The cookies associated with each package in the group. They share the same order as + // packages_. std::vector<ApkAssetsCookie> cookies_; + + // A library reference table that contains build-package ID to runtime-package ID mappings. DynamicRefTable dynamic_ref_table; }; @@ -290,6 +335,8 @@ class Theme { friend class AssetManager2; public: + ~Theme(); + // Applies the style identified by `resid` to this theme. This can be called // multiple times with different styles. By default, any theme attributes that // are already defined before this call are not overridden. If `force` is set @@ -304,69 +351,58 @@ class Theme { void Clear(); - inline const AssetManager2* GetAssetManager() const { return asset_manager_; } + inline const AssetManager2* GetAssetManager() const { + return asset_manager_; + } - inline AssetManager2* GetAssetManager() { return asset_manager_; } + inline AssetManager2* GetAssetManager() { + return asset_manager_; + } // Returns a bit mask of configuration changes that will impact this // theme (and thus require completely reloading it). - inline uint32_t GetChangingConfigurations() const { return type_spec_flags_; } - - // Retrieve a value in the theme. If the theme defines this value, - // returns an asset cookie indicating which ApkAssets it came from - // and populates `out_value` with the value. If `out_flags` is non-null, - // populates it with a bitmask of the configuration axis the resource - // varies with. + inline uint32_t GetChangingConfigurations() const { + return type_spec_flags_; + } + + // Retrieve a value in the theme. If the theme defines this value, returns an asset cookie + // indicating which ApkAssets it came from and populates `out_value` with the value. + // `out_flags` is populated with a bitmask of the configuration axis with which the resource + // varies. // // If the attribute is not found, returns kInvalidCookie. // - // NOTE: This function does not do reference traversal. If you want - // to follow references to other resources to get the "real" value to - // use, you need to call ResolveReference() after this function. - ApkAssetsCookie GetAttribute(uint32_t resid, Res_value* out_value, - uint32_t* out_flags = nullptr) const; + // NOTE: This function does not do reference traversal. If you want to follow references to other + // resources to get the "real" value to use, you need to call ResolveReference() after this + // function. + ApkAssetsCookie GetAttribute(uint32_t resid, Res_value* out_value, uint32_t* out_flags) const; // This is like AssetManager2::ResolveReference(), but also takes // care of resolving attribute references to the theme. ApkAssetsCookie ResolveAttributeReference(ApkAssetsCookie cookie, Res_value* in_out_value, ResTable_config* in_out_selected_config = nullptr, uint32_t* in_out_type_spec_flags = nullptr, - uint32_t* out_last_ref = nullptr); + uint32_t* out_last_ref = nullptr) const; private: DISALLOW_COPY_AND_ASSIGN(Theme); // Called by AssetManager2. - explicit inline Theme(AssetManager2* asset_manager) : asset_manager_(asset_manager) {} - - struct Entry { - ApkAssetsCookie cookie; - uint32_t type_spec_flags; - Res_value value; - }; - - struct Type { - // Use uint32_t for fewer cycles when loading from memory. - uint32_t entry_count; - uint32_t entry_capacity; - Entry entries[0]; - }; - - static constexpr const size_t kPackageCount = std::numeric_limits<uint8_t>::max() + 1; - static constexpr const size_t kTypeCount = std::numeric_limits<uint8_t>::max() + 1; - - struct Package { - // Each element of Type will be a dynamically sized object - // allocated to have the entries stored contiguously with the Type. - std::array<util::unique_cptr<Type>, kTypeCount> types; - }; + explicit Theme(AssetManager2* asset_manager); AssetManager2* asset_manager_; uint32_t type_spec_flags_ = 0u; + + // Defined in the cpp. + struct Package; + + constexpr static size_t kPackageCount = std::numeric_limits<uint8_t>::max() + 1; std::array<std::unique_ptr<Package>, kPackageCount> packages_; }; -inline const ResolvedBag::Entry* begin(const ResolvedBag* bag) { return bag->entries; } +inline const ResolvedBag::Entry* begin(const ResolvedBag* bag) { + return bag->entries; +} inline const ResolvedBag::Entry* end(const ResolvedBag* bag) { return bag->entries + bag->entry_count; diff --git a/libs/androidfw/include/androidfw/AttributeFinder.h b/libs/androidfw/include/androidfw/AttributeFinder.h index f281921824e7..03fad4947dfe 100644 --- a/libs/androidfw/include/androidfw/AttributeFinder.h +++ b/libs/androidfw/include/androidfw/AttributeFinder.h @@ -58,6 +58,7 @@ class BackTrackingAttributeFinder { BackTrackingAttributeFinder(const Iterator& begin, const Iterator& end); Iterator Find(uint32_t attr); + inline Iterator end(); private: void JumpToClosestAttribute(uint32_t package_id); @@ -201,6 +202,11 @@ Iterator BackTrackingAttributeFinder<Derived, Iterator>::Find(uint32_t attr) { return end_; } +template <typename Derived, typename Iterator> +Iterator BackTrackingAttributeFinder<Derived, Iterator>::end() { + return end_; +} + } // namespace android #endif // ANDROIDFW_ATTRIBUTE_FINDER_H diff --git a/libs/androidfw/include/androidfw/AttributeResolution.h b/libs/androidfw/include/androidfw/AttributeResolution.h index 69b760414846..35ef98d8c704 100644 --- a/libs/androidfw/include/androidfw/AttributeResolution.h +++ b/libs/androidfw/include/androidfw/AttributeResolution.h @@ -17,7 +17,8 @@ #ifndef ANDROIDFW_ATTRIBUTERESOLUTION_H #define ANDROIDFW_ATTRIBUTERESOLUTION_H -#include <androidfw/ResourceTypes.h> +#include "androidfw/AssetManager2.h" +#include "androidfw/ResourceTypes.h" namespace android { @@ -42,19 +43,19 @@ enum { // `out_values` must NOT be nullptr. // `out_indices` may be nullptr. -bool ResolveAttrs(ResTable::Theme* theme, uint32_t def_style_attr, uint32_t def_style_res, +bool ResolveAttrs(Theme* theme, uint32_t def_style_attr, uint32_t def_style_resid, uint32_t* src_values, size_t src_values_length, uint32_t* attrs, size_t attrs_length, uint32_t* out_values, uint32_t* out_indices); // `out_values` must NOT be nullptr. // `out_indices` is NOT optional and must NOT be nullptr. -void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, - uint32_t def_style_res, const uint32_t* attrs, size_t attrs_length, +void ApplyStyle(Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, + uint32_t def_style_resid, const uint32_t* attrs, size_t attrs_length, uint32_t* out_values, uint32_t* out_indices); // `out_values` must NOT be nullptr. // `out_indices` may be nullptr. -bool RetrieveAttributes(const ResTable* res, ResXMLParser* xml_parser, uint32_t* attrs, +bool RetrieveAttributes(AssetManager2* assetmanager, ResXMLParser* xml_parser, uint32_t* attrs, size_t attrs_length, uint32_t* out_values, uint32_t* out_indices); } // namespace android diff --git a/libs/androidfw/include/androidfw/Idmap.h b/libs/androidfw/include/androidfw/Idmap.h new file mode 100644 index 000000000000..fd02e6f63b74 --- /dev/null +++ b/libs/androidfw/include/androidfw/Idmap.h @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef IDMAP_H_ +#define IDMAP_H_ + +#include <memory> +#include <string> +#include <unordered_map> + +#include "android-base/macros.h" + +#include "androidfw/StringPiece.h" + +namespace android { + +struct Idmap_header; +struct IdmapEntry_header; + +// Represents a loaded/parsed IDMAP for a Runtime Resource Overlay (RRO). +// An RRO and its target APK have different resource IDs assigned to their resources. Overlaying +// a resource is done by resource name. An IDMAP is a generated mapping between the resource IDs +// of the RRO and the target APK for each resource with the same name. +// A LoadedIdmap can be set alongside the overlay's LoadedArsc to allow the overlay ApkAssets to +// masquerade as the target ApkAssets resources. +class LoadedIdmap { + public: + // Loads an IDMAP from a chunk of memory. Returns nullptr if the IDMAP data was malformed. + static std::unique_ptr<const LoadedIdmap> Load(const StringPiece& idmap_data); + + // Performs a lookup of the expected entry ID for the given IDMAP entry header. + // Returns true if the mapping exists and fills `output_entry_id` with the result. + static bool Lookup(const IdmapEntry_header* header, uint16_t input_entry_id, + uint16_t* output_entry_id); + + // Returns the package ID for which this overlay should apply. + uint8_t TargetPackageId() const; + + // Returns the path to the RRO (Runtime Resource Overlay) APK for which this IDMAP was generated. + inline const std::string& OverlayApkPath() const { + return overlay_apk_path_; + } + + // Returns the mapping of target entry ID to overlay entry ID for the given target type. + const IdmapEntry_header* GetEntryMapForType(uint8_t type_id) const; + + protected: + // Exposed as protected so that tests can subclass and mock this class out. + LoadedIdmap() = default; + + const Idmap_header* header_ = nullptr; + std::string overlay_apk_path_; + std::unordered_map<uint8_t, const IdmapEntry_header*> type_map_; + + private: + DISALLOW_COPY_AND_ASSIGN(LoadedIdmap); + + explicit LoadedIdmap(const Idmap_header* header); +}; + +} // namespace android + +#endif // IDMAP_H_ diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h index f30b158084eb..35ae5fcd9e7b 100644 --- a/libs/androidfw/include/androidfw/LoadedArsc.h +++ b/libs/androidfw/include/androidfw/LoadedArsc.h @@ -25,6 +25,7 @@ #include "androidfw/ByteBucketArray.h" #include "androidfw/Chunk.h" +#include "androidfw/Idmap.h" #include "androidfw/ResourceTypes.h" #include "androidfw/Util.h" @@ -40,50 +41,94 @@ class DynamicPackageEntry { int package_id = 0; }; -struct LoadedArscEntry { - // A pointer to the resource table entry for this resource. - // If the size of the entry is > sizeof(ResTable_entry), it can be cast to - // a ResTable_map_entry and processed as a bag/map. - const ResTable_entry* entry = nullptr; +// TypeSpec is going to be immediately proceeded by +// an array of Type structs, all in the same block of memory. +struct TypeSpec { + // Pointer to the mmapped data where flags are kept. + // Flags denote whether the resource entry is public + // and under which configurations it varies. + const ResTable_typeSpec* type_spec; + + // Pointer to the mmapped data where the IDMAP mappings for this type + // exist. May be nullptr if no IDMAP exists. + const IdmapEntry_header* idmap_entries; + + // The number of types that follow this struct. + // There is a type for each configuration that entries are defined for. + size_t type_count; + + // Trick to easily access a variable number of Type structs + // proceeding this struct, and to ensure their alignment. + const ResTable_type* types[0]; + + inline uint32_t GetFlagsForEntryIndex(uint16_t entry_index) const { + if (entry_index >= dtohl(type_spec->entryCount)) { + return 0u; + } + + const uint32_t* flags = reinterpret_cast<const uint32_t*>(type_spec + 1); + return flags[entry_index]; + } +}; - // The dynamic package ID map for the package from which this resource came from. - const DynamicRefTable* dynamic_ref_table = nullptr; +// TypeSpecPtr points to a block of memory that holds a TypeSpec struct, followed by an array of +// ResTable_type pointers. +// TypeSpecPtr is a managed pointer that knows how to delete itself. +using TypeSpecPtr = util::unique_cptr<TypeSpec>; - // The string pool reference to the type's name. This uses a different string pool than - // the global string pool, but this is hidden from the caller. - StringPoolRef type_string_ref; +class LoadedPackage { + public: + static std::unique_ptr<const LoadedPackage> Load(const Chunk& chunk, + const LoadedIdmap* loaded_idmap, bool system, + bool load_as_shared_library); - // The string pool reference to the entry's name. This uses a different string pool than - // the global string pool, but this is hidden from the caller. - StringPoolRef entry_string_ref; -}; + ~LoadedPackage(); + + // Finds the entry with the specified type name and entry name. The names are in UTF-16 because + // the underlying ResStringPool API expects this. For now this is acceptable, but since + // the default policy in AAPT2 is to build UTF-8 string pools, this needs to change. + // Returns a partial resource ID, with the package ID left as 0x00. The caller is responsible + // for patching the correct package ID to the resource ID. + uint32_t FindEntryByName(const std::u16string& type_name, const std::u16string& entry_name) const; -struct TypeSpec; -class LoadedArsc; + static const ResTable_entry* GetEntry(const ResTable_type* type_chunk, uint16_t entry_index); -class LoadedPackage { - friend class LoadedArsc; + static uint32_t GetEntryOffset(const ResTable_type* type_chunk, uint16_t entry_index); - public: - bool FindEntry(uint8_t type_idx, uint16_t entry_idx, const ResTable_config& config, - LoadedArscEntry* out_entry, ResTable_config* out_selected_config, - uint32_t* out_flags) const; + static const ResTable_entry* GetEntryFromOffset(const ResTable_type* type_chunk, uint32_t offset); // Returns the string pool where type names are stored. - inline const ResStringPool* GetTypeStringPool() const { return &type_string_pool_; } + inline const ResStringPool* GetTypeStringPool() const { + return &type_string_pool_; + } // Returns the string pool where the names of resource entries are stored. - inline const ResStringPool* GetKeyStringPool() const { return &key_string_pool_; } + inline const ResStringPool* GetKeyStringPool() const { + return &key_string_pool_; + } - inline const std::string& GetPackageName() const { return package_name_; } + inline const std::string& GetPackageName() const { + return package_name_; + } - inline int GetPackageId() const { return package_id_; } + inline int GetPackageId() const { + return package_id_; + } // Returns true if this package is dynamic (shared library) and needs to have an ID assigned. - inline bool IsDynamic() const { return dynamic_; } + inline bool IsDynamic() const { + return dynamic_; + } // Returns true if this package originates from a system provided resource. - inline bool IsSystem() const { return system_; } + inline bool IsSystem() const { + return system_; + } + + // Returns true if this package is from an overlay ApkAssets. + inline bool IsOverlay() const { + return overlay_; + } // Returns the map of package name to package ID used in this LoadedPackage. At runtime, a // package could have been assigned a different package ID than what this LoadedPackage was @@ -101,19 +146,31 @@ class LoadedPackage { // before being inserted into the set. This may cause some equivalent locales to de-dupe. void CollectLocales(bool canonicalize, std::set<std::string>* out_locales) const; - // Finds the entry with the specified type name and entry name. The names are in UTF-16 because - // the underlying ResStringPool API expects this. For now this is acceptable, but since - // the default policy in AAPT2 is to build UTF-8 string pools, this needs to change. - // Returns a partial resource ID, with the package ID left as 0x00. The caller is responsible - // for patching the correct package ID to the resource ID. - uint32_t FindEntryByName(const std::u16string& type_name, const std::u16string& entry_name) const; + // type_idx is TT - 1 from 0xPPTTEEEE. + inline const TypeSpec* GetTypeSpecByTypeIndex(uint8_t type_index) const { + // If the type IDs are offset in this package, we need to take that into account when searching + // for a type. + return type_specs_[type_index - type_id_offset_].get(); + } + + template <typename Func> + void ForEachTypeSpec(Func f) const { + for (size_t i = 0; i < type_specs_.size(); i++) { + const TypeSpecPtr& ptr = type_specs_[i]; + if (ptr != nullptr) { + uint8_t type_id = ptr->type_spec->id; + if (ptr->idmap_entries != nullptr) { + type_id = ptr->idmap_entries->target_type_id; + } + f(ptr.get(), type_id - 1); + } + } + } private: DISALLOW_COPY_AND_ASSIGN(LoadedPackage); - static std::unique_ptr<LoadedPackage> Load(const Chunk& chunk); - - LoadedPackage() = default; + LoadedPackage(); ResStringPool type_string_pool_; ResStringPool key_string_pool_; @@ -122,8 +179,9 @@ class LoadedPackage { int type_id_offset_ = 0; bool dynamic_ = false; bool system_ = false; + bool overlay_ = false; - ByteBucketArray<util::unique_cptr<TypeSpec>> type_specs_; + ByteBucketArray<TypeSpecPtr> type_specs_; std::vector<DynamicPackageEntry> dynamic_package_map_; }; @@ -137,38 +195,39 @@ class LoadedArsc { // If `load_as_shared_library` is set to true, the application package (0x7f) is treated // as a shared library (0x00). When loaded into an AssetManager, the package will be assigned an // ID. - static std::unique_ptr<const LoadedArsc> Load(const void* data, size_t len, bool system = false, + static std::unique_ptr<const LoadedArsc> Load(const StringPiece& data, + const LoadedIdmap* loaded_idmap = nullptr, + bool system = false, bool load_as_shared_library = false); - ~LoadedArsc(); + // Create an empty LoadedArsc. This is used when an APK has no resources.arsc. + static std::unique_ptr<const LoadedArsc> CreateEmpty(); // Returns the string pool where all string resource values // (Res_value::dataType == Res_value::TYPE_STRING) are indexed. - inline const ResStringPool* GetStringPool() const { return &global_string_pool_; } - - // Finds the resource with ID `resid` with the best value for configuration `config`. - // The parameter `out_entry` will be filled with the resulting resource entry. - // The resource entry can be a simple entry (ResTable_entry) or a complex bag - // (ResTable_entry_map). - bool FindEntry(uint32_t resid, const ResTable_config& config, LoadedArscEntry* out_entry, - ResTable_config* selected_config, uint32_t* out_flags) const; + inline const ResStringPool* GetStringPool() const { + return &global_string_pool_; + } - // Gets a pointer to the name of the package in `resid`, or nullptr if the package doesn't exist. - const LoadedPackage* GetPackageForId(uint32_t resid) const; - - // Returns true if this is a system provided resource. - inline bool IsSystem() const { return system_; } + // Gets a pointer to the package with the specified package ID, or nullptr if no such package + // exists. + const LoadedPackage* GetPackageById(uint8_t package_id) const; // Returns a vector of LoadedPackage pointers, representing the packages in this LoadedArsc. inline const std::vector<std::unique_ptr<const LoadedPackage>>& GetPackages() const { return packages_; } + // Returns true if this is a system provided resource. + inline bool IsSystem() const { + return system_; + } + private: DISALLOW_COPY_AND_ASSIGN(LoadedArsc); LoadedArsc() = default; - bool LoadTable(const Chunk& chunk, bool load_as_shared_library); + bool LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap, bool load_as_shared_library); ResStringPool global_string_pool_; std::vector<std::unique_ptr<const LoadedPackage>> packages_; diff --git a/libs/androidfw/include/androidfw/MutexGuard.h b/libs/androidfw/include/androidfw/MutexGuard.h new file mode 100644 index 000000000000..64924f433245 --- /dev/null +++ b/libs/androidfw/include/androidfw/MutexGuard.h @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROIDFW_MUTEXGUARD_H +#define ANDROIDFW_MUTEXGUARD_H + +#include <mutex> +#include <type_traits> + +#include "android-base/macros.h" + +namespace android { + +template <typename T> +class ScopedLock; + +// Owns the guarded object and protects access to it via a mutex. +// The guarded object is inaccessible via this class. +// The mutex is locked and the object accessed via the ScopedLock<T> class. +// +// NOTE: The template parameter T should not be a raw pointer, since ownership +// is ambiguous and error-prone. Instead use an std::unique_ptr<>. +// +// Example use: +// +// Guarded<std::string> shared_string("hello"); +// { +// ScopedLock<std::string> locked_string(shared_string); +// *locked_string += " world"; +// } +// +template <typename T> +class Guarded { + static_assert(!std::is_pointer<T>::value, "T must not be a raw pointer"); + + public: + explicit Guarded() : guarded_() { + } + + template <typename U = T> + explicit Guarded(const T& guarded, + typename std::enable_if<std::is_copy_constructible<U>::value>::type = void()) + : guarded_(guarded) { + } + + template <typename U = T> + explicit Guarded(T&& guarded, + typename std::enable_if<std::is_move_constructible<U>::value>::type = void()) + : guarded_(std::move(guarded)) { + } + + private: + friend class ScopedLock<T>; + + DISALLOW_COPY_AND_ASSIGN(Guarded); + + std::mutex lock_; + T guarded_; +}; + +template <typename T> +class ScopedLock { + public: + explicit ScopedLock(Guarded<T>& guarded) : lock_(guarded.lock_), guarded_(guarded.guarded_) { + } + + T& operator*() { + return guarded_; + } + + T* operator->() { + return &guarded_; + } + + T* get() { + return &guarded_; + } + + private: + DISALLOW_COPY_AND_ASSIGN(ScopedLock); + + std::lock_guard<std::mutex> lock_; + T& guarded_; +}; + +} // namespace android + +#endif // ANDROIDFW_MUTEXGUARD_H diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h index 66c66c251d9b..a1f15f0c96cb 100644 --- a/libs/androidfw/include/androidfw/ResourceTypes.h +++ b/libs/androidfw/include/androidfw/ResourceTypes.h @@ -38,6 +38,9 @@ namespace android { +constexpr const static uint32_t kIdmapMagic = 0x504D4449u; +constexpr const static uint32_t kIdmapCurrentVersion = 0x00000001u; + /** * In C++11, char16_t is defined as *at least* 16 bits. We do a lot of * casting on raw data and expect char16_t to be exactly 16 bits. @@ -543,15 +546,15 @@ private: */ class StringPoolRef { public: - StringPoolRef(); - StringPoolRef(const ResStringPool* pool, uint32_t index); + StringPoolRef() = default; + StringPoolRef(const ResStringPool* pool, uint32_t index); - const char* string8(size_t* outLen) const; - const char16_t* string16(size_t* outLen) const; + const char* string8(size_t* outLen) const; + const char16_t* string16(size_t* outLen) const; private: - const ResStringPool* mPool; - uint32_t mIndex; + const ResStringPool* mPool = nullptr; + uint32_t mIndex = 0u; }; /** ******************************************************************** @@ -891,9 +894,10 @@ struct ResTable_package // - a 8 char variant code prefixed by a 'v' // // each separated by a single char separator, which sums up to a total of 24 -// chars, (25 include the string terminator) rounded up to 28 to be 4 byte -// aligned. -#define RESTABLE_MAX_LOCALE_LEN 28 +// chars, (25 include the string terminator). Numbering system specificator, +// if present, can add up to 14 bytes (-u-nu-xxxxxxxx), giving 39 bytes, +// or 40 bytes to make it 4 bytes aligned. +#define RESTABLE_MAX_LOCALE_LEN 40 /** @@ -1179,6 +1183,10 @@ struct ResTable_config // tried but could not compute a script. bool localeScriptWasComputed; + // The value of BCP 47 Unicode extension for key 'nu' (numbering system). + // Varies in length from 3 to 8 chars. Zero-filled value. + char localeNumberingSystem[8]; + void copyFromDeviceNoSwap(const ResTable_config& o); void copyFromDtoH(const ResTable_config& o); @@ -1256,9 +1264,9 @@ struct ResTable_config // variants, it will be a modified bcp47 tag: b+en+Latn+US. void appendDirLocale(String8& str) const; - // Sets the values of language, region, script and variant to the - // well formed BCP-47 locale contained in |in|. The input locale is - // assumed to be valid and no validation is performed. + // Sets the values of language, region, script, variant and numbering + // system to the well formed BCP 47 locale contained in |in|. + // The input locale is assumed to be valid and no validation is performed. void setBcp47Locale(const char* in); inline void clearLocale() { @@ -1266,6 +1274,7 @@ struct ResTable_config localeScriptWasComputed = false; memset(localeScript, 0, sizeof(localeScript)); memset(localeVariant, 0, sizeof(localeVariant)); + memset(localeNumberingSystem, 0, sizeof(localeNumberingSystem)); } inline void computeScript() { @@ -1295,6 +1304,9 @@ struct ResTable_config // and 0 if they're equally specific. int isLocaleMoreSpecificThan(const ResTable_config &o) const; + // Returns an integer representng the imporance score of the configuration locale. + int getImportanceScoreOfLocale() const; + // Return true if 'this' is a better locale match than 'o' for the // 'requested' configuration. Similar to isBetterThan(), this assumes that // match() has already been used to remove any configurations that don't @@ -1331,9 +1343,13 @@ struct ResTable_typeSpec // Number of uint32_t entry configuration masks that follow. uint32_t entryCount; - enum { + enum : uint32_t { // Additional flag indicating an entry is public. - SPEC_PUBLIC = 0x40000000 + SPEC_PUBLIC = 0x40000000u, + + // Additional flag indicating an entry is overlayable at runtime. + // Added in Android-P. + SPEC_OVERLAYABLE = 0x80000000u, }; }; @@ -1583,6 +1599,30 @@ struct ResTable_lib_entry uint16_t packageName[128]; }; +struct alignas(uint32_t) Idmap_header { + // Always 0x504D4449 ('IDMP') + uint32_t magic; + + uint32_t version; + + uint32_t target_crc32; + uint32_t overlay_crc32; + + uint8_t target_path[256]; + uint8_t overlay_path[256]; + + uint16_t target_package_id; + uint16_t type_count; +} __attribute__((packed)); + +struct alignas(uint32_t) IdmapEntry_header { + uint16_t target_type_id; + uint16_t overlay_type_id; + uint16_t entry_count; + uint16_t entry_id_offset; + uint32_t entries[0]; +} __attribute__((packed)); + class AssetManager2; /** diff --git a/libs/androidfw/include/androidfw/ResourceUtils.h b/libs/androidfw/include/androidfw/ResourceUtils.h index 6bf7c2438797..c2eae855bb7b 100644 --- a/libs/androidfw/include/androidfw/ResourceUtils.h +++ b/libs/androidfw/include/androidfw/ResourceUtils.h @@ -40,7 +40,9 @@ inline uint8_t get_type_id(uint32_t resid) { return static_cast<uint8_t>((resid >> 16) & 0x000000ffu); } -inline uint16_t get_entry_id(uint32_t resid) { return static_cast<uint16_t>(resid & 0x0000ffffu); } +inline uint16_t get_entry_id(uint32_t resid) { + return static_cast<uint16_t>(resid & 0x0000ffffu); +} inline bool is_internal_resid(uint32_t resid) { return (resid & 0xffff0000u) != 0 && (resid & 0x00ff0000u) == 0; diff --git a/libs/androidfw/include/androidfw/ZipFileRO.h b/libs/androidfw/include/androidfw/ZipFileRO.h index 768034287afa..03154d04def1 100644 --- a/libs/androidfw/include/androidfw/ZipFileRO.h +++ b/libs/androidfw/include/androidfw/ZipFileRO.h @@ -80,6 +80,12 @@ public: static ZipFileRO* open(const char* zipFileName); /* + * Open an archive from an already open file descriptor. + */ + static ZipFileRO* openFd(int fd, const char* debugFileName, + bool assume_ownership = true); + + /* * Find an entry, by name. Returns the entry identifier, or NULL if * not found. */ diff --git a/libs/androidfw/tests/Android.mk b/libs/androidfw/tests/Android.mk deleted file mode 100644 index 921fd147aa80..000000000000 --- a/libs/androidfw/tests/Android.mk +++ /dev/null @@ -1,126 +0,0 @@ -# -# Copyright (C) 2014 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -# ========================================================== -# Setup some common variables for the different build -# targets here. -# ========================================================== -LOCAL_PATH:= $(call my-dir) - -testFiles := \ - ApkAssets_test.cpp \ - AppAsLib_test.cpp \ - Asset_test.cpp \ - AssetManager2_test.cpp \ - AttributeFinder_test.cpp \ - AttributeResolution_test.cpp \ - ByteBucketArray_test.cpp \ - Config_test.cpp \ - ConfigLocale_test.cpp \ - Idmap_test.cpp \ - LoadedArsc_test.cpp \ - ResourceUtils_test.cpp \ - ResTable_test.cpp \ - Split_test.cpp \ - StringPiece_test.cpp \ - TestHelpers.cpp \ - TestMain.cpp \ - Theme_test.cpp \ - TypeWrappers_test.cpp \ - ZipUtils_test.cpp - -benchmarkFiles := \ - AssetManager2_bench.cpp \ - BenchMain.cpp \ - BenchmarkHelpers.cpp \ - SparseEntry_bench.cpp \ - TestHelpers.cpp \ - Theme_bench.cpp - -androidfw_test_cflags := \ - -Wall \ - -Werror \ - -Wunused \ - -Wunreachable-code \ - -Wno-missing-field-initializers - -# gtest is broken. -androidfw_test_cflags += -Wno-unnamed-type-template-args - -# ========================================================== -# Build the host tests: libandroidfw_tests -# ========================================================== -include $(CLEAR_VARS) - -LOCAL_MODULE := libandroidfw_tests -LOCAL_CFLAGS := $(androidfw_test_cflags) -LOCAL_SRC_FILES := $(testFiles) -LOCAL_STATIC_LIBRARIES := \ - libandroidfw \ - libbase \ - libutils \ - libcutils \ - liblog \ - libz \ - libziparchive -LOCAL_PICKUP_FILES := $(LOCAL_PATH)/data - -include $(BUILD_HOST_NATIVE_TEST) - -# ========================================================== -# Build the device tests: libandroidfw_tests -# ========================================================== -ifneq ($(SDK_ONLY),true) -include $(CLEAR_VARS) - -LOCAL_MODULE := libandroidfw_tests -LOCAL_CFLAGS := $(androidfw_test_cflags) -LOCAL_SRC_FILES := $(testFiles) \ - BackupData_test.cpp \ - ObbFile_test.cpp \ - -LOCAL_SHARED_LIBRARIES := \ - libandroidfw \ - libbase \ - libcutils \ - libutils \ - libui \ - libziparchive -LOCAL_PICKUP_FILES := $(LOCAL_PATH)/data - -include $(BUILD_NATIVE_TEST) - -# ========================================================== -# Build the device benchmarks: libandroidfw_benchmarks -# ========================================================== -include $(CLEAR_VARS) - -LOCAL_MODULE := libandroidfw_benchmarks -LOCAL_CFLAGS := $(androidfw_test_cflags) -LOCAL_SRC_FILES := $(benchmarkFiles) -LOCAL_STATIC_LIBRARIES := \ - libgoogle-benchmark -LOCAL_SHARED_LIBRARIES := \ - libandroidfw \ - libbase \ - libcutils \ - libutils \ - libziparchive -LOCAL_PICKUP_FILES := $(LOCAL_PATH)/data - -include $(BUILD_NATIVE_TEST) -endif # Not SDK_ONLY - diff --git a/libs/androidfw/tests/ApkAssets_test.cpp b/libs/androidfw/tests/ApkAssets_test.cpp index c85b0b98ca5e..e2b9f0040989 100644 --- a/libs/androidfw/tests/ApkAssets_test.cpp +++ b/libs/androidfw/tests/ApkAssets_test.cpp @@ -17,70 +17,122 @@ #include "androidfw/ApkAssets.h" #include "android-base/file.h" +#include "android-base/test_utils.h" #include "android-base/unique_fd.h" +#include "androidfw/Util.h" #include "TestHelpers.h" #include "data/basic/R.h" -using com::android::basic::R; +using ::android::base::unique_fd; +using ::com::android::basic::R; +using ::testing::Eq; +using ::testing::Ge; +using ::testing::NotNull; +using ::testing::SizeIs; +using ::testing::StrEq; namespace android { TEST(ApkAssetsTest, LoadApk) { std::unique_ptr<const ApkAssets> loaded_apk = ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk"); - ASSERT_NE(nullptr, loaded_apk); - EXPECT_NE(nullptr, loaded_apk->GetLoadedArsc()); + ASSERT_THAT(loaded_apk, NotNull()); - std::unique_ptr<Asset> asset = loaded_apk->Open("res/layout/main.xml"); - ASSERT_NE(nullptr, asset); + const LoadedArsc* loaded_arsc = loaded_apk->GetLoadedArsc(); + ASSERT_THAT(loaded_arsc, NotNull()); + ASSERT_THAT(loaded_arsc->GetPackageById(0x7fu), NotNull()); + ASSERT_THAT(loaded_apk->Open("res/layout/main.xml"), NotNull()); +} + +TEST(ApkAssetsTest, LoadApkFromFd) { + const std::string path = GetTestDataPath() + "/basic/basic.apk"; + unique_fd fd(::open(path.c_str(), O_RDONLY | O_BINARY)); + ASSERT_THAT(fd.get(), Ge(0)); + + std::unique_ptr<const ApkAssets> loaded_apk = + ApkAssets::LoadFromFd(std::move(fd), path, false /*system*/, false /*force_shared_lib*/); + ASSERT_THAT(loaded_apk, NotNull()); + + const LoadedArsc* loaded_arsc = loaded_apk->GetLoadedArsc(); + ASSERT_THAT(loaded_arsc, NotNull()); + ASSERT_THAT(loaded_arsc->GetPackageById(0x7fu), NotNull()); + ASSERT_THAT(loaded_apk->Open("res/layout/main.xml"), NotNull()); } TEST(ApkAssetsTest, LoadApkAsSharedLibrary) { std::unique_ptr<const ApkAssets> loaded_apk = ApkAssets::Load(GetTestDataPath() + "/appaslib/appaslib.apk"); - ASSERT_NE(nullptr, loaded_apk); + ASSERT_THAT(loaded_apk, NotNull()); + const LoadedArsc* loaded_arsc = loaded_apk->GetLoadedArsc(); - ASSERT_NE(nullptr, loaded_arsc); - ASSERT_EQ(1u, loaded_arsc->GetPackages().size()); + ASSERT_THAT(loaded_arsc, NotNull()); + ASSERT_THAT(loaded_arsc->GetPackages(), SizeIs(1u)); EXPECT_FALSE(loaded_arsc->GetPackages()[0]->IsDynamic()); loaded_apk = ApkAssets::LoadAsSharedLibrary(GetTestDataPath() + "/appaslib/appaslib.apk"); - ASSERT_NE(nullptr, loaded_apk); + ASSERT_THAT(loaded_apk, NotNull()); loaded_arsc = loaded_apk->GetLoadedArsc(); - ASSERT_NE(nullptr, loaded_arsc); - ASSERT_EQ(1u, loaded_arsc->GetPackages().size()); + ASSERT_THAT(loaded_arsc, NotNull()); + ASSERT_THAT(loaded_arsc->GetPackages(), SizeIs(1u)); EXPECT_TRUE(loaded_arsc->GetPackages()[0]->IsDynamic()); } +TEST(ApkAssetsTest, LoadApkWithIdmap) { + std::string contents; + ResTable target_table; + const std::string target_path = GetTestDataPath() + "/basic/basic.apk"; + ASSERT_TRUE(ReadFileFromZipToString(target_path, "resources.arsc", &contents)); + ASSERT_THAT(target_table.add(contents.data(), contents.size(), 0, true /*copyData*/), + Eq(NO_ERROR)); + + ResTable overlay_table; + const std::string overlay_path = GetTestDataPath() + "/overlay/overlay.apk"; + ASSERT_TRUE(ReadFileFromZipToString(overlay_path, "resources.arsc", &contents)); + ASSERT_THAT(overlay_table.add(contents.data(), contents.size(), 0, true /*copyData*/), + Eq(NO_ERROR)); + + util::unique_cptr<void> idmap_data; + void* temp_data; + size_t idmap_len; + + ASSERT_THAT(target_table.createIdmap(overlay_table, 0u, 0u, target_path.c_str(), + overlay_path.c_str(), &temp_data, &idmap_len), + Eq(NO_ERROR)); + idmap_data.reset(temp_data); + + TemporaryFile tf; + ASSERT_TRUE(base::WriteFully(tf.fd, idmap_data.get(), idmap_len)); + close(tf.fd); + + // Open something so that the destructor of TemporaryFile closes a valid fd. + tf.fd = open("/dev/null", O_WRONLY); + + ASSERT_THAT(ApkAssets::LoadOverlay(tf.path), NotNull()); +} + TEST(ApkAssetsTest, CreateAndDestroyAssetKeepsApkAssetsOpen) { std::unique_ptr<const ApkAssets> loaded_apk = ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk"); - ASSERT_NE(nullptr, loaded_apk); + ASSERT_THAT(loaded_apk, NotNull()); - { - std::unique_ptr<Asset> assets = loaded_apk->Open("res/layout/main.xml", Asset::ACCESS_BUFFER); - ASSERT_NE(nullptr, assets); - } + { ASSERT_THAT(loaded_apk->Open("res/layout/main.xml", Asset::ACCESS_BUFFER), NotNull()); } - { - std::unique_ptr<Asset> assets = loaded_apk->Open("res/layout/main.xml", Asset::ACCESS_BUFFER); - ASSERT_NE(nullptr, assets); - } + { ASSERT_THAT(loaded_apk->Open("res/layout/main.xml", Asset::ACCESS_BUFFER), NotNull()); } } TEST(ApkAssetsTest, OpenUncompressedAssetFd) { std::unique_ptr<const ApkAssets> loaded_apk = ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk"); - ASSERT_NE(nullptr, loaded_apk); + ASSERT_THAT(loaded_apk, NotNull()); auto asset = loaded_apk->Open("assets/uncompressed.txt", Asset::ACCESS_UNKNOWN); - ASSERT_NE(nullptr, asset); + ASSERT_THAT(asset, NotNull()); off64_t start, length; - base::unique_fd fd(asset->openFileDescriptor(&start, &length)); - EXPECT_GE(fd.get(), 0); + unique_fd fd(asset->openFileDescriptor(&start, &length)); + ASSERT_THAT(fd.get(), Ge(0)); lseek64(fd.get(), start, SEEK_SET); @@ -88,7 +140,7 @@ TEST(ApkAssetsTest, OpenUncompressedAssetFd) { buffer.resize(length); ASSERT_TRUE(base::ReadFully(fd.get(), &*buffer.begin(), length)); - EXPECT_EQ("This should be uncompressed.\n\n", buffer); + EXPECT_THAT(buffer, StrEq("This should be uncompressed.\n\n")); } } // namespace android diff --git a/libs/androidfw/tests/AssetManager2_bench.cpp b/libs/androidfw/tests/AssetManager2_bench.cpp index 67de741b1b66..437e14772964 100644 --- a/libs/androidfw/tests/AssetManager2_bench.cpp +++ b/libs/androidfw/tests/AssetManager2_bench.cpp @@ -23,7 +23,6 @@ #include "androidfw/ResourceTypes.h" #include "BenchmarkHelpers.h" -#include "TestHelpers.h" #include "data/basic/R.h" #include "data/libclient/R.h" #include "data/styles/R.h" @@ -82,48 +81,18 @@ static void BM_AssetManagerLoadFrameworkAssetsOld(benchmark::State& state) { } BENCHMARK(BM_AssetManagerLoadFrameworkAssetsOld); -static void GetResourceBenchmark(const std::vector<std::string>& paths, - const ResTable_config* config, uint32_t resid, - benchmark::State& state) { - std::vector<std::unique_ptr<const ApkAssets>> apk_assets; - std::vector<const ApkAssets*> apk_assets_ptrs; - for (const std::string& path : paths) { - std::unique_ptr<const ApkAssets> apk = ApkAssets::Load(path); - if (apk == nullptr) { - state.SkipWithError(base::StringPrintf("Failed to load assets %s", path.c_str()).c_str()); - return; - } - apk_assets_ptrs.push_back(apk.get()); - apk_assets.push_back(std::move(apk)); - } - - AssetManager2 assetmanager; - assetmanager.SetApkAssets(apk_assets_ptrs); - if (config != nullptr) { - assetmanager.SetConfiguration(*config); - } - - Res_value value; - ResTable_config selected_config; - uint32_t flags; - - while (state.KeepRunning()) { - assetmanager.GetResource(resid, false /* may_be_bag */, 0u /* density_override */, &value, - &selected_config, &flags); - } -} - -static void BM_AssetManagerGetResource(benchmark::State& state) { - GetResourceBenchmark({GetTestDataPath() + "/basic/basic.apk"}, nullptr /*config*/, - basic::R::integer::number1, state); +static void BM_AssetManagerGetResource(benchmark::State& state, uint32_t resid) { + GetResourceBenchmark({GetTestDataPath() + "/basic/basic.apk"}, nullptr /*config*/, resid, state); } -BENCHMARK(BM_AssetManagerGetResource); +BENCHMARK_CAPTURE(BM_AssetManagerGetResource, number1, basic::R::integer::number1); +BENCHMARK_CAPTURE(BM_AssetManagerGetResource, deep_ref, basic::R::integer::deep_ref); -static void BM_AssetManagerGetResourceOld(benchmark::State& state) { - GetResourceBenchmarkOld({GetTestDataPath() + "/basic/basic.apk"}, nullptr /*config*/, - basic::R::integer::number1, state); +static void BM_AssetManagerGetResourceOld(benchmark::State& state, uint32_t resid) { + GetResourceBenchmarkOld({GetTestDataPath() + "/basic/basic.apk"}, nullptr /*config*/, resid, + state); } -BENCHMARK(BM_AssetManagerGetResourceOld); +BENCHMARK_CAPTURE(BM_AssetManagerGetResourceOld, number1, basic::R::integer::number1); +BENCHMARK_CAPTURE(BM_AssetManagerGetResourceOld, deep_ref, basic::R::integer::deep_ref); static void BM_AssetManagerGetLibraryResource(benchmark::State& state) { GetResourceBenchmark( @@ -228,7 +197,7 @@ BENCHMARK(BM_AssetManagerGetResourceLocales); static void BM_AssetManagerGetResourceLocalesOld(benchmark::State& state) { AssetManager assets; if (!assets.addAssetPath(String8(kFrameworkPath), nullptr /*cookie*/, false /*appAsLib*/, - false /*isSystemAssets*/)) { + true /*isSystemAssets*/)) { state.SkipWithError("Failed to load assets"); return; } @@ -243,4 +212,44 @@ static void BM_AssetManagerGetResourceLocalesOld(benchmark::State& state) { } BENCHMARK(BM_AssetManagerGetResourceLocalesOld); +static void BM_AssetManagerSetConfigurationFramework(benchmark::State& state) { + std::unique_ptr<const ApkAssets> apk = ApkAssets::Load(kFrameworkPath); + if (apk == nullptr) { + state.SkipWithError("Failed to load assets"); + return; + } + + AssetManager2 assets; + assets.SetApkAssets({apk.get()}); + + ResTable_config config; + memset(&config, 0, sizeof(config)); + + while (state.KeepRunning()) { + config.sdkVersion = ~config.sdkVersion; + assets.SetConfiguration(config); + } +} +BENCHMARK(BM_AssetManagerSetConfigurationFramework); + +static void BM_AssetManagerSetConfigurationFrameworkOld(benchmark::State& state) { + AssetManager assets; + if (!assets.addAssetPath(String8(kFrameworkPath), nullptr /*cookie*/, false /*appAsLib*/, + true /*isSystemAssets*/)) { + state.SkipWithError("Failed to load assets"); + return; + } + + const ResTable& table = assets.getResources(true); + + ResTable_config config; + memset(&config, 0, sizeof(config)); + + while (state.KeepRunning()) { + config.sdkVersion = ~config.sdkVersion; + assets.setConfiguration(config); + } +} +BENCHMARK(BM_AssetManagerSetConfigurationFrameworkOld); + } // namespace android diff --git a/libs/androidfw/tests/AssetManager2_test.cpp b/libs/androidfw/tests/AssetManager2_test.cpp index fcae53b322b3..92462a6cfadf 100644 --- a/libs/androidfw/tests/AssetManager2_test.cpp +++ b/libs/androidfw/tests/AssetManager2_test.cpp @@ -317,7 +317,7 @@ TEST_F(AssetManager2Test, ResolveReferenceToResource) { EXPECT_EQ(Res_value::TYPE_REFERENCE, value.dataType); EXPECT_EQ(basic::R::integer::ref2, value.data); - uint32_t last_ref; + uint32_t last_ref = 0u; cookie = assetmanager.ResolveReference(cookie, &value, &selected_config, &flags, &last_ref); ASSERT_NE(kInvalidCookie, cookie); EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType); @@ -340,7 +340,7 @@ TEST_F(AssetManager2Test, ResolveReferenceToBag) { EXPECT_EQ(Res_value::TYPE_REFERENCE, value.dataType); EXPECT_EQ(basic::R::array::integerArray1, value.data); - uint32_t last_ref; + uint32_t last_ref = 0u; cookie = assetmanager.ResolveReference(cookie, &value, &selected_config, &flags, &last_ref); ASSERT_NE(kInvalidCookie, cookie); EXPECT_EQ(Res_value::TYPE_REFERENCE, value.dataType); @@ -348,6 +348,25 @@ TEST_F(AssetManager2Test, ResolveReferenceToBag) { EXPECT_EQ(basic::R::array::integerArray1, last_ref); } +TEST_F(AssetManager2Test, KeepLastReferenceIdUnmodifiedIfNoReferenceIsResolved) { + AssetManager2 assetmanager; + assetmanager.SetApkAssets({basic_assets_.get()}); + + ResTable_config selected_config; + memset(&selected_config, 0, sizeof(selected_config)); + + uint32_t flags = 0u; + + // Create some kind of Res_value that is NOT a reference. + Res_value value; + value.dataType = Res_value::TYPE_STRING; + value.data = 0; + + uint32_t last_ref = basic::R::string::test1; + EXPECT_EQ(1, assetmanager.ResolveReference(1, &value, &selected_config, &flags, &last_ref)); + EXPECT_EQ(basic::R::string::test1, last_ref); +} + static bool IsConfigurationPresent(const std::set<ResTable_config>& configurations, const ResTable_config& configuration) { return configurations.count(configuration) > 0; diff --git a/libs/androidfw/tests/AttributeResolution_bench.cpp b/libs/androidfw/tests/AttributeResolution_bench.cpp new file mode 100644 index 000000000000..fa300c50218a --- /dev/null +++ b/libs/androidfw/tests/AttributeResolution_bench.cpp @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "benchmark/benchmark.h" + +//#include "android-base/stringprintf.h" +#include "androidfw/ApkAssets.h" +#include "androidfw/AssetManager.h" +#include "androidfw/AssetManager2.h" +#include "androidfw/AttributeResolution.h" +#include "androidfw/ResourceTypes.h" + +#include "BenchmarkHelpers.h" +#include "data/basic/R.h" +#include "data/styles/R.h" + +namespace app = com::android::app; +namespace basic = com::android::basic; + +namespace android { + +constexpr const static char* kFrameworkPath = "/system/framework/framework-res.apk"; +constexpr const static uint32_t Theme_Material_Light = 0x01030237u; + +static void BM_ApplyStyle(benchmark::State& state) { + std::unique_ptr<const ApkAssets> styles_apk = + ApkAssets::Load(GetTestDataPath() + "/styles/styles.apk"); + if (styles_apk == nullptr) { + state.SkipWithError("failed to load assets"); + return; + } + + AssetManager2 assetmanager; + assetmanager.SetApkAssets({styles_apk.get()}); + + std::unique_ptr<Asset> asset = + assetmanager.OpenNonAsset("res/layout/layout.xml", Asset::ACCESS_BUFFER); + if (asset == nullptr) { + state.SkipWithError("failed to load layout"); + return; + } + + ResXMLTree xml_tree; + if (xml_tree.setTo(asset->getBuffer(true), asset->getLength(), false /*copyData*/) != NO_ERROR) { + state.SkipWithError("corrupt xml layout"); + return; + } + + // Skip to the first tag. + while (xml_tree.next() != ResXMLParser::START_TAG) { + } + + std::unique_ptr<Theme> theme = assetmanager.NewTheme(); + theme->ApplyStyle(app::R::style::StyleTwo); + + std::array<uint32_t, 6> attrs{{app::R::attr::attr_one, app::R::attr::attr_two, + app::R::attr::attr_three, app::R::attr::attr_four, + app::R::attr::attr_five, app::R::attr::attr_empty}}; + std::array<uint32_t, attrs.size() * STYLE_NUM_ENTRIES> values; + std::array<uint32_t, attrs.size() + 1> indices; + + while (state.KeepRunning()) { + ApplyStyle(theme.get(), &xml_tree, 0u /*def_style_attr*/, 0u /*def_style_res*/, attrs.data(), + attrs.size(), values.data(), indices.data()); + } +} +BENCHMARK(BM_ApplyStyle); + +static void BM_ApplyStyleFramework(benchmark::State& state) { + std::unique_ptr<const ApkAssets> framework_apk = ApkAssets::Load(kFrameworkPath); + if (framework_apk == nullptr) { + state.SkipWithError("failed to load framework assets"); + return; + } + + std::unique_ptr<const ApkAssets> basic_apk = + ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk"); + if (basic_apk == nullptr) { + state.SkipWithError("failed to load assets"); + return; + } + + AssetManager2 assetmanager; + assetmanager.SetApkAssets({framework_apk.get(), basic_apk.get()}); + + ResTable_config device_config; + memset(&device_config, 0, sizeof(device_config)); + device_config.language[0] = 'e'; + device_config.language[1] = 'n'; + device_config.country[0] = 'U'; + device_config.country[1] = 'S'; + device_config.orientation = ResTable_config::ORIENTATION_PORT; + device_config.smallestScreenWidthDp = 700; + device_config.screenWidthDp = 700; + device_config.screenHeightDp = 1024; + device_config.sdkVersion = 27; + + Res_value value; + ResTable_config config; + uint32_t flags = 0u; + ApkAssetsCookie cookie = + assetmanager.GetResource(basic::R::layout::layoutt, false /*may_be_bag*/, + 0u /*density_override*/, &value, &config, &flags); + if (cookie == kInvalidCookie) { + state.SkipWithError("failed to find R.layout.layout"); + return; + } + + size_t len = 0u; + const char* layout_path = + assetmanager.GetStringPoolForCookie(cookie)->string8At(value.data, &len); + if (layout_path == nullptr || len == 0u) { + state.SkipWithError("failed to lookup layout path"); + return; + } + + std::unique_ptr<Asset> asset = assetmanager.OpenNonAsset( + StringPiece(layout_path, len).to_string(), cookie, Asset::ACCESS_BUFFER); + if (asset == nullptr) { + state.SkipWithError("failed to load layout"); + return; + } + + ResXMLTree xml_tree; + if (xml_tree.setTo(asset->getBuffer(true), asset->getLength(), false /*copyData*/) != NO_ERROR) { + state.SkipWithError("corrupt xml layout"); + return; + } + + // Skip to the first tag. + while (xml_tree.next() != ResXMLParser::START_TAG) { + } + + std::unique_ptr<Theme> theme = assetmanager.NewTheme(); + theme->ApplyStyle(Theme_Material_Light); + + std::array<uint32_t, 92> attrs{ + {0x0101000e, 0x01010034, 0x01010095, 0x01010096, 0x01010097, 0x01010098, 0x01010099, + 0x0101009a, 0x0101009b, 0x010100ab, 0x010100af, 0x010100b0, 0x010100b1, 0x0101011f, + 0x01010120, 0x0101013f, 0x01010140, 0x0101014e, 0x0101014f, 0x01010150, 0x01010151, + 0x01010152, 0x01010153, 0x01010154, 0x01010155, 0x01010156, 0x01010157, 0x01010158, + 0x01010159, 0x0101015a, 0x0101015b, 0x0101015c, 0x0101015d, 0x0101015e, 0x0101015f, + 0x01010160, 0x01010161, 0x01010162, 0x01010163, 0x01010164, 0x01010165, 0x01010166, + 0x01010167, 0x01010168, 0x01010169, 0x0101016a, 0x0101016b, 0x0101016c, 0x0101016d, + 0x0101016e, 0x0101016f, 0x01010170, 0x01010171, 0x01010217, 0x01010218, 0x0101021d, + 0x01010220, 0x01010223, 0x01010224, 0x01010264, 0x01010265, 0x01010266, 0x010102c5, + 0x010102c6, 0x010102c7, 0x01010314, 0x01010315, 0x01010316, 0x0101035e, 0x0101035f, + 0x01010362, 0x01010374, 0x0101038c, 0x01010392, 0x01010393, 0x010103ac, 0x0101045d, + 0x010104b6, 0x010104b7, 0x010104d6, 0x010104d7, 0x010104dd, 0x010104de, 0x010104df, + 0x01010535, 0x01010536, 0x01010537, 0x01010538, 0x01010546, 0x01010567, 0x011100c9, + 0x011100ca}}; + + std::array<uint32_t, attrs.size() * STYLE_NUM_ENTRIES> values; + std::array<uint32_t, attrs.size() + 1> indices; + while (state.KeepRunning()) { + ApplyStyle(theme.get(), &xml_tree, 0x01010084u /*def_style_attr*/, 0u /*def_style_res*/, + attrs.data(), attrs.size(), values.data(), indices.data()); + } +} +BENCHMARK(BM_ApplyStyleFramework); + +} // namespace android diff --git a/libs/androidfw/tests/AttributeResolution_test.cpp b/libs/androidfw/tests/AttributeResolution_test.cpp index 2d73ce8f8ee3..cc3053798e7b 100644 --- a/libs/androidfw/tests/AttributeResolution_test.cpp +++ b/libs/androidfw/tests/AttributeResolution_test.cpp @@ -21,6 +21,7 @@ #include "android-base/file.h" #include "android-base/logging.h" #include "android-base/macros.h" +#include "androidfw/AssetManager2.h" #include "TestHelpers.h" #include "data/styles/R.h" @@ -32,15 +33,14 @@ namespace android { class AttributeResolutionTest : public ::testing::Test { public: virtual void SetUp() override { - std::string contents; - ASSERT_TRUE(ReadFileFromZipToString( - GetTestDataPath() + "/styles/styles.apk", "resources.arsc", &contents)); - ASSERT_EQ(NO_ERROR, table_.add(contents.data(), contents.size(), - 1 /*cookie*/, true /*copyData*/)); + styles_assets_ = ApkAssets::Load(GetTestDataPath() + "/styles/styles.apk"); + ASSERT_NE(nullptr, styles_assets_); + assetmanager_.SetApkAssets({styles_assets_.get()}); } protected: - ResTable table_; + std::unique_ptr<const ApkAssets> styles_assets_; + AssetManager2 assetmanager_; }; class AttributeResolutionXmlTest : public AttributeResolutionTest { @@ -48,13 +48,12 @@ class AttributeResolutionXmlTest : public AttributeResolutionTest { virtual void SetUp() override { AttributeResolutionTest::SetUp(); - std::string contents; - ASSERT_TRUE( - ReadFileFromZipToString(GetTestDataPath() + "/styles/styles.apk", - "res/layout/layout.xml", &contents)); + std::unique_ptr<Asset> asset = + assetmanager_.OpenNonAsset("res/layout/layout.xml", Asset::ACCESS_BUFFER); + ASSERT_NE(nullptr, asset); - ASSERT_EQ(NO_ERROR, xml_parser_.setTo(contents.data(), contents.size(), - true /*copyData*/)); + ASSERT_EQ(NO_ERROR, + xml_parser_.setTo(asset->getBuffer(true), asset->getLength(), true /*copyData*/)); // Skip to the first tag. while (xml_parser_.next() != ResXMLParser::START_TAG) { @@ -66,14 +65,14 @@ class AttributeResolutionXmlTest : public AttributeResolutionTest { }; TEST_F(AttributeResolutionTest, Theme) { - ResTable::Theme theme(table_); - ASSERT_EQ(NO_ERROR, theme.applyStyle(R::style::StyleTwo)); + std::unique_ptr<Theme> theme = assetmanager_.NewTheme(); + ASSERT_TRUE(theme->ApplyStyle(R::style::StyleTwo)); std::array<uint32_t, 5> attrs{{R::attr::attr_one, R::attr::attr_two, R::attr::attr_three, R::attr::attr_four, R::attr::attr_empty}}; std::array<uint32_t, attrs.size() * STYLE_NUM_ENTRIES> values; - ASSERT_TRUE(ResolveAttrs(&theme, 0 /*def_style_attr*/, 0 /*def_style_res*/, + ASSERT_TRUE(ResolveAttrs(theme.get(), 0u /*def_style_attr*/, 0u /*def_style_res*/, nullptr /*src_values*/, 0 /*src_values_length*/, attrs.data(), attrs.size(), values.data(), nullptr /*out_indices*/)); @@ -126,8 +125,8 @@ TEST_F(AttributeResolutionXmlTest, XmlParser) { R::attr::attr_four, R::attr::attr_empty}}; std::array<uint32_t, attrs.size() * STYLE_NUM_ENTRIES> values; - ASSERT_TRUE(RetrieveAttributes(&table_, &xml_parser_, attrs.data(), attrs.size(), values.data(), - nullptr /*out_indices*/)); + ASSERT_TRUE(RetrieveAttributes(&assetmanager_, &xml_parser_, attrs.data(), attrs.size(), + values.data(), nullptr /*out_indices*/)); uint32_t* values_cursor = values.data(); EXPECT_EQ(Res_value::TYPE_NULL, values_cursor[STYLE_TYPE]); @@ -171,15 +170,15 @@ TEST_F(AttributeResolutionXmlTest, XmlParser) { } TEST_F(AttributeResolutionXmlTest, ThemeAndXmlParser) { - ResTable::Theme theme(table_); - ASSERT_EQ(NO_ERROR, theme.applyStyle(R::style::StyleTwo)); + std::unique_ptr<Theme> theme = assetmanager_.NewTheme(); + ASSERT_TRUE(theme->ApplyStyle(R::style::StyleTwo)); std::array<uint32_t, 6> attrs{{R::attr::attr_one, R::attr::attr_two, R::attr::attr_three, R::attr::attr_four, R::attr::attr_five, R::attr::attr_empty}}; std::array<uint32_t, attrs.size() * STYLE_NUM_ENTRIES> values; std::array<uint32_t, attrs.size() + 1> indices; - ApplyStyle(&theme, &xml_parser_, 0 /*def_style_attr*/, 0 /*def_style_res*/, attrs.data(), + ApplyStyle(theme.get(), &xml_parser_, 0u /*def_style_attr*/, 0u /*def_style_res*/, attrs.data(), attrs.size(), values.data(), indices.data()); const uint32_t public_flag = ResTable_typeSpec::SPEC_PUBLIC; diff --git a/libs/androidfw/tests/BenchMain.cpp b/libs/androidfw/tests/BenchMain.cpp index 105c5f9551b7..58fc54a4a04c 100644 --- a/libs/androidfw/tests/BenchMain.cpp +++ b/libs/androidfw/tests/BenchMain.cpp @@ -18,7 +18,7 @@ #include "benchmark/benchmark.h" -#include "TestHelpers.h" +#include "BenchmarkHelpers.h" int main(int argc, char** argv) { ::benchmark::Initialize(&argc, argv); diff --git a/libs/androidfw/tests/BenchmarkHelpers.cpp b/libs/androidfw/tests/BenchmarkHelpers.cpp index 3619b7ee83ab..faddfe599af4 100644 --- a/libs/androidfw/tests/BenchmarkHelpers.cpp +++ b/libs/androidfw/tests/BenchmarkHelpers.cpp @@ -18,6 +18,7 @@ #include "android-base/stringprintf.h" #include "androidfw/AssetManager.h" +#include "androidfw/AssetManager2.h" namespace android { @@ -32,19 +33,53 @@ void GetResourceBenchmarkOld(const std::vector<std::string>& paths, const ResTab } } + // Make sure to force creation of the ResTable first, or else the configuration doesn't get set. + const ResTable& table = assetmanager.getResources(true); if (config != nullptr) { assetmanager.setConfiguration(*config); } - const ResTable& table = assetmanager.getResources(true); + Res_value value; + ResTable_config selected_config; + uint32_t flags; + uint32_t last_ref = 0u; + + while (state.KeepRunning()) { + ssize_t block = table.getResource(resid, &value, false /*may_be_bag*/, 0u /*density*/, &flags, + &selected_config); + table.resolveReference(&value, block, &last_ref, &flags, &selected_config); + } +} + +void GetResourceBenchmark(const std::vector<std::string>& paths, const ResTable_config* config, + uint32_t resid, benchmark::State& state) { + std::vector<std::unique_ptr<const ApkAssets>> apk_assets; + std::vector<const ApkAssets*> apk_assets_ptrs; + for (const std::string& path : paths) { + std::unique_ptr<const ApkAssets> apk = ApkAssets::Load(path); + if (apk == nullptr) { + state.SkipWithError(base::StringPrintf("Failed to load assets %s", path.c_str()).c_str()); + return; + } + apk_assets_ptrs.push_back(apk.get()); + apk_assets.push_back(std::move(apk)); + } + + AssetManager2 assetmanager; + assetmanager.SetApkAssets(apk_assets_ptrs); + if (config != nullptr) { + assetmanager.SetConfiguration(*config); + } Res_value value; ResTable_config selected_config; uint32_t flags; + uint32_t last_id = 0u; while (state.KeepRunning()) { - table.getResource(resid, &value, false /*may_be_bag*/, 0u /*density*/, &flags, - &selected_config); + ApkAssetsCookie cookie = assetmanager.GetResource( + resid, false /* may_be_bag */, 0u /* density_override */, &value, &selected_config, &flags); + assetmanager.ResolveReference(cookie, &value, &selected_config, &flags, &last_id); } } diff --git a/libs/androidfw/tests/BenchmarkHelpers.h b/libs/androidfw/tests/BenchmarkHelpers.h index fc366642ca36..eb0939d0e23b 100644 --- a/libs/androidfw/tests/BenchmarkHelpers.h +++ b/libs/androidfw/tests/BenchmarkHelpers.h @@ -14,21 +14,25 @@ * limitations under the License. */ -#ifndef TESTS_BENCHMARKHELPERS_H_ -#define TESTS_BENCHMARKHELPERS_H_ +#ifndef ANDROIDFW_TESTS_BENCHMARKHELPERS_H +#define ANDROIDFW_TESTS_BENCHMARKHELPERS_H #include <string> #include <vector> +#include "androidfw/ResourceTypes.h" #include "benchmark/benchmark.h" -#include "androidfw/ResourceTypes.h" +#include "CommonHelpers.h" namespace android { void GetResourceBenchmarkOld(const std::vector<std::string>& paths, const ResTable_config* config, - uint32_t resid, benchmark::State& state); + uint32_t resid, ::benchmark::State& state); + +void GetResourceBenchmark(const std::vector<std::string>& paths, const ResTable_config* config, + uint32_t resid, benchmark::State& state); } // namespace android -#endif /* TESTS_BENCHMARKHELPERS_H_ */ +#endif // ANDROIDFW_TESTS_BENCHMARKHELPERS_H diff --git a/libs/androidfw/tests/CommonHelpers.cpp b/libs/androidfw/tests/CommonHelpers.cpp new file mode 100644 index 000000000000..faa5350f9ecc --- /dev/null +++ b/libs/androidfw/tests/CommonHelpers.cpp @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "CommonHelpers.h" + +#include <iostream> + +#include "android-base/file.h" +#include "android-base/logging.h" +#include "android-base/strings.h" + +namespace android { + +static std::string sTestDataPath; + +void InitializeTest(int* argc, char** argv) { + // Set the default test data path to be the executable path directory + data. + SetTestDataPath(base::GetExecutableDirectory() + "/tests/data"); + + for (int i = 1; i < *argc; i++) { + const std::string arg = argv[i]; + if (base::StartsWith(arg, "--testdata=")) { + SetTestDataPath(arg.substr(strlen("--testdata="))); + for (int j = i; j != *argc; j++) { + argv[j] = argv[j + 1]; + } + --(*argc); + --i; + } else if (arg == "-h" || arg == "--help") { + std::cerr << "\nAdditional options specific to this test:\n" + " --testdata=[PATH]\n" + " Specify the location of test data used within the tests.\n"; + exit(1); + } + } +} + +void SetTestDataPath(const std::string& path) { + sTestDataPath = path; +} + +const std::string& GetTestDataPath() { + CHECK(!sTestDataPath.empty()) << "no test data path set."; + return sTestDataPath; +} + +std::string GetStringFromPool(const ResStringPool* pool, uint32_t idx) { + String8 str = pool->string8ObjectAt(idx); + return std::string(str.string(), str.length()); +} + +} // namespace android diff --git a/libs/androidfw/tests/CommonHelpers.h b/libs/androidfw/tests/CommonHelpers.h new file mode 100644 index 000000000000..c160fbba4c01 --- /dev/null +++ b/libs/androidfw/tests/CommonHelpers.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROIDFW_TEST_COMMON_HELPERS_H +#define ANDROIDFW_TEST_COMMON_HELPERS_H + +#include <ostream> +#include <string> + +#include "androidfw/ResourceTypes.h" +#include "utils/String16.h" +#include "utils/String8.h" + +namespace android { + +void InitializeTest(int* argc, char** argv); + +enum { MAY_NOT_BE_BAG = false }; + +void SetTestDataPath(const std::string& path); + +const std::string& GetTestDataPath(); + +std::string GetStringFromPool(const ResStringPool* pool, uint32_t idx); + +static inline bool operator==(const ResTable_config& a, const ResTable_config& b) { + return a.compare(b) == 0; +} + +static inline ::std::ostream& operator<<(::std::ostream& out, const String8& str) { + return out << str.string(); +} + +static inline ::std::ostream& operator<<(::std::ostream& out, const String16& str) { + return out << String8(str).string(); +} + +static inline ::std::ostream& operator<<(::std::ostream& out, const ResTable_config& c) { + return out << c.toString(); +} + +} // namespace android + +#endif // ANDROIDFW_TEST_COMMON_HELPERS_H diff --git a/libs/androidfw/tests/ConfigLocale_test.cpp b/libs/androidfw/tests/ConfigLocale_test.cpp index 86a627e1485d..ac08c52772d1 100644 --- a/libs/androidfw/tests/ConfigLocale_test.cpp +++ b/libs/androidfw/tests/ConfigLocale_test.cpp @@ -173,6 +173,18 @@ TEST(ConfigLocaleTest, IsMoreSpecificThan) { fillIn("en", "US", NULL, "POSIX", &r); EXPECT_FALSE(l.isMoreSpecificThan(r)); EXPECT_TRUE(r.isMoreSpecificThan(l)); + + fillIn("ar", "EG", NULL, NULL, &l); + fillIn("ar", "EG", NULL, NULL, &r); + memcpy(&r.localeNumberingSystem, "latn", 4); + EXPECT_FALSE(l.isMoreSpecificThan(r)); + EXPECT_TRUE(r.isMoreSpecificThan(l)); + + fillIn("en", "US", NULL, NULL, &l); + fillIn("es", "ES", NULL, NULL, &r); + + EXPECT_FALSE(l.isMoreSpecificThan(r)); + EXPECT_FALSE(r.isMoreSpecificThan(l)); } TEST(ConfigLocaleTest, setLocale) { @@ -185,6 +197,7 @@ TEST(ConfigLocaleTest, setLocale) { EXPECT_TRUE(test.localeScriptWasComputed); EXPECT_EQ(0, memcmp("Latn", test.localeScript, 4)); EXPECT_EQ(0, test.localeVariant[0]); + EXPECT_EQ(0, test.localeNumberingSystem[0]); test.setBcp47Locale("eng-419"); char out[4] = {1, 1, 1, 1}; @@ -198,6 +211,7 @@ TEST(ConfigLocaleTest, setLocale) { EXPECT_EQ('4', out[0]); EXPECT_EQ('1', out[1]); EXPECT_EQ('9', out[2]); + EXPECT_EQ(0, test.localeNumberingSystem[0]); test.setBcp47Locale("en-Latn-419"); EXPECT_EQ('e', test.language[0]); @@ -209,6 +223,7 @@ TEST(ConfigLocaleTest, setLocale) { EXPECT_EQ('4', out[0]); EXPECT_EQ('1', out[1]); EXPECT_EQ('9', out[2]); + EXPECT_EQ(0, test.localeNumberingSystem[0]); test.setBcp47Locale("de-1901"); memset(out, 1, 4); @@ -222,6 +237,7 @@ TEST(ConfigLocaleTest, setLocale) { test.unpackRegion(out); EXPECT_EQ('\0', out[0]); EXPECT_EQ(0, strcmp("1901", test.localeVariant)); + EXPECT_EQ(0, test.localeNumberingSystem[0]); test.setBcp47Locale("de-Latn-1901"); memset(out, 1, 4); @@ -235,6 +251,44 @@ TEST(ConfigLocaleTest, setLocale) { test.unpackRegion(out); EXPECT_EQ('\0', out[0]); EXPECT_EQ(0, strcmp("1901", test.localeVariant)); + EXPECT_EQ(0, test.localeNumberingSystem[0]); + + test.setBcp47Locale("ar-EG-u-nu-latn"); + EXPECT_EQ('a', test.language[0]); + EXPECT_EQ('r', test.language[1]); + EXPECT_EQ('E', test.country[0]); + EXPECT_EQ('G', test.country[1]); + EXPECT_TRUE(test.localeScriptWasComputed); + EXPECT_EQ(0, memcmp("Arab", test.localeScript, 4)); + EXPECT_EQ(0, test.localeVariant[0]); + EXPECT_EQ(0, memcmp("latn", test.localeNumberingSystem, 4)); + + test.setBcp47Locale("ar-EG-u"); + EXPECT_EQ(0, test.localeNumberingSystem[0]); + + test.setBcp47Locale("ar-EG-u-nu"); + EXPECT_EQ(0, test.localeNumberingSystem[0]); + + test.setBcp47Locale("ar-EG-u-attr-nu-latn"); + EXPECT_EQ(0, memcmp("latn", test.localeNumberingSystem, 4)); + + test.setBcp47Locale("ar-EG-u-ca-gregory-nu-latn"); + EXPECT_EQ(0, memcmp("latn", test.localeNumberingSystem, 4)); + + test.setBcp47Locale("ar-EG-u-nu-latn-ca-gregory"); + EXPECT_EQ(0, memcmp("latn", test.localeNumberingSystem, 4)); + + test.setBcp47Locale("ar-EG-u-nu-toolongnumsys"); + EXPECT_EQ(0, test.localeNumberingSystem[0]); + + test.setBcp47Locale("ar-EG-u-nu-latn-nu-arab"); + EXPECT_EQ(0, memcmp("latn", test.localeNumberingSystem, 4)); + + test.setBcp47Locale("ar-EG-u-co-nu-latn"); + EXPECT_EQ(0, test.localeNumberingSystem[0]); + + test.setBcp47Locale("ar-u-co-abcd-attr-nu-latn"); + EXPECT_EQ(0, test.localeNumberingSystem[0]); } TEST(ConfigLocaleTest, computeScript) { @@ -279,6 +333,22 @@ TEST(ConfigLocaleTest, getBcp47Locale_script) { EXPECT_EQ(0, strcmp("en", out)); } +TEST(ConfigLocaleTest, getBcp47Locale_numberingSystem) { + ResTable_config config; + fillIn("en", NULL, NULL, NULL, &config); + + char out[RESTABLE_MAX_LOCALE_LEN]; + + memcpy(&config.localeNumberingSystem, "latn", 4); + config.getBcp47Locale(out); + EXPECT_EQ(0, strcmp("en-u-nu-latn", out)); + + fillIn("sr", "SR", "Latn", NULL, &config); + memcpy(&config.localeNumberingSystem, "latn", 4); + config.getBcp47Locale(out); + EXPECT_EQ(0, strcmp("sr-Latn-SR-u-nu-latn", out)); +} + TEST(ConfigLocaleTest, getBcp47Locale_canonicalize) { ResTable_config config; char out[RESTABLE_MAX_LOCALE_LEN]; @@ -391,6 +461,11 @@ TEST(ConfigLocaleTest, match) { fillIn("ar", "XB", NULL, NULL, &requested); // Even if they are pseudo-locales, exactly equal locales match. EXPECT_TRUE(supported.match(requested)); + + fillIn("ar", "EG", NULL, NULL, &supported); + fillIn("ar", "TN", NULL, NULL, &requested); + memcpy(&supported.localeNumberingSystem, "latn", 4); + EXPECT_TRUE(supported.match(requested)); } TEST(ConfigLocaleTest, match_emptyScript) { @@ -716,6 +791,26 @@ TEST(ConfigLocaleTest, isLocaleBetterThan_regionComparison) { EXPECT_FALSE(config2.isLocaleBetterThan(config1, &request)); } +TEST(ConfigLocaleTest, isLocaleBetterThan_numberingSystem) { + ResTable_config config1, config2, request; + + fillIn("ar", "EG", NULL, NULL, &request); + memcpy(&request.localeNumberingSystem, "latn", 4); + fillIn("ar", NULL, NULL, NULL, &config1); + memcpy(&config1.localeNumberingSystem, "latn", 4); + fillIn("ar", NULL, NULL, NULL, &config2); + EXPECT_TRUE(config1.isLocaleBetterThan(config2, &request)); + EXPECT_FALSE(config2.isLocaleBetterThan(config1, &request)); + + fillIn("ar", "EG", NULL, NULL, &request); + memcpy(&request.localeNumberingSystem, "latn", 4); + fillIn("ar", "TN", NULL, NULL, &config1); + memcpy(&config1.localeNumberingSystem, "latn", 4); + fillIn("ar", NULL, NULL, NULL, &config2); + EXPECT_TRUE(config2.isLocaleBetterThan(config1, &request)); + EXPECT_FALSE(config1.isLocaleBetterThan(config2, &request)); +} + // Default resources are considered better matches for US English // and US-like English locales than International English locales TEST(ConfigLocaleTest, isLocaleBetterThan_UsEnglishIsSpecial) { diff --git a/libs/androidfw/tests/Idmap_test.cpp b/libs/androidfw/tests/Idmap_test.cpp index d12be184745c..9eb4a13f34d1 100644 --- a/libs/androidfw/tests/Idmap_test.cpp +++ b/libs/androidfw/tests/Idmap_test.cpp @@ -22,7 +22,7 @@ #include "TestHelpers.h" #include "data/basic/R.h" -using com::android::basic::R; +using ::com::android::basic::R; namespace android { diff --git a/libs/androidfw/tests/LoadedArsc_test.cpp b/libs/androidfw/tests/LoadedArsc_test.cpp index 756869f6377d..bedebd66cb2f 100644 --- a/libs/androidfw/tests/LoadedArsc_test.cpp +++ b/libs/androidfw/tests/LoadedArsc_test.cpp @@ -16,14 +16,25 @@ #include "androidfw/LoadedArsc.h" +#include "androidfw/ResourceUtils.h" + #include "TestHelpers.h" #include "data/basic/R.h" #include "data/libclient/R.h" +#include "data/sparse/R.h" #include "data/styles/R.h" namespace app = com::android::app; namespace basic = com::android::basic; namespace libclient = com::android::libclient; +namespace sparse = com::android::sparse; + +using ::testing::Eq; +using ::testing::Ge; +using ::testing::IsNull; +using ::testing::NotNull; +using ::testing::SizeIs; +using ::testing::StrEq; namespace android { @@ -32,49 +43,49 @@ TEST(LoadedArscTest, LoadSinglePackageArsc) { ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/styles/styles.apk", "resources.arsc", &contents)); - std::unique_ptr<const LoadedArsc> loaded_arsc = - LoadedArsc::Load(contents.data(), contents.size()); - ASSERT_NE(nullptr, loaded_arsc); + std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents)); + ASSERT_THAT(loaded_arsc, NotNull()); - const std::vector<std::unique_ptr<const LoadedPackage>>& packages = loaded_arsc->GetPackages(); - ASSERT_EQ(1u, packages.size()); - EXPECT_EQ(std::string("com.android.app"), packages[0]->GetPackageName()); - EXPECT_EQ(0x7f, packages[0]->GetPackageId()); + const LoadedPackage* package = + loaded_arsc->GetPackageById(get_package_id(app::R::string::string_one)); + ASSERT_THAT(package, NotNull()); + EXPECT_THAT(package->GetPackageName(), StrEq("com.android.app")); + EXPECT_THAT(package->GetPackageId(), Eq(0x7f)); - ResTable_config config; - memset(&config, 0, sizeof(config)); - config.sdkVersion = 24; + const uint8_t type_index = get_type_id(app::R::string::string_one) - 1; + const uint16_t entry_index = get_entry_id(app::R::string::string_one); - LoadedArscEntry entry; - ResTable_config selected_config; - uint32_t flags; + const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(type_index); + ASSERT_THAT(type_spec, NotNull()); + ASSERT_THAT(type_spec->type_count, Ge(1u)); - ASSERT_TRUE( - loaded_arsc->FindEntry(app::R::string::string_one, config, &entry, &selected_config, &flags)); - ASSERT_NE(nullptr, entry.entry); + const ResTable_type* type = type_spec->types[0]; + ASSERT_THAT(type, NotNull()); + ASSERT_THAT(LoadedPackage::GetEntry(type, entry_index), NotNull()); } -TEST(LoadedArscTest, FindDefaultEntry) { +TEST(LoadedArscTest, LoadSparseEntryApp) { std::string contents; - ASSERT_TRUE( - ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk", "resources.arsc", &contents)); + ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/sparse/sparse.apk", "resources.arsc", + &contents)); - std::unique_ptr<const LoadedArsc> loaded_arsc = - LoadedArsc::Load(contents.data(), contents.size()); - ASSERT_NE(nullptr, loaded_arsc); + std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents)); + ASSERT_THAT(loaded_arsc, NotNull()); - ResTable_config desired_config; - memset(&desired_config, 0, sizeof(desired_config)); - desired_config.language[0] = 'd'; - desired_config.language[1] = 'e'; + const LoadedPackage* package = + loaded_arsc->GetPackageById(get_package_id(sparse::R::integer::foo_9)); + ASSERT_THAT(package, NotNull()); - LoadedArscEntry entry; - ResTable_config selected_config; - uint32_t flags; + const uint8_t type_index = get_type_id(sparse::R::integer::foo_9) - 1; + const uint16_t entry_index = get_entry_id(sparse::R::integer::foo_9); - ASSERT_TRUE(loaded_arsc->FindEntry(basic::R::string::test1, desired_config, &entry, - &selected_config, &flags)); - ASSERT_NE(nullptr, entry.entry); + const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(type_index); + ASSERT_THAT(type_spec, NotNull()); + ASSERT_THAT(type_spec->type_count, Ge(1u)); + + const ResTable_type* type = type_spec->types[0]; + ASSERT_THAT(type, NotNull()); + ASSERT_THAT(LoadedPackage::GetEntry(type, entry_index), NotNull()); } TEST(LoadedArscTest, LoadSharedLibrary) { @@ -82,16 +93,14 @@ TEST(LoadedArscTest, LoadSharedLibrary) { ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/lib_one/lib_one.apk", "resources.arsc", &contents)); - std::unique_ptr<const LoadedArsc> loaded_arsc = - LoadedArsc::Load(contents.data(), contents.size()); - ASSERT_NE(nullptr, loaded_arsc); + std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents)); + ASSERT_THAT(loaded_arsc, NotNull()); const auto& packages = loaded_arsc->GetPackages(); - ASSERT_EQ(1u, packages.size()); - + ASSERT_THAT(packages, SizeIs(1u)); EXPECT_TRUE(packages[0]->IsDynamic()); - EXPECT_EQ(std::string("com.android.lib_one"), packages[0]->GetPackageName()); - EXPECT_EQ(0, packages[0]->GetPackageId()); + EXPECT_THAT(packages[0]->GetPackageName(), StrEq("com.android.lib_one")); + EXPECT_THAT(packages[0]->GetPackageId(), Eq(0)); const auto& dynamic_pkg_map = packages[0]->GetDynamicPackageMap(); @@ -104,27 +113,24 @@ TEST(LoadedArscTest, LoadAppLinkedAgainstSharedLibrary) { ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/libclient/libclient.apk", "resources.arsc", &contents)); - std::unique_ptr<const LoadedArsc> loaded_arsc = - LoadedArsc::Load(contents.data(), contents.size()); - ASSERT_NE(nullptr, loaded_arsc); + std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents)); + ASSERT_THAT(loaded_arsc, NotNull()); const auto& packages = loaded_arsc->GetPackages(); - ASSERT_EQ(1u, packages.size()); - + ASSERT_THAT(packages, SizeIs(1u)); EXPECT_FALSE(packages[0]->IsDynamic()); - EXPECT_EQ(std::string("com.android.libclient"), packages[0]->GetPackageName()); - EXPECT_EQ(0x7f, packages[0]->GetPackageId()); + EXPECT_THAT(packages[0]->GetPackageName(), StrEq("com.android.libclient")); + EXPECT_THAT(packages[0]->GetPackageId(), Eq(0x7f)); const auto& dynamic_pkg_map = packages[0]->GetDynamicPackageMap(); // The library has two dependencies. - ASSERT_EQ(2u, dynamic_pkg_map.size()); + ASSERT_THAT(dynamic_pkg_map, SizeIs(2u)); + EXPECT_THAT(dynamic_pkg_map[0].package_name, StrEq("com.android.lib_one")); + EXPECT_THAT(dynamic_pkg_map[0].package_id, Eq(0x02)); - EXPECT_EQ(std::string("com.android.lib_one"), dynamic_pkg_map[0].package_name); - EXPECT_EQ(0x02, dynamic_pkg_map[0].package_id); - - EXPECT_EQ(std::string("com.android.lib_two"), dynamic_pkg_map[1].package_name); - EXPECT_EQ(0x03, dynamic_pkg_map[1].package_id); + EXPECT_THAT(dynamic_pkg_map[1].package_name, StrEq("com.android.lib_two")); + EXPECT_THAT(dynamic_pkg_map[1].package_id, Eq(0x03)); } TEST(LoadedArscTest, LoadAppAsSharedLibrary) { @@ -132,46 +138,97 @@ TEST(LoadedArscTest, LoadAppAsSharedLibrary) { ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/appaslib/appaslib.apk", "resources.arsc", &contents)); - std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load( - contents.data(), contents.size(), false /*system*/, true /*load_as_shared_library*/); - ASSERT_NE(nullptr, loaded_arsc); + std::unique_ptr<const LoadedArsc> loaded_arsc = + LoadedArsc::Load(StringPiece(contents), nullptr /*loaded_idmap*/, false /*system*/, + true /*load_as_shared_library*/); + ASSERT_THAT(loaded_arsc, NotNull()); const auto& packages = loaded_arsc->GetPackages(); - ASSERT_EQ(1u, packages.size()); - + ASSERT_THAT(packages, SizeIs(1u)); EXPECT_TRUE(packages[0]->IsDynamic()); - EXPECT_EQ(0x7f, packages[0]->GetPackageId()); + EXPECT_THAT(packages[0]->GetPackageId(), Eq(0x7f)); } TEST(LoadedArscTest, LoadFeatureSplit) { std::string contents; ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/feature/feature.apk", "resources.arsc", &contents)); - std::unique_ptr<const LoadedArsc> loaded_arsc = - LoadedArsc::Load(contents.data(), contents.size()); - ASSERT_NE(nullptr, loaded_arsc); + std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents)); + ASSERT_THAT(loaded_arsc, NotNull()); - ResTable_config desired_config; - memset(&desired_config, 0, sizeof(desired_config)); + const LoadedPackage* package = + loaded_arsc->GetPackageById(get_package_id(basic::R::string::test3)); + ASSERT_THAT(package, NotNull()); - LoadedArscEntry entry; - ResTable_config selected_config; - uint32_t flags; + uint8_t type_index = get_type_id(basic::R::string::test3) - 1; + uint8_t entry_index = get_entry_id(basic::R::string::test3); - ASSERT_TRUE(loaded_arsc->FindEntry(basic::R::string::test3, desired_config, &entry, - &selected_config, &flags)); + const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(type_index); + ASSERT_THAT(type_spec, NotNull()); + ASSERT_THAT(type_spec->type_count, Ge(1u)); + ASSERT_THAT(type_spec->types[0], NotNull()); size_t len; - const char16_t* type_name16 = entry.type_string_ref.string16(&len); - ASSERT_NE(nullptr, type_name16); - ASSERT_NE(0u, len); + const char16_t* type_name16 = + package->GetTypeStringPool()->stringAt(type_spec->type_spec->id - 1, &len); + ASSERT_THAT(type_name16, NotNull()); + EXPECT_THAT(util::Utf16ToUtf8(StringPiece16(type_name16, len)), StrEq("string")); + + ASSERT_THAT(LoadedPackage::GetEntry(type_spec->types[0], entry_index), NotNull()); +} - size_t utf8_len = utf16_to_utf8_length(type_name16, len); - std::string type_name; - type_name.resize(utf8_len); - utf16_to_utf8(type_name16, len, &*type_name.begin(), utf8_len + 1); +class MockLoadedIdmap : public LoadedIdmap { + public: + MockLoadedIdmap() : LoadedIdmap() { + local_header_.magic = kIdmapMagic; + local_header_.version = kIdmapCurrentVersion; + local_header_.target_package_id = 0x08; + local_header_.type_count = 1; + header_ = &local_header_; + + entry_header = util::unique_cptr<IdmapEntry_header>( + (IdmapEntry_header*)::malloc(sizeof(IdmapEntry_header) + sizeof(uint32_t))); + entry_header->target_type_id = 0x03; + entry_header->overlay_type_id = 0x02; + entry_header->entry_id_offset = 1; + entry_header->entry_count = 1; + entry_header->entries[0] = 0x00000000u; + type_map_[entry_header->overlay_type_id] = entry_header.get(); + } + + private: + Idmap_header local_header_; + util::unique_cptr<IdmapEntry_header> entry_header; +}; + +TEST(LoadedArscTest, LoadOverlay) { + std::string contents; + ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/overlay/overlay.apk", "resources.arsc", + &contents)); - EXPECT_EQ(std::string("string"), type_name); + MockLoadedIdmap loaded_idmap; + + std::unique_ptr<const LoadedArsc> loaded_arsc = + LoadedArsc::Load(StringPiece(contents), &loaded_idmap); + ASSERT_THAT(loaded_arsc, NotNull()); + + const LoadedPackage* package = loaded_arsc->GetPackageById(0x08u); + ASSERT_THAT(package, NotNull()); + + const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(0x03u - 1); + ASSERT_THAT(type_spec, NotNull()); + ASSERT_THAT(type_spec->type_count, Ge(1u)); + ASSERT_THAT(type_spec->types[0], NotNull()); + + // The entry being overlaid doesn't exist at the original entry index. + ASSERT_THAT(LoadedPackage::GetEntry(type_spec->types[0], 0x0001u), IsNull()); + + // Since this is an overlay, the actual entry ID must be mapped. + ASSERT_THAT(type_spec->idmap_entries, NotNull()); + uint16_t target_entry_id = 0u; + ASSERT_TRUE(LoadedIdmap::Lookup(type_spec->idmap_entries, 0x0001u, &target_entry_id)); + ASSERT_THAT(target_entry_id, Eq(0x0u)); + ASSERT_THAT(LoadedPackage::GetEntry(type_spec->types[0], 0x0000), NotNull()); } // structs with size fields (like Res_value, ResTable_entry) should be diff --git a/libs/androidfw/tests/SparseEntry_bench.cpp b/libs/androidfw/tests/SparseEntry_bench.cpp index 1ebf7ce623bd..c9b4ad8af278 100644 --- a/libs/androidfw/tests/SparseEntry_bench.cpp +++ b/libs/androidfw/tests/SparseEntry_bench.cpp @@ -18,47 +18,46 @@ #include "androidfw/ResourceTypes.h" #include "BenchmarkHelpers.h" -#include "TestHelpers.h" #include "data/sparse/R.h" namespace sparse = com::android::sparse; namespace android { -static void BM_SparseEntryGetResourceSparseSmall(benchmark::State& state) { +static void BM_SparseEntryGetResourceOldSparse(benchmark::State& state, uint32_t resid) { ResTable_config config; memset(&config, 0, sizeof(config)); config.sdkVersion = 26; - GetResourceBenchmarkOld({GetTestDataPath() + "/sparse/sparse.apk"}, &config, - sparse::R::integer::foo_9, state); + GetResourceBenchmarkOld({GetTestDataPath() + "/sparse/sparse.apk"}, &config, resid, state); } -BENCHMARK(BM_SparseEntryGetResourceSparseSmall); +BENCHMARK_CAPTURE(BM_SparseEntryGetResourceOldSparse, Small, sparse::R::integer::foo_9); +BENCHMARK_CAPTURE(BM_SparseEntryGetResourceOldSparse, Large, sparse::R::string::foo_999); -static void BM_SparseEntryGetResourceNotSparseSmall(benchmark::State& state) { +static void BM_SparseEntryGetResourceOldNotSparse(benchmark::State& state, uint32_t resid) { ResTable_config config; memset(&config, 0, sizeof(config)); config.sdkVersion = 26; - GetResourceBenchmarkOld({GetTestDataPath() + "/sparse/not_sparse.apk"}, &config, - sparse::R::integer::foo_9, state); + GetResourceBenchmarkOld({GetTestDataPath() + "/sparse/not_sparse.apk"}, &config, resid, state); } -BENCHMARK(BM_SparseEntryGetResourceNotSparseSmall); +BENCHMARK_CAPTURE(BM_SparseEntryGetResourceOldNotSparse, Small, sparse::R::integer::foo_9); +BENCHMARK_CAPTURE(BM_SparseEntryGetResourceOldNotSparse, Large, sparse::R::string::foo_999); -static void BM_SparseEntryGetResourceSparseLarge(benchmark::State& state) { +static void BM_SparseEntryGetResourceSparse(benchmark::State& state, uint32_t resid) { ResTable_config config; memset(&config, 0, sizeof(config)); config.sdkVersion = 26; - GetResourceBenchmarkOld({GetTestDataPath() + "/sparse/sparse.apk"}, &config, - sparse::R::string::foo_999, state); + GetResourceBenchmark({GetTestDataPath() + "/sparse/sparse.apk"}, &config, resid, state); } -BENCHMARK(BM_SparseEntryGetResourceSparseLarge); +BENCHMARK_CAPTURE(BM_SparseEntryGetResourceSparse, Small, sparse::R::integer::foo_9); +BENCHMARK_CAPTURE(BM_SparseEntryGetResourceSparse, Large, sparse::R::string::foo_999); -static void BM_SparseEntryGetResourceNotSparseLarge(benchmark::State& state) { +static void BM_SparseEntryGetResourceNotSparse(benchmark::State& state, uint32_t resid) { ResTable_config config; memset(&config, 0, sizeof(config)); config.sdkVersion = 26; - GetResourceBenchmarkOld({GetTestDataPath() + "/sparse/not_sparse.apk"}, &config, - sparse::R::string::foo_999, state); + GetResourceBenchmark({GetTestDataPath() + "/sparse/not_sparse.apk"}, &config, resid, state); } -BENCHMARK(BM_SparseEntryGetResourceNotSparseLarge); +BENCHMARK_CAPTURE(BM_SparseEntryGetResourceNotSparse, Small, sparse::R::integer::foo_9); +BENCHMARK_CAPTURE(BM_SparseEntryGetResourceNotSparse, Large, sparse::R::string::foo_999); } // namespace android diff --git a/libs/androidfw/tests/TestHelpers.cpp b/libs/androidfw/tests/TestHelpers.cpp index 1e763a5e53a8..9e320a21b534 100644 --- a/libs/androidfw/tests/TestHelpers.cpp +++ b/libs/androidfw/tests/TestHelpers.cpp @@ -16,67 +16,22 @@ #include "TestHelpers.h" -#include <libgen.h> -#include <unistd.h> - -#include <memory> -#include <string> - -#include "android-base/file.h" -#include "android-base/logging.h" -#include "android-base/strings.h" #include "ziparchive/zip_archive.h" -namespace android { - -static std::string sTestDataPath; +using ::testing::AssertionFailure; +using ::testing::AssertionResult; +using ::testing::AssertionSuccess; -// Extract the directory of the current executable path. -static std::string GetExecutableDir() { - const std::string path = base::GetExecutablePath(); - std::unique_ptr<char, decltype(&std::free)> mutable_path = {strdup(path.c_str()), std::free}; - std::string executable_dir = dirname(mutable_path.get()); - return executable_dir; -} - -void InitializeTest(int* argc, char** argv) { - // Set the default test data path to be the executable path directory. - SetTestDataPath(GetExecutableDir()); - - for (int i = 1; i < *argc; i++) { - const std::string arg = argv[i]; - if (base::StartsWith(arg, "--testdata=")) { - SetTestDataPath(arg.substr(strlen("--testdata="))); - for (int j = i; j != *argc; j++) { - argv[j] = argv[j + 1]; - } - --(*argc); - --i; - } else if (arg == "-h" || arg == "--help") { - std::cerr << "\nAdditional options specific to this test:\n" - " --testdata=[PATH]\n" - " Specify the location of test data used within the tests.\n"; - exit(1); - } - } -} - -void SetTestDataPath(const std::string& path) { sTestDataPath = path; } - -const std::string& GetTestDataPath() { - CHECK(!sTestDataPath.empty()) << "no test data path set."; - return sTestDataPath; -} +namespace android { -::testing::AssertionResult ReadFileFromZipToString(const std::string& zip_path, - const std::string& file, - std::string* out_contents) { +AssertionResult ReadFileFromZipToString(const std::string& zip_path, const std::string& file, + std::string* out_contents) { out_contents->clear(); ::ZipArchiveHandle handle; int32_t result = OpenArchive(zip_path.c_str(), &handle); if (result != 0) { - return ::testing::AssertionFailure() << "Failed to open zip '" << zip_path - << "': " << ::ErrorCodeString(result); + return AssertionFailure() << "Failed to open zip '" << zip_path + << "': " << ::ErrorCodeString(result); } ::ZipString name(file.c_str()); @@ -84,8 +39,8 @@ const std::string& GetTestDataPath() { result = ::FindEntry(handle, name, &entry); if (result != 0) { ::CloseArchive(handle); - return ::testing::AssertionFailure() << "Could not find file '" << file << "' in zip '" - << zip_path << "' : " << ::ErrorCodeString(result); + return AssertionFailure() << "Could not find file '" << file << "' in zip '" << zip_path + << "' : " << ::ErrorCodeString(result); } out_contents->resize(entry.uncompressed_length); @@ -94,41 +49,36 @@ const std::string& GetTestDataPath() { out_contents->size()); if (result != 0) { ::CloseArchive(handle); - return ::testing::AssertionFailure() << "Failed to extract file '" << file << "' from zip '" - << zip_path << "': " << ::ErrorCodeString(result); + return AssertionFailure() << "Failed to extract file '" << file << "' from zip '" << zip_path + << "': " << ::ErrorCodeString(result); } ::CloseArchive(handle); - return ::testing::AssertionSuccess(); + return AssertionSuccess(); } -::testing::AssertionResult IsStringEqual(const ResTable& table, uint32_t resource_id, - const char* expected_str) { +AssertionResult IsStringEqual(const ResTable& table, uint32_t resource_id, + const char* expected_str) { Res_value val; ssize_t block = table.getResource(resource_id, &val, MAY_NOT_BE_BAG); if (block < 0) { - return ::testing::AssertionFailure() << "could not find resource"; + return AssertionFailure() << "could not find resource"; } if (val.dataType != Res_value::TYPE_STRING) { - return ::testing::AssertionFailure() << "resource is not a string"; + return AssertionFailure() << "resource is not a string"; } const ResStringPool* pool = table.getTableStringBlock(block); if (pool == NULL) { - return ::testing::AssertionFailure() << "table has no string pool for block " << block; + return AssertionFailure() << "table has no string pool for block " << block; } const String8 actual_str = pool->string8ObjectAt(val.data); if (String8(expected_str) != actual_str) { - return ::testing::AssertionFailure() << actual_str.string(); + return AssertionFailure() << actual_str.string(); } - return ::testing::AssertionSuccess() << actual_str.string(); -} - -std::string GetStringFromPool(const ResStringPool* pool, uint32_t idx) { - String8 str = pool->string8ObjectAt(idx); - return std::string(str.string(), str.length()); + return AssertionSuccess() << actual_str.string(); } } // namespace android diff --git a/libs/androidfw/tests/TestHelpers.h b/libs/androidfw/tests/TestHelpers.h index ec78b2ae5efc..df0c642f4565 100644 --- a/libs/androidfw/tests/TestHelpers.h +++ b/libs/androidfw/tests/TestHelpers.h @@ -14,53 +14,26 @@ * limitations under the License. */ -#ifndef TEST_HELPERS_H_ -#define TEST_HELPERS_H_ +#ifndef ANDROIDFW_TEST_TESTHELPERS_H +#define ANDROIDFW_TEST_TESTHELPERS_H -#include <ostream> #include <string> -#include <vector> #include "androidfw/ResourceTypes.h" +#include "gmock/gmock.h" #include "gtest/gtest.h" -#include "utils/String16.h" -#include "utils/String8.h" -static inline ::std::ostream& operator<<(::std::ostream& out, const android::String8& str) { - return out << str.string(); -} - -static inline ::std::ostream& operator<<(::std::ostream& out, const android::String16& str) { - return out << android::String8(str).string(); -} +#include "CommonHelpers.h" namespace android { -void InitializeTest(int* argc, char** argv); - -enum { MAY_NOT_BE_BAG = false }; - -void SetTestDataPath(const std::string& path); - -const std::string& GetTestDataPath(); - ::testing::AssertionResult ReadFileFromZipToString(const std::string& zip_path, const std::string& file, std::string* out_contents); -static inline bool operator==(const ResTable_config& a, const ResTable_config& b) { - return a.compare(b) == 0; -} - -static inline ::std::ostream& operator<<(::std::ostream& out, const ResTable_config& c) { - return out << c.toString().string(); -} - ::testing::AssertionResult IsStringEqual(const ResTable& table, uint32_t resource_id, const char* expected_str); -std::string GetStringFromPool(const ResStringPool* pool, uint32_t idx); - } // namespace android -#endif // TEST_HELPERS_H_ +#endif // ANDROIDFW_TEST_TESTHELPERS_H diff --git a/libs/androidfw/tests/Theme_test.cpp b/libs/androidfw/tests/Theme_test.cpp index dfff9c00922c..55d53edf6a2b 100644 --- a/libs/androidfw/tests/Theme_test.cpp +++ b/libs/androidfw/tests/Theme_test.cpp @@ -23,6 +23,7 @@ #include "data/lib_one/R.h" #include "data/libclient/R.h" #include "data/styles/R.h" +#include "data/system/R.h" namespace app = com::android::app; namespace lib_one = com::android::lib_one; @@ -33,6 +34,9 @@ namespace android { class ThemeTest : public ::testing::Test { public: void SetUp() override { + system_assets_ = ApkAssets::Load(GetTestDataPath() + "/system/system.apk", true /*system*/); + ASSERT_NE(nullptr, system_assets_); + style_assets_ = ApkAssets::Load(GetTestDataPath() + "/styles/styles.apk"); ASSERT_NE(nullptr, style_assets_); @@ -47,6 +51,7 @@ class ThemeTest : public ::testing::Test { } protected: + std::unique_ptr<const ApkAssets> system_assets_; std::unique_ptr<const ApkAssets> style_assets_; std::unique_ptr<const ApkAssets> libclient_assets_; std::unique_ptr<const ApkAssets> lib_one_assets_; @@ -123,6 +128,18 @@ TEST_F(ThemeTest, SingleThemeWithParent) { EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags); } +TEST_F(ThemeTest, TryToUseBadResourceId) { + AssetManager2 assetmanager; + assetmanager.SetApkAssets({style_assets_.get()}); + + std::unique_ptr<Theme> theme = assetmanager.NewTheme(); + ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleTwo)); + + Res_value value; + uint32_t flags; + ASSERT_EQ(kInvalidCookie, theme->GetAttribute(0x7f000001, &value, &flags)); +} + TEST_F(ThemeTest, MultipleThemesOverlaidNotForce) { AssetManager2 assetmanager; assetmanager.SetApkAssets({style_assets_.get()}); @@ -262,20 +279,30 @@ TEST_F(ThemeTest, CopyThemeSameAssetManager) { EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags); } -TEST_F(ThemeTest, FailToCopyThemeWithDifferentAssetManager) { +TEST_F(ThemeTest, OnlyCopySystemThemeWhenAssetManagersDiffer) { AssetManager2 assetmanager_one; - assetmanager_one.SetApkAssets({style_assets_.get()}); + assetmanager_one.SetApkAssets({system_assets_.get(), style_assets_.get()}); AssetManager2 assetmanager_two; - assetmanager_two.SetApkAssets({style_assets_.get()}); + assetmanager_two.SetApkAssets({system_assets_.get(), style_assets_.get()}); auto theme_one = assetmanager_one.NewTheme(); ASSERT_TRUE(theme_one->ApplyStyle(app::R::style::StyleOne)); auto theme_two = assetmanager_two.NewTheme(); + ASSERT_TRUE(theme_two->ApplyStyle(R::style::Theme_One)); ASSERT_TRUE(theme_two->ApplyStyle(app::R::style::StyleTwo)); - EXPECT_FALSE(theme_one->SetTo(*theme_two)); + EXPECT_TRUE(theme_one->SetTo(*theme_two)); + + Res_value value; + uint32_t flags; + + // No app resources. + EXPECT_EQ(kInvalidCookie, theme_one->GetAttribute(app::R::attr::attr_one, &value, &flags)); + + // Only system. + EXPECT_NE(kInvalidCookie, theme_one->GetAttribute(R::attr::foreground, &value, &flags)); } } // namespace android diff --git a/libs/androidfw/tests/data/basic/R.h b/libs/androidfw/tests/data/basic/R.h index 94a2a14ced87..b7e814fea079 100644 --- a/libs/androidfw/tests/data/basic/R.h +++ b/libs/androidfw/tests/data/basic/R.h @@ -34,6 +34,7 @@ struct R { struct layout { enum : uint32_t { main = 0x7f020000, + layoutt = 0x7f020001, }; }; @@ -55,6 +56,7 @@ struct R { number2 = 0x7f040001, ref1 = 0x7f040002, ref2 = 0x7f040003, + deep_ref = 0x7f040004, // From feature number3 = 0x80030000, diff --git a/libs/androidfw/tests/data/basic/basic.apk b/libs/androidfw/tests/data/basic/basic.apk Binary files differindex 0c17328e86b2..1733b6a16546 100644 --- a/libs/androidfw/tests/data/basic/basic.apk +++ b/libs/androidfw/tests/data/basic/basic.apk diff --git a/libs/androidfw/tests/data/basic/basic_de_fr.apk b/libs/androidfw/tests/data/basic/basic_de_fr.apk Binary files differindex e45258c6a005..767dff6fcfa5 100644 --- a/libs/androidfw/tests/data/basic/basic_de_fr.apk +++ b/libs/androidfw/tests/data/basic/basic_de_fr.apk diff --git a/libs/androidfw/tests/data/basic/basic_hdpi-v4.apk b/libs/androidfw/tests/data/basic/basic_hdpi-v4.apk Binary files differindex 4ae1a7c87a70..58953f56d736 100644 --- a/libs/androidfw/tests/data/basic/basic_hdpi-v4.apk +++ b/libs/androidfw/tests/data/basic/basic_hdpi-v4.apk diff --git a/libs/androidfw/tests/data/basic/basic_xhdpi-v4.apk b/libs/androidfw/tests/data/basic/basic_xhdpi-v4.apk Binary files differindex a240d4c06c1d..103f6565bb06 100644 --- a/libs/androidfw/tests/data/basic/basic_xhdpi-v4.apk +++ b/libs/androidfw/tests/data/basic/basic_xhdpi-v4.apk diff --git a/libs/androidfw/tests/data/basic/basic_xxhdpi-v4.apk b/libs/androidfw/tests/data/basic/basic_xxhdpi-v4.apk Binary files differindex fd3d9b233084..61369d506786 100644 --- a/libs/androidfw/tests/data/basic/basic_xxhdpi-v4.apk +++ b/libs/androidfw/tests/data/basic/basic_xxhdpi-v4.apk diff --git a/libs/androidfw/tests/data/basic/build b/libs/androidfw/tests/data/basic/build index d619800d6fc5..5682ed4d3041 100755 --- a/libs/androidfw/tests/data/basic/build +++ b/libs/androidfw/tests/data/basic/build @@ -19,11 +19,15 @@ set -e PATH_TO_FRAMEWORK_RES=${ANDROID_BUILD_TOP}/prebuilts/sdk/current/android.jar -aapt package \ - -M AndroidManifest.xml \ - -S res \ - -A assets \ +aapt2 compile --dir res -o compiled.flata +aapt2 link \ -I $PATH_TO_FRAMEWORK_RES \ - --split hdpi --split xhdpi --split xxhdpi --split fr,de \ - -F basic.apk \ - -f + --manifest AndroidManifest.xml \ + -A assets \ + --split basic_hdpi-v4.apk:hdpi \ + --split basic_xhdpi-v4.apk:xhdpi \ + --split basic_xxhdpi-v4.apk:xxhdpi \ + --split basic_de_fr.apk:de,fr \ + -o basic.apk \ + compiled.flata +rm compiled.flata diff --git a/libs/androidfw/tests/data/basic/res/layout/layout.xml b/libs/androidfw/tests/data/basic/res/layout/layout.xml new file mode 100644 index 000000000000..045ede454bca --- /dev/null +++ b/libs/androidfw/tests/data/basic/res/layout/layout.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2018 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<Button xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/ok" + android:layout_width="0sp" + android:layout_height="fill_parent" + android:layout_weight="1" + android:layout_marginStart="2dip" + android:layout_marginEnd="2dip" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textStyle="bold" + android:text="@android:string/ok" />
\ No newline at end of file diff --git a/libs/androidfw/tests/data/basic/res/values/values.xml b/libs/androidfw/tests/data/basic/res/values/values.xml index 638c9832ab4c..b3435629265b 100644 --- a/libs/androidfw/tests/data/basic/res/values/values.xml +++ b/libs/androidfw/tests/data/basic/res/values/values.xml @@ -22,6 +22,7 @@ <attr name="attr2" format="reference|integer" /> <public type="layout" name="main" id="0x7f020000" /> + <public type="layout" name="layout" id="0x7f020001" /> <public type="string" name="test1" id="0x7f030000" /> <string name="test1">test1</string> @@ -43,6 +44,18 @@ <public type="integer" name="ref2" id="0x7f040003" /> <integer name="ref2">12000</integer> + <public type="integer" name="deep_ref" id="0x7f040004" /> + <integer name="deep_ref">@integer/deep_ref_1</integer> + <integer name="deep_ref_1">@integer/deep_ref_2</integer> + <integer name="deep_ref_2">@integer/deep_ref_3</integer> + <integer name="deep_ref_3">@integer/deep_ref_4</integer> + <integer name="deep_ref_4">@integer/deep_ref_5</integer> + <integer name="deep_ref_5">@integer/deep_ref_6</integer> + <integer name="deep_ref_6">@integer/deep_ref_7</integer> + <integer name="deep_ref_7">@integer/deep_ref_8</integer> + <integer name="deep_ref_8">@integer/deep_ref_9</integer> + <integer name="deep_ref_9">100</integer> + <public type="style" name="Theme1" id="0x7f050000" /> <style name="Theme1"> <item name="com.android.basic:attr1">100</item> @@ -60,4 +73,9 @@ <item>2</item> <item>3</item> </integer-array> + + <overlayable> + <item type="string" name="test2" /> + <item type="array" name="integerArray1" /> + </overlayable> </resources> diff --git a/libs/androidfw/tests/data/overlay/build b/libs/androidfw/tests/data/overlay/build index 112f373ead30..716b1bd9db64 100755 --- a/libs/androidfw/tests/data/overlay/build +++ b/libs/androidfw/tests/data/overlay/build @@ -17,4 +17,6 @@ set -e -aapt package -M AndroidManifest.xml -S res -F overlay.apk -f +aapt2 compile --dir res -o compiled.flata +aapt2 link --manifest AndroidManifest.xml -o overlay.apk compiled.flata +rm compiled.flata diff --git a/libs/androidfw/tests/data/overlay/overlay.apk b/libs/androidfw/tests/data/overlay/overlay.apk Binary files differindex 40bf17c5951a..33f961117c44 100644 --- a/libs/androidfw/tests/data/overlay/overlay.apk +++ b/libs/androidfw/tests/data/overlay/overlay.apk diff --git a/libs/hwui/.clang-format b/libs/hwui/.clang-format new file mode 100644 index 000000000000..5e9bba8377cb --- /dev/null +++ b/libs/hwui/.clang-format @@ -0,0 +1,10 @@ +BasedOnStyle: Google +DerivePointerAlignment: false +IndentWidth: 4 +ConstructorInitializerIndentWidth: 8 +ContinuationIndentWidth: 8 +ColumnLimit: 100 +AllowShortFunctionsOnASingleLine: Inline +AccessModifierOffset: -4 +#BreakConstructorInitializers: BeforeComma +BreakConstructorInitializersBeforeComma: true diff --git a/libs/hwui/AmbientShadow.cpp b/libs/hwui/AmbientShadow.cpp index 3982fa0938bf..aa96698c1e53 100644 --- a/libs/hwui/AmbientShadow.cpp +++ b/libs/hwui/AmbientShadow.cpp @@ -25,7 +25,7 @@ // For the whole polygon, the sum of all the deltas b/t normals is 2 * M_PI, // therefore, the maximum number of extra vertices will be twice bigger. -#define MAX_EXTRA_CORNER_VERTEX_NUMBER (2 * EXTRA_CORNER_VERTEX_PER_PI) +#define MAX_EXTRA_CORNER_VERTEX_NUMBER (2 * EXTRA_CORNER_VERTEX_PER_PI) // For each RADIANS_DIVISOR, we would allocate one more vertex b/t the normals. #define CORNER_RADIANS_DIVISOR (M_PI / EXTRA_CORNER_VERTEX_PER_PI) @@ -36,9 +36,9 @@ */ #define EXTRA_EDGE_VERTEX_PER_PI 50 -#define MAX_EXTRA_EDGE_VERTEX_NUMBER (2 * EXTRA_EDGE_VERTEX_PER_PI) +#define MAX_EXTRA_EDGE_VERTEX_NUMBER (2 * EXTRA_EDGE_VERTEX_PER_PI) -#define EDGE_RADIANS_DIVISOR (M_PI / EXTRA_EDGE_VERTEX_PER_PI) +#define EDGE_RADIANS_DIVISOR (M_PI / EXTRA_EDGE_VERTEX_PER_PI) /** * Other constants: @@ -56,8 +56,8 @@ #include "Vertex.h" #include "VertexBuffer.h" -#include <algorithm> #include <utils/Log.h> +#include <algorithm> namespace android { namespace uirenderer { @@ -67,8 +67,8 @@ namespace uirenderer { */ inline Vector2 getNormalFromVertices(const Vector3* vertices, int current, int next) { // Convert from Vector3 to Vector2 first. - Vector2 currentVertex = { vertices[current].x, vertices[current].y }; - Vector2 nextVertex = { vertices[next].x, vertices[next].y }; + Vector2 currentVertex = {vertices[current].x, vertices[current].y}; + Vector2 nextVertex = {vertices[next].x, vertices[next].y}; return ShadowTessellator::calculateNormal(currentVertex, nextVertex); } @@ -79,24 +79,24 @@ inline float getAlphaFromFactoredZ(float factoredZ) { return 1.0 / (1 + std::max(factoredZ, 0.0f)); } -inline int getEdgeExtraAndUpdateSpike(Vector2* currentSpike, - const Vector3& secondVertex, const Vector3& centroid) { - Vector2 secondSpike = {secondVertex.x - centroid.x, secondVertex.y - centroid.y}; +inline int getEdgeExtraAndUpdateSpike(Vector2* currentSpike, const Vector3& secondVertex, + const Vector3& centroid) { + Vector2 secondSpike = {secondVertex.x - centroid.x, secondVertex.y - centroid.y}; secondSpike.normalize(); int result = ShadowTessellator::getExtraVertexNumber(secondSpike, *currentSpike, - EDGE_RADIANS_DIVISOR); + EDGE_RADIANS_DIVISOR); *currentSpike = secondSpike; return result; } // Given the caster's vertex count, compute all the buffers size depending on // whether or not the caster is opaque. -inline void computeBufferSize(int* totalVertexCount, int* totalIndexCount, - int* totalUmbraCount, int casterVertexCount, bool isCasterOpaque) { +inline void computeBufferSize(int* totalVertexCount, int* totalIndexCount, int* totalUmbraCount, + int casterVertexCount, bool isCasterOpaque) { // Compute the size of the vertex buffer. - int outerVertexCount = casterVertexCount * 2 + MAX_EXTRA_CORNER_VERTEX_NUMBER + - MAX_EXTRA_EDGE_VERTEX_NUMBER; + int outerVertexCount = + casterVertexCount * 2 + MAX_EXTRA_CORNER_VERTEX_NUMBER + MAX_EXTRA_EDGE_VERTEX_NUMBER; int innerVertexCount = casterVertexCount + MAX_EXTRA_EDGE_VERTEX_NUMBER; *totalVertexCount = outerVertexCount + innerVertexCount; @@ -163,44 +163,42 @@ inline bool needsExtraForEdge(float firstAlpha, float secondAlpha) { * | | * (V3)-----------------------------------(V2) */ -void AmbientShadow::createAmbientShadow(bool isCasterOpaque, - const Vector3* casterVertices, int casterVertexCount, const Vector3& centroid3d, - float heightFactor, float geomFactor, VertexBuffer& shadowVertexBuffer) { +void AmbientShadow::createAmbientShadow(bool isCasterOpaque, const Vector3* casterVertices, + int casterVertexCount, const Vector3& centroid3d, + float heightFactor, float geomFactor, + VertexBuffer& shadowVertexBuffer) { shadowVertexBuffer.setMeshFeatureFlags(VertexBuffer::kAlpha | VertexBuffer::kIndices); // In order to computer the outer vertices in one loop, we need pre-compute // the normal by the vertex (n - 1) to vertex 0, and the spike and alpha value // for vertex 0. - Vector2 previousNormal = getNormalFromVertices(casterVertices, - casterVertexCount - 1 , 0); - Vector2 currentSpike = {casterVertices[0].x - centroid3d.x, - casterVertices[0].y - centroid3d.y}; + Vector2 previousNormal = getNormalFromVertices(casterVertices, casterVertexCount - 1, 0); + Vector2 currentSpike = {casterVertices[0].x - centroid3d.x, casterVertices[0].y - centroid3d.y}; currentSpike.normalize(); float currentAlpha = getAlphaFromFactoredZ(casterVertices[0].z * heightFactor); // Preparing all the output data. int totalVertexCount, totalIndexCount, totalUmbraCount; - computeBufferSize(&totalVertexCount, &totalIndexCount, &totalUmbraCount, - casterVertexCount, isCasterOpaque); - AlphaVertex* shadowVertices = - shadowVertexBuffer.alloc<AlphaVertex>(totalVertexCount); + computeBufferSize(&totalVertexCount, &totalIndexCount, &totalUmbraCount, casterVertexCount, + isCasterOpaque); + AlphaVertex* shadowVertices = shadowVertexBuffer.alloc<AlphaVertex>(totalVertexCount); int vertexBufferIndex = 0; uint16_t* indexBuffer = shadowVertexBuffer.allocIndices<uint16_t>(totalIndexCount); int indexBufferIndex = 0; uint16_t umbraVertices[totalUmbraCount]; int umbraIndex = 0; - for (int i = 0; i < casterVertexCount; i++) { + for (int i = 0; i < casterVertexCount; i++) { // Corner: first figure out the extra vertices we need for the corner. const Vector3& innerVertex = casterVertices[i]; - Vector2 currentNormal = getNormalFromVertices(casterVertices, i, - (i + 1) % casterVertexCount); + Vector2 currentNormal = + getNormalFromVertices(casterVertices, i, (i + 1) % casterVertexCount); - int extraVerticesNumber = ShadowTessellator::getExtraVertexNumber(currentNormal, - previousNormal, CORNER_RADIANS_DIVISOR); + int extraVerticesNumber = ShadowTessellator::getExtraVertexNumber( + currentNormal, previousNormal, CORNER_RADIANS_DIVISOR); float expansionDist = innerVertex.z * heightFactor * geomFactor; - const int cornerSlicesNumber = extraVerticesNumber + 1; // Minimal as 1. + const int cornerSlicesNumber = extraVerticesNumber + 1; // Minimal as 1. #if DEBUG_SHADOW ALOGD("cornerSlicesNumber is %d", cornerSlicesNumber); #endif @@ -212,9 +210,8 @@ void AmbientShadow::createAmbientShadow(bool isCasterOpaque, if (!isCasterOpaque) { umbraVertices[umbraIndex++] = vertexBufferIndex; } - AlphaVertex::set(&shadowVertices[vertexBufferIndex++], - casterVertices[i].x, casterVertices[i].y, - currentAlpha); + AlphaVertex::set(&shadowVertices[vertexBufferIndex++], casterVertices[i].x, + casterVertices[i].y, currentAlpha); const Vector3& innerStart = casterVertices[i]; @@ -225,8 +222,7 @@ void AmbientShadow::createAmbientShadow(bool isCasterOpaque, // This will create vertices from [0, cornerSlicesNumber] inclusively, // which means minimally 2 vertices even without the extra ones. for (int j = 0; j <= cornerSlicesNumber; j++) { - Vector2 averageNormal = - previousNormal * (cornerSlicesNumber - j) + currentNormal * j; + Vector2 averageNormal = previousNormal * (cornerSlicesNumber - j) + currentNormal * j; averageNormal /= cornerSlicesNumber; averageNormal.normalize(); Vector2 outerVertex; @@ -235,8 +231,8 @@ void AmbientShadow::createAmbientShadow(bool isCasterOpaque, indexBuffer[indexBufferIndex++] = vertexBufferIndex; indexBuffer[indexBufferIndex++] = currentInnerVertexIndex; - AlphaVertex::set(&shadowVertices[vertexBufferIndex++], outerVertex.x, - outerVertex.y, OUTER_ALPHA); + AlphaVertex::set(&shadowVertices[vertexBufferIndex++], outerVertex.x, outerVertex.y, + OUTER_ALPHA); if (j == 0) { outerStart = outerVertex; @@ -257,8 +253,8 @@ void AmbientShadow::createAmbientShadow(bool isCasterOpaque, outerNext.y = innerNext.y + currentNormal.y * expansionDist; // Compute the angle and see how many extra points we need. - int extraVerticesNumber = getEdgeExtraAndUpdateSpike(¤tSpike, - innerNext, centroid3d); + int extraVerticesNumber = + getEdgeExtraAndUpdateSpike(¤tSpike, innerNext, centroid3d); #if DEBUG_SHADOW ALOGD("extraVerticesNumber %d for edge %d", extraVerticesNumber, i); #endif @@ -269,20 +265,20 @@ void AmbientShadow::createAmbientShadow(bool isCasterOpaque, for (int k = 1; k < extraVerticesNumber; k++) { int startWeight = extraVerticesNumber - k; Vector2 currentOuter = - (outerLast * startWeight + outerNext * k) / extraVerticesNumber; + (outerLast * startWeight + outerNext * k) / extraVerticesNumber; indexBuffer[indexBufferIndex++] = vertexBufferIndex; AlphaVertex::set(&shadowVertices[vertexBufferIndex++], currentOuter.x, - currentOuter.y, OUTER_ALPHA); + currentOuter.y, OUTER_ALPHA); if (!isCasterOpaque) { umbraVertices[umbraIndex++] = vertexBufferIndex; } Vector3 currentInner = - (innerStart * startWeight + innerNext * k) / extraVerticesNumber; + (innerStart * startWeight + innerNext * k) / extraVerticesNumber; indexBuffer[indexBufferIndex++] = vertexBufferIndex; AlphaVertex::set(&shadowVertices[vertexBufferIndex++], currentInner.x, - currentInner.y, - getAlphaFromFactoredZ(currentInner.z * heightFactor)); + currentInner.y, + getAlphaFromFactoredZ(currentInner.z * heightFactor)); } } currentAlpha = nextAlpha; @@ -293,11 +289,10 @@ void AmbientShadow::createAmbientShadow(bool isCasterOpaque, if (!isCasterOpaque) { // Add the centroid as the last one in the vertex buffer. - float centroidOpacity = - getAlphaFromFactoredZ(centroid3d.z * heightFactor); + float centroidOpacity = getAlphaFromFactoredZ(centroid3d.z * heightFactor); int centroidIndex = vertexBufferIndex; - AlphaVertex::set(&shadowVertices[vertexBufferIndex++], centroid3d.x, - centroid3d.y, centroidOpacity); + AlphaVertex::set(&shadowVertices[vertexBufferIndex++], centroid3d.x, centroid3d.y, + centroidOpacity); for (int i = 0; i < umbraIndex; i++) { // Note that umbraVertices[0] is always 0. @@ -322,7 +317,7 @@ void AmbientShadow::createAmbientShadow(bool isCasterOpaque, #if DEBUG_SHADOW for (int i = 0; i < vertexBufferIndex; i++) { ALOGD("vertexBuffer i %d, (%f, %f %f)", i, shadowVertices[i].x, shadowVertices[i].y, - shadowVertices[i].alpha); + shadowVertices[i].alpha); } for (int i = 0; i < indexBufferIndex; i++) { ALOGD("indexBuffer i %d, indexBuffer[i] %d", i, indexBuffer[i]); @@ -330,5 +325,5 @@ void AmbientShadow::createAmbientShadow(bool isCasterOpaque, #endif } -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/AmbientShadow.h b/libs/hwui/AmbientShadow.h index 8eb1048f4d1c..cb1d915f2540 100644 --- a/libs/hwui/AmbientShadow.h +++ b/libs/hwui/AmbientShadow.h @@ -31,12 +31,12 @@ class VertexBuffer; */ class AmbientShadow { public: - static void createAmbientShadow(bool isCasterOpaque, const Vector3* poly, - int polyLength, const Vector3& centroid3d, float heightFactor, - float geomFactor, VertexBuffer& shadowVertexBuffer); -}; // AmbientShadow + static void createAmbientShadow(bool isCasterOpaque, const Vector3* poly, int polyLength, + const Vector3& centroid3d, float heightFactor, float geomFactor, + VertexBuffer& shadowVertexBuffer); +}; // AmbientShadow -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android -#endif // ANDROID_HWUI_AMBIENT_SHADOW_H +#endif // ANDROID_HWUI_AMBIENT_SHADOW_H diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index 770a57a5c445..17f9b7cd62fc 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -2,11 +2,14 @@ cc_defaults { name: "hwui_defaults", defaults: [ "hwui_static_deps", - + "skia_deps", //"hwui_bugreport_font_cache_usage", //"hwui_compile_for_perf", + "hwui_pgo", ], + cpp_std: "c++17", + cflags: [ "-DEGL_EGLEXT_PROTOTYPES", "-DGL_GLEXT_PROTOTYPES", @@ -55,7 +58,6 @@ cc_defaults { "libEGL", "libGLESv2", "libvulkan", - "libskia", "libui", "libgui", "libprotobuf-cpp-lite", @@ -67,6 +69,7 @@ cc_defaults { ], static_libs: [ "libplatformprotos", + "libEGL_blobCache", ], } @@ -107,6 +110,22 @@ cc_defaults { include_dirs: ["frameworks/native/opengl/libs/GLES2"], } +// Build libhwui with PGO by default. +// Location of PGO profile data is defined in build/soong/cc/pgo.go +// and is separate from hwui. +// To turn it off, set ANDROID_PGO_NO_PROFILE_USE environment variable +// or set enable_profile_use property to false. +cc_defaults { + name: "hwui_pgo", + + pgo: { + instrumentation: true, + profile_file: "hwui/hwui.profdata", + benchmarks: ["hwui"], + enable_profile_use: true, + }, +} + // ------------------------ // library // ------------------------ @@ -114,7 +133,11 @@ cc_defaults { cc_defaults { name: "libhwui_defaults", defaults: ["hwui_defaults"], + + whole_static_libs: ["libskia"], + srcs: [ + "hwui/AnimatedImageDrawable.cpp", "hwui/Bitmap.cpp", "font/CacheTexture.cpp", "font/Font.cpp", @@ -127,6 +150,7 @@ cc_defaults { "pipeline/skia/LayerDrawable.cpp", "pipeline/skia/RenderNodeDrawable.cpp", "pipeline/skia/ReorderBarrierDrawables.cpp", + "pipeline/skia/ShaderCache.cpp", "pipeline/skia/SkiaDisplayList.cpp", "pipeline/skia/SkiaOpenGLPipeline.cpp", "pipeline/skia/SkiaOpenGLReadback.cpp", @@ -248,7 +272,7 @@ cc_library { // If enabled, every GLES call is wrapped & error checked // Has moderate overhead "hwui_enable_opengl_validation", -], + ], } // ------------------------ @@ -330,6 +354,7 @@ cc_test { "tests/unit/RecordingCanvasTests.cpp", "tests/unit/RenderNodeTests.cpp", "tests/unit/RenderPropertiesTests.cpp", + "tests/unit/ShaderCacheTests.cpp", "tests/unit/SkiaBehaviorTests.cpp", "tests/unit/SkiaDisplayListTests.cpp", "tests/unit/SkiaPipelineTests.cpp", @@ -340,7 +365,8 @@ cc_test { "tests/unit/TestUtilsTests.cpp", "tests/unit/TextDropShadowCacheTests.cpp", "tests/unit/TextureCacheTests.cpp", - "tests/unit/TypefaceTests.cpp", + "tests/unit/ThreadBaseTests.cpp", + "tests/unit/TypefaceTests.cpp", "tests/unit/VectorDrawableTests.cpp", "tests/unit/VectorDrawableAtlasTests.cpp", ], @@ -392,3 +418,15 @@ cc_benchmark { "tests/microbench/TaskManagerBench.cpp", ], } + +// ---------------------------------------- +// Phony target to build benchmarks for PGO +// ---------------------------------------- + +phony { + name: "pgo-targets-hwui", + required: [ + "hwuimicro", + "hwuimacro", + ] +} diff --git a/libs/hwui/AnimationContext.cpp b/libs/hwui/AnimationContext.cpp index 5759ccd85464..46c9da2ac6b8 100644 --- a/libs/hwui/AnimationContext.cpp +++ b/libs/hwui/AnimationContext.cpp @@ -26,11 +26,9 @@ AnimationContext::AnimationContext(renderthread::TimeLord& clock) : mClock(clock) , mCurrentFrameAnimations(*this) , mNextFrameAnimations(*this) - , mFrameTimeMs(0) { -} + , mFrameTimeMs(0) {} -AnimationContext::~AnimationContext() { -} +AnimationContext::~AnimationContext() {} void AnimationContext::destroy() { startFrame(TreeInfo::MODE_RT_ONLY); @@ -39,7 +37,7 @@ void AnimationContext::destroy() { AnimatorManager& animators = current->mRenderNode->animators(); animators.endAllActiveAnimators(); LOG_ALWAYS_FATAL_IF(mCurrentFrameAnimations.mNextHandle == current, - "endAllAnimators failed to remove from current frame list!"); + "endAllAnimators failed to remove from current frame list!"); } } @@ -56,7 +54,7 @@ void AnimationContext::addAnimationHandle(AnimationHandle* handle) { void AnimationContext::startFrame(TreeInfo::TraversalMode mode) { LOG_ALWAYS_FATAL_IF(mCurrentFrameAnimations.mNextHandle, - "Missed running animations last frame!"); + "Missed running animations last frame!"); AnimationHandle* head = mNextFrameAnimations.mNextHandle; if (head) { mNextFrameAnimations.mNextHandle = nullptr; @@ -73,20 +71,17 @@ void AnimationContext::runRemainingAnimations(TreeInfo& info) { animators.pushStaging(); animators.animateNoDamage(info); LOG_ALWAYS_FATAL_IF(mCurrentFrameAnimations.mNextHandle == current, - "Animate failed to remove from current frame list!"); + "Animate failed to remove from current frame list!"); } } void AnimationContext::callOnFinished(BaseRenderNodeAnimator* animator, - AnimationListener* listener) { + AnimationListener* listener) { listener->onAnimationFinished(animator); } AnimationHandle::AnimationHandle(AnimationContext& context) - : mContext(context) - , mPreviousHandle(nullptr) - , mNextHandle(nullptr) { -} + : mContext(context), mPreviousHandle(nullptr), mNextHandle(nullptr) {} AnimationHandle::AnimationHandle(RenderNode& animatingNode, AnimationContext& context) : mRenderNode(&animatingNode) @@ -98,7 +93,7 @@ AnimationHandle::AnimationHandle(RenderNode& animatingNode, AnimationContext& co AnimationHandle::~AnimationHandle() { LOG_ALWAYS_FATAL_IF(mPreviousHandle || mNextHandle, - "AnimationHandle destroyed while still animating!"); + "AnimationHandle destroyed while still animating!"); } void AnimationHandle::notifyAnimationsRan() { @@ -112,7 +107,7 @@ void AnimationHandle::notifyAnimationsRan() { void AnimationHandle::release() { LOG_ALWAYS_FATAL_IF(mRenderNode->animators().hasAnimators(), - "Releasing the handle for an RenderNode with outstanding animators!"); + "Releasing the handle for an RenderNode with outstanding animators!"); removeFromList(); mRenderNode->animators().setAnimationHandle(nullptr); delete this; diff --git a/libs/hwui/AnimationContext.h b/libs/hwui/AnimationContext.h index 71ee8602a29f..74d5e79c0b77 100644 --- a/libs/hwui/AnimationContext.h +++ b/libs/hwui/AnimationContext.h @@ -43,6 +43,7 @@ class RenderNode; */ class AnimationHandle { PREVENT_COPY_AND_ASSIGN(AnimationHandle); + public: AnimationContext& context() { return mContext; } @@ -74,14 +75,14 @@ private: class AnimationContext { PREVENT_COPY_AND_ASSIGN(AnimationContext); + public: ANDROID_API explicit AnimationContext(renderthread::TimeLord& clock); ANDROID_API virtual ~AnimationContext(); nsecs_t frameTimeMs() { return mFrameTimeMs; } bool hasAnimations() { - return mCurrentFrameAnimations.mNextHandle - || mNextFrameAnimations.mNextHandle; + return mCurrentFrameAnimations.mNextHandle || mNextFrameAnimations.mNextHandle; } // Will always add to the next frame list, which is swapped when @@ -96,7 +97,8 @@ public: // as part of the standard RenderNode:prepareTree pass. ANDROID_API virtual void runRemainingAnimations(TreeInfo& info); - ANDROID_API virtual void callOnFinished(BaseRenderNodeAnimator* animator, AnimationListener* listener); + ANDROID_API virtual void callOnFinished(BaseRenderNodeAnimator* animator, + AnimationListener* listener); ANDROID_API virtual void destroy(); diff --git a/libs/hwui/Animator.cpp b/libs/hwui/Animator.cpp index b6fbf891f84d..74cf1fda1b75 100644 --- a/libs/hwui/Animator.cpp +++ b/libs/hwui/Animator.cpp @@ -44,16 +44,14 @@ BaseRenderNodeAnimator::BaseRenderNodeAnimator(float finalValue) , mDuration(300) , mStartDelay(0) , mMayRunAsync(true) - , mPlayTime(0) { -} + , mPlayTime(0) {} -BaseRenderNodeAnimator::~BaseRenderNodeAnimator() { -} +BaseRenderNodeAnimator::~BaseRenderNodeAnimator() {} void BaseRenderNodeAnimator::checkMutable() { // Should be impossible to hit as the Java-side also has guards for this LOG_ALWAYS_FATAL_IF(mStagingPlayState != PlayState::NotStarted, - "Animator has already been started!"); + "Animator has already been started!"); } void BaseRenderNodeAnimator::setInterpolator(Interpolator* interpolator) { @@ -119,34 +117,36 @@ void BaseRenderNodeAnimator::end() { void BaseRenderNodeAnimator::resolveStagingRequest(Request request) { switch (request) { - case Request::Start: - mPlayTime = (mPlayState == PlayState::Running || mPlayState == PlayState::Reversing) ? - mPlayTime : 0; - mPlayState = PlayState::Running; - mPendingActionUponFinish = Action::None; - break; - case Request::Reverse: - mPlayTime = (mPlayState == PlayState::Running || mPlayState == PlayState::Reversing) ? - mPlayTime : mDuration; - mPlayState = PlayState::Reversing; - mPendingActionUponFinish = Action::None; - break; - case Request::Reset: - mPlayTime = 0; - mPlayState = PlayState::Finished; - mPendingActionUponFinish = Action::Reset; - break; - case Request::Cancel: - mPlayState = PlayState::Finished; - mPendingActionUponFinish = Action::None; - break; - case Request::End: - mPlayTime = mPlayState == PlayState::Reversing ? 0 : mDuration; - mPlayState = PlayState::Finished; - mPendingActionUponFinish = Action::End; - break; - default: - LOG_ALWAYS_FATAL("Invalid staging request: %d", static_cast<int>(request)); + case Request::Start: + mPlayTime = (mPlayState == PlayState::Running || mPlayState == PlayState::Reversing) + ? mPlayTime + : 0; + mPlayState = PlayState::Running; + mPendingActionUponFinish = Action::None; + break; + case Request::Reverse: + mPlayTime = (mPlayState == PlayState::Running || mPlayState == PlayState::Reversing) + ? mPlayTime + : mDuration; + mPlayState = PlayState::Reversing; + mPendingActionUponFinish = Action::None; + break; + case Request::Reset: + mPlayTime = 0; + mPlayState = PlayState::Finished; + mPendingActionUponFinish = Action::Reset; + break; + case Request::Cancel: + mPlayState = PlayState::Finished; + mPendingActionUponFinish = Action::None; + break; + case Request::End: + mPlayTime = mPlayState == PlayState::Reversing ? 0 : mDuration; + mPlayState = PlayState::Finished; + mPendingActionUponFinish = Action::End; + break; + default: + LOG_ALWAYS_FATAL("Invalid staging request: %d", static_cast<int>(request)); }; } @@ -182,8 +182,8 @@ void BaseRenderNodeAnimator::pushStaging(AnimationContext& context) { if (mStagingPlayState == PlayState::Finished) { callOnFinishedListener(context); - } else if (mStagingPlayState == PlayState::Running - || mStagingPlayState == PlayState::Reversing) { + } else if (mStagingPlayState == PlayState::Running || + mStagingPlayState == PlayState::Reversing) { bool changed = currentPlayTime != mPlayTime || prevFramePlayState != mStagingPlayState; if (prevFramePlayState != mStagingPlayState) { transitionToRunning(context); @@ -197,7 +197,7 @@ void BaseRenderNodeAnimator::pushStaging(AnimationContext& context) { if (mPlayState == PlayState::Reversing) { // Reverse is not supported for animations with a start delay, so here we // assume no start delay. - mStartTime = currentFrameTime - (mDuration - mPlayTime); + mStartTime = currentFrameTime - (mDuration - mPlayTime); } else { // Animation should play forward if (mPlayTime == 0) { @@ -223,9 +223,9 @@ void BaseRenderNodeAnimator::transitionToRunning(AnimationContext& context) { } mStartTime = frameTimeMs + mStartDelay; if (mStartTime < 0) { - ALOGW("Ended up with a really weird start time of %" PRId64 - " with frame time %" PRId64 " and start delay %" PRId64, - mStartTime, frameTimeMs, mStartDelay); + ALOGW("Ended up with a really weird start time of %" PRId64 " with frame time %" PRId64 + " and start delay %" PRId64, + mStartTime, frameTimeMs, mStartDelay); // Set to 0 so that the animate() basically instantly finishes mStartTime = 0; } @@ -247,7 +247,7 @@ bool BaseRenderNodeAnimator::animate(AnimationContext& context) { updatePlayTime(mDuration); } // Reset pending action. - mPendingActionUponFinish = Action ::None; + mPendingActionUponFinish = Action::None; return true; } @@ -276,7 +276,7 @@ bool BaseRenderNodeAnimator::updatePlayTime(nsecs_t playTime) { float fraction = 1.0f; if ((mPlayState == PlayState::Running || mPlayState == PlayState::Reversing) && mDuration > 0) { - fraction = mPlayTime / (float) mDuration; + fraction = mPlayTime / (float)mDuration; } fraction = MathUtils::clamp(fraction, 0.0f, 1.0f); @@ -308,35 +308,35 @@ void BaseRenderNodeAnimator::callOnFinishedListener(AnimationContext& context) { ************************************************************/ struct RenderPropertyAnimator::PropertyAccessors { - RenderNode::DirtyPropertyMask dirtyMask; - GetFloatProperty getter; - SetFloatProperty setter; + RenderNode::DirtyPropertyMask dirtyMask; + GetFloatProperty getter; + SetFloatProperty setter; }; // Maps RenderProperty enum to accessors const RenderPropertyAnimator::PropertyAccessors RenderPropertyAnimator::PROPERTY_ACCESSOR_LUT[] = { - {RenderNode::TRANSLATION_X, &RenderProperties::getTranslationX, &RenderProperties::setTranslationX }, - {RenderNode::TRANSLATION_Y, &RenderProperties::getTranslationY, &RenderProperties::setTranslationY }, - {RenderNode::TRANSLATION_Z, &RenderProperties::getTranslationZ, &RenderProperties::setTranslationZ }, - {RenderNode::SCALE_X, &RenderProperties::getScaleX, &RenderProperties::setScaleX }, - {RenderNode::SCALE_Y, &RenderProperties::getScaleY, &RenderProperties::setScaleY }, - {RenderNode::ROTATION, &RenderProperties::getRotation, &RenderProperties::setRotation }, - {RenderNode::ROTATION_X, &RenderProperties::getRotationX, &RenderProperties::setRotationX }, - {RenderNode::ROTATION_Y, &RenderProperties::getRotationY, &RenderProperties::setRotationY }, - {RenderNode::X, &RenderProperties::getX, &RenderProperties::setX }, - {RenderNode::Y, &RenderProperties::getY, &RenderProperties::setY }, - {RenderNode::Z, &RenderProperties::getZ, &RenderProperties::setZ }, - {RenderNode::ALPHA, &RenderProperties::getAlpha, &RenderProperties::setAlpha }, + {RenderNode::TRANSLATION_X, &RenderProperties::getTranslationX, + &RenderProperties::setTranslationX}, + {RenderNode::TRANSLATION_Y, &RenderProperties::getTranslationY, + &RenderProperties::setTranslationY}, + {RenderNode::TRANSLATION_Z, &RenderProperties::getTranslationZ, + &RenderProperties::setTranslationZ}, + {RenderNode::SCALE_X, &RenderProperties::getScaleX, &RenderProperties::setScaleX}, + {RenderNode::SCALE_Y, &RenderProperties::getScaleY, &RenderProperties::setScaleY}, + {RenderNode::ROTATION, &RenderProperties::getRotation, &RenderProperties::setRotation}, + {RenderNode::ROTATION_X, &RenderProperties::getRotationX, &RenderProperties::setRotationX}, + {RenderNode::ROTATION_Y, &RenderProperties::getRotationY, &RenderProperties::setRotationY}, + {RenderNode::X, &RenderProperties::getX, &RenderProperties::setX}, + {RenderNode::Y, &RenderProperties::getY, &RenderProperties::setY}, + {RenderNode::Z, &RenderProperties::getZ, &RenderProperties::setZ}, + {RenderNode::ALPHA, &RenderProperties::getAlpha, &RenderProperties::setAlpha}, }; RenderPropertyAnimator::RenderPropertyAnimator(RenderProperty property, float finalValue) - : BaseRenderNodeAnimator(finalValue) - , mPropertyAccess(&(PROPERTY_ACCESSOR_LUT[property])) { -} + : BaseRenderNodeAnimator(finalValue), mPropertyAccess(&(PROPERTY_ACCESSOR_LUT[property])) {} void RenderPropertyAnimator::onAttached() { - if (!mHasStartValue - && mStagingTarget->isPropertyFieldDirty(mPropertyAccess->dirtyMask)) { + if (!mHasStartValue && mStagingTarget->isPropertyFieldDirty(mPropertyAccess->dirtyMask)) { setStartValue((mStagingTarget->stagingProperties().*mPropertyAccess->getter)()); } } @@ -385,11 +385,9 @@ void RenderPropertyAnimator::setValue(RenderNode* target, float value) { * CanvasPropertyPrimitiveAnimator ************************************************************/ -CanvasPropertyPrimitiveAnimator::CanvasPropertyPrimitiveAnimator( - CanvasPropertyPrimitive* property, float finalValue) - : BaseRenderNodeAnimator(finalValue) - , mProperty(property) { -} +CanvasPropertyPrimitiveAnimator::CanvasPropertyPrimitiveAnimator(CanvasPropertyPrimitive* property, + float finalValue) + : BaseRenderNodeAnimator(finalValue), mProperty(property) {} float CanvasPropertyPrimitiveAnimator::getValue(RenderNode* target) const { return mProperty->value; @@ -407,50 +405,44 @@ uint32_t CanvasPropertyPrimitiveAnimator::dirtyMask() { * CanvasPropertySkPaintAnimator ************************************************************/ -CanvasPropertyPaintAnimator::CanvasPropertyPaintAnimator( - CanvasPropertyPaint* property, PaintField field, float finalValue) - : BaseRenderNodeAnimator(finalValue) - , mProperty(property) - , mField(field) { -} +CanvasPropertyPaintAnimator::CanvasPropertyPaintAnimator(CanvasPropertyPaint* property, + PaintField field, float finalValue) + : BaseRenderNodeAnimator(finalValue), mProperty(property), mField(field) {} float CanvasPropertyPaintAnimator::getValue(RenderNode* target) const { switch (mField) { - case STROKE_WIDTH: - return mProperty->value.getStrokeWidth(); - case ALPHA: - return mProperty->value.getAlpha(); + case STROKE_WIDTH: + return mProperty->value.getStrokeWidth(); + case ALPHA: + return mProperty->value.getAlpha(); } - LOG_ALWAYS_FATAL("Unknown field %d", (int) mField); + LOG_ALWAYS_FATAL("Unknown field %d", (int)mField); return -1; } static uint8_t to_uint8(float value) { - int c = (int) (value + .5f); - return static_cast<uint8_t>( c < 0 ? 0 : c > 255 ? 255 : c ); + int c = (int)(value + .5f); + return static_cast<uint8_t>(c < 0 ? 0 : c > 255 ? 255 : c); } void CanvasPropertyPaintAnimator::setValue(RenderNode* target, float value) { switch (mField) { - case STROKE_WIDTH: - mProperty->value.setStrokeWidth(value); - return; - case ALPHA: - mProperty->value.setAlpha(to_uint8(value)); - return; + case STROKE_WIDTH: + mProperty->value.setStrokeWidth(value); + return; + case ALPHA: + mProperty->value.setAlpha(to_uint8(value)); + return; } - LOG_ALWAYS_FATAL("Unknown field %d", (int) mField); + LOG_ALWAYS_FATAL("Unknown field %d", (int)mField); } uint32_t CanvasPropertyPaintAnimator::dirtyMask() { return RenderNode::DISPLAY_LIST; } -RevealAnimator::RevealAnimator(int centerX, int centerY, - float startValue, float finalValue) - : BaseRenderNodeAnimator(finalValue) - , mCenterX(centerX) - , mCenterY(centerY) { +RevealAnimator::RevealAnimator(int centerX, int centerY, float startValue, float finalValue) + : BaseRenderNodeAnimator(finalValue), mCenterX(centerX), mCenterY(centerY) { setStartValue(startValue); } @@ -459,8 +451,7 @@ float RevealAnimator::getValue(RenderNode* target) const { } void RevealAnimator::setValue(RenderNode* target, float value) { - target->animatorProperties().mutableRevealClip().set(true, - mCenterX, mCenterY, value); + target->animatorProperties().mutableRevealClip().set(true, mCenterX, mCenterY, value); } uint32_t RevealAnimator::dirtyMask() { diff --git a/libs/hwui/Animator.h b/libs/hwui/Animator.h index aac355c11ac9..42f4cf8828bd 100644 --- a/libs/hwui/Animator.h +++ b/libs/hwui/Animator.h @@ -16,11 +16,11 @@ #ifndef ANIMATOR_H #define ANIMATOR_H -#include <memory> #include <cutils/compiler.h> #include <utils/RefBase.h> #include <utils/StrongPointer.h> #include <utils/Timers.h> +#include <memory> #include "utils/Macros.h" @@ -40,6 +40,7 @@ class RenderProperties; class AnimationListener : public VirtualLightRefBase { public: ANDROID_API virtual void onAnimationFinished(BaseRenderNodeAnimator*) = 0; + protected: ANDROID_API virtual ~AnimationListener() {} }; @@ -52,6 +53,7 @@ enum class RepeatMode { class BaseRenderNodeAnimator : public VirtualLightRefBase { PREVENT_COPY_AND_ASSIGN(BaseRenderNodeAnimator); + public: ANDROID_API void setStartValue(float value); ANDROID_API void setInterpolator(Interpolator* interpolator); @@ -59,13 +61,9 @@ public: ANDROID_API nsecs_t duration() { return mDuration; } ANDROID_API void setStartDelay(nsecs_t startDelayInMs); ANDROID_API nsecs_t startDelay() { return mStartDelay; } - ANDROID_API void setListener(AnimationListener* listener) { - mListener = listener; - } + ANDROID_API void setListener(AnimationListener* listener) { mListener = listener; } AnimationListener* listener() { return mListener.get(); } - ANDROID_API void setAllowRunningAsync(bool mayRunAsync) { - mMayRunAsync = mayRunAsync; - } + ANDROID_API void setAllowRunningAsync(bool mayRunAsync) { mMayRunAsync = mayRunAsync; } bool mayRunAsync() { return mMayRunAsync; } ANDROID_API void start(); ANDROID_API virtual void reset(); @@ -86,8 +84,9 @@ public: // an animation on RenderThread. ANDROID_API nsecs_t getRemainingPlayTime(); - bool isRunning() { return mPlayState == PlayState::Running - || mPlayState == PlayState::Reversing; } + bool isRunning() { + return mPlayState == PlayState::Running || mPlayState == PlayState::Reversing; + } bool isFinished() { return mPlayState == PlayState::Finished; } float finalValue() { return mFinalValue; } @@ -158,13 +157,7 @@ protected: sp<AnimationListener> mListener; private: - enum class Request { - Start, - Reverse, - Reset, - Cancel, - End - }; + enum class Request { Start, Reverse, Reset, Cancel, End }; // Defines different actions upon finish. enum class Action { @@ -229,13 +222,14 @@ private: class CanvasPropertyPrimitiveAnimator : public BaseRenderNodeAnimator { public: ANDROID_API CanvasPropertyPrimitiveAnimator(CanvasPropertyPrimitive* property, - float finalValue); + float finalValue); ANDROID_API virtual uint32_t dirtyMask(); protected: virtual float getValue(RenderNode* target) const override; virtual void setValue(RenderNode* target, float value) override; + private: sp<CanvasPropertyPrimitive> mProperty; }; @@ -247,14 +241,15 @@ public: ALPHA, }; - ANDROID_API CanvasPropertyPaintAnimator(CanvasPropertyPaint* property, - PaintField field, float finalValue); + ANDROID_API CanvasPropertyPaintAnimator(CanvasPropertyPaint* property, PaintField field, + float finalValue); ANDROID_API virtual uint32_t dirtyMask(); protected: virtual float getValue(RenderNode* target) const override; virtual void setValue(RenderNode* target, float value) override; + private: sp<CanvasPropertyPaint> mProperty; PaintField mField; @@ -262,8 +257,7 @@ private: class RevealAnimator : public BaseRenderNodeAnimator { public: - ANDROID_API RevealAnimator(int centerX, int centerY, - float startValue, float finalValue); + ANDROID_API RevealAnimator(int centerX, int centerY, float startValue, float finalValue); ANDROID_API virtual uint32_t dirtyMask(); diff --git a/libs/hwui/AnimatorManager.cpp b/libs/hwui/AnimatorManager.cpp index 69ead5890566..4826d5a0c8da 100644 --- a/libs/hwui/AnimatorManager.cpp +++ b/libs/hwui/AnimatorManager.cpp @@ -17,8 +17,8 @@ #include <algorithm> -#include "Animator.h" #include "AnimationContext.h" +#include "Animator.h" #include "DamageAccumulator.h" #include "RenderNode.h" @@ -31,10 +31,7 @@ static void detach(sp<BaseRenderNodeAnimator>& animator) { animator->detach(); } -AnimatorManager::AnimatorManager(RenderNode& parent) - : mParent(parent) - , mAnimationHandle(nullptr) { -} +AnimatorManager::AnimatorManager(RenderNode& parent) : mParent(parent), mAnimationHandle(nullptr) {} AnimatorManager::~AnimatorManager() { for_each(mNewAnimators.begin(), mNewAnimators.end(), detach); @@ -58,22 +55,22 @@ void AnimatorManager::addAnimator(const sp<BaseRenderNodeAnimator>& animator) { void AnimatorManager::removeAnimator(const sp<BaseRenderNodeAnimator>& animator) { mNewAnimators.erase(std::remove(mNewAnimators.begin(), mNewAnimators.end(), animator), - mNewAnimators.end()); + mNewAnimators.end()); } void AnimatorManager::setAnimationHandle(AnimationHandle* handle) { LOG_ALWAYS_FATAL_IF(mAnimationHandle && handle, "Already have an AnimationHandle!"); mAnimationHandle = handle; LOG_ALWAYS_FATAL_IF(!mAnimationHandle && mAnimators.size(), - "Lost animation handle on %p (%s) with outstanding animators!", - &mParent, mParent.getName()); + "Lost animation handle on %p (%s) with outstanding animators!", &mParent, + mParent.getName()); } void AnimatorManager::pushStaging() { if (mNewAnimators.size()) { if (CC_UNLIKELY(!mAnimationHandle)) { - ALOGW("Trying to start new animators on %p (%s) without an animation handle!", - &mParent, mParent.getName()); + ALOGW("Trying to start new animators on %p (%s) without an animation handle!", &mParent, + mParent.getName()); return; } @@ -100,7 +97,7 @@ public: AnimateFunctor(TreeInfo& info, AnimationContext& context, uint32_t* outDirtyMask) : mInfo(info), mContext(context), mDirtyMask(outDirtyMask) {} - bool operator() (sp<BaseRenderNodeAnimator>& animator) { + bool operator()(sp<BaseRenderNodeAnimator>& animator) { *mDirtyMask |= animator->dirtyMask(); bool remove = animator->animate(mContext); if (remove) { @@ -172,17 +169,15 @@ class EndActiveAnimatorsFunctor { public: explicit EndActiveAnimatorsFunctor(AnimationContext& context) : mContext(context) {} - void operator() (sp<BaseRenderNodeAnimator>& animator) { - animator->forceEndNow(mContext); - } + void operator()(sp<BaseRenderNodeAnimator>& animator) { animator->forceEndNow(mContext); } private: AnimationContext& mContext; }; void AnimatorManager::endAllActiveAnimators() { - ALOGD("endAllActiveAnimators on %p (%s) with handle %p", - &mParent, mParent.getName(), mAnimationHandle); + ALOGD("endAllActiveAnimators on %p (%s) with handle %p", &mParent, mParent.getName(), + mAnimationHandle); EndActiveAnimatorsFunctor functor(mAnimationHandle->context()); for_each(mAnimators.begin(), mAnimators.end(), functor); mAnimators.clear(); diff --git a/libs/hwui/AnimatorManager.h b/libs/hwui/AnimatorManager.h index 8e6f820608b5..9575391a8b3f 100644 --- a/libs/hwui/AnimatorManager.h +++ b/libs/hwui/AnimatorManager.h @@ -34,6 +34,7 @@ class TreeInfo; // Responsible for managing the animators for a single RenderNode class AnimatorManager { PREVENT_COPY_AND_ASSIGN(AnimatorManager); + public: explicit AnimatorManager(RenderNode& parent); ~AnimatorManager(); @@ -68,8 +69,8 @@ private: AnimationHandle* mAnimationHandle; // To improve the efficiency of resizing & removing from the vector - std::vector< sp<BaseRenderNodeAnimator> > mNewAnimators; - std::vector< sp<BaseRenderNodeAnimator> > mAnimators; + std::vector<sp<BaseRenderNodeAnimator> > mNewAnimators; + std::vector<sp<BaseRenderNodeAnimator> > mAnimators; }; } /* namespace uirenderer */ diff --git a/libs/hwui/BakedOpDispatcher.cpp b/libs/hwui/BakedOpDispatcher.cpp index 03a397c1084d..f78cb418c5cd 100644 --- a/libs/hwui/BakedOpDispatcher.cpp +++ b/libs/hwui/BakedOpDispatcher.cpp @@ -23,29 +23,28 @@ #include "GlopBuilder.h" #include "Patch.h" #include "PathTessellator.h" +#include "VertexBuffer.h" #include "renderstate/OffscreenBufferPool.h" #include "renderstate/RenderState.h" #include "utils/GLUtils.h" -#include "VertexBuffer.h" -#include <algorithm> -#include <math.h> #include <SkPaintDefaults.h> #include <SkPathOps.h> +#include <math.h> +#include <algorithm> namespace android { namespace uirenderer { static void storeTexturedRect(TextureVertex* vertices, const Rect& bounds) { - vertices[0] = { bounds.left, bounds.top, 0, 0 }; - vertices[1] = { bounds.right, bounds.top, 1, 0 }; - vertices[2] = { bounds.left, bounds.bottom, 0, 1 }; - vertices[3] = { bounds.right, bounds.bottom, 1, 1 }; + vertices[0] = {bounds.left, bounds.top, 0, 0}; + vertices[1] = {bounds.right, bounds.top, 1, 0}; + vertices[2] = {bounds.left, bounds.bottom, 0, 1}; + vertices[3] = {bounds.right, bounds.bottom, 1, 1}; } void BakedOpDispatcher::onMergedBitmapOps(BakedOpRenderer& renderer, - const MergedBakedOpList& opList) { - + const MergedBakedOpList& opList) { const BakedOpState& firstState = *(opList.states[0]); Bitmap* bitmap = (static_cast<const BitmapOp*>(opList.states[0]->op))->bitmap; @@ -70,7 +69,8 @@ void BakedOpDispatcher::onMergedBitmapOps(BakedOpRenderer& renderer, } const int textureFillFlags = (bitmap->colorType() == kAlpha_8_SkColorType) - ? TextureFillFlags::IsAlphaMaskTexture : TextureFillFlags::None; + ? TextureFillFlags::IsAlphaMaskTexture + : TextureFillFlags::None; Glop glop; GlopBuilder(renderer.renderState(), renderer.caches(), &glop) .setRoundRectClipState(firstState.roundRectClipState) @@ -85,7 +85,7 @@ void BakedOpDispatcher::onMergedBitmapOps(BakedOpRenderer& renderer, } void BakedOpDispatcher::onMergedPatchOps(BakedOpRenderer& renderer, - const MergedBakedOpList& opList) { + const MergedBakedOpList& opList) { const PatchOp& firstOp = *(static_cast<const PatchOp*>(opList.states[0]->op)); const BakedOpState& firstState = *(opList.states[0]); @@ -99,8 +99,8 @@ void BakedOpDispatcher::onMergedPatchOps(BakedOpRenderer& renderer, // TODO: cache mesh lookups const Patch* opMesh = renderer.caches().patchCache.get( - op.bitmap->width(), op.bitmap->height(), - op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.patch); + op.bitmap->width(), op.bitmap->height(), op.unmappedBounds.getWidth(), + op.unmappedBounds.getHeight(), op.patch); totalVertices += opMesh->verticesCount; } @@ -120,9 +120,8 @@ void BakedOpDispatcher::onMergedPatchOps(BakedOpRenderer& renderer, // TODO: cache mesh lookups const Patch* opMesh = renderer.caches().patchCache.get( - op.bitmap->width(), op.bitmap->height(), - op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.patch); - + op.bitmap->width(), op.bitmap->height(), op.unmappedBounds.getWidth(), + op.unmappedBounds.getHeight(), op.patch); uint32_t vertexCount = opMesh->verticesCount; if (vertexCount == 0) continue; @@ -130,17 +129,16 @@ void BakedOpDispatcher::onMergedPatchOps(BakedOpRenderer& renderer, // We use the bounds to know where to translate our vertices // Using patchOp->state.mBounds wouldn't work because these // bounds are clipped - const float tx = floorf(state.computedState.transform.getTranslateX() - + op.unmappedBounds.left + 0.5f); - const float ty = floorf(state.computedState.transform.getTranslateY() - + op.unmappedBounds.top + 0.5f); + const float tx = floorf(state.computedState.transform.getTranslateX() + + op.unmappedBounds.left + 0.5f); + const float ty = floorf(state.computedState.transform.getTranslateY() + + op.unmappedBounds.top + 0.5f); // Copy & transform all the vertices for the current operation TextureVertex* opVertices = opMesh->vertices.get(); for (uint32_t j = 0; j < vertexCount; j++, opVertices++) { - TextureVertex::set(vertex++, - opVertices->x + tx, opVertices->y + ty, - opVertices->u, opVertices->v); + TextureVertex::set(vertex++, opVertices->x + tx, opVertices->y + ty, opVertices->u, + opVertices->v); } // Dirty the current layer if possible. When the 9-patch does not @@ -148,16 +146,16 @@ void BakedOpDispatcher::onMergedPatchOps(BakedOpRenderer& renderer, // dirty rect to the object's bounds. if (dirtyRenderTarget) { if (!opMesh->hasEmptyQuads) { - renderer.dirtyRenderTarget(Rect(tx, ty, - tx + op.unmappedBounds.getWidth(), ty + op.unmappedBounds.getHeight())); + renderer.dirtyRenderTarget(Rect(tx, ty, tx + op.unmappedBounds.getWidth(), + ty + op.unmappedBounds.getHeight())); } else { const size_t count = opMesh->quads.size(); for (size_t i = 0; i < count; i++) { const Rect& quadBounds = opMesh->quads[i]; const float x = tx + quadBounds.left; const float y = ty + quadBounds.top; - renderer.dirtyRenderTarget(Rect(x, y, - x + quadBounds.getWidth(), y + quadBounds.getHeight())); + renderer.dirtyRenderTarget( + Rect(x, y, x + quadBounds.getWidth(), y + quadBounds.getHeight())); } } } @@ -165,7 +163,6 @@ void BakedOpDispatcher::onMergedPatchOps(BakedOpRenderer& renderer, indexCount += opMesh->indexCount; } - Texture* texture = renderer.caches().textureCache.get(firstOp.bitmap); if (!texture) return; const AutoTexture autoCleanup(texture); @@ -188,8 +185,8 @@ void BakedOpDispatcher::onMergedPatchOps(BakedOpRenderer& renderer, renderer.renderGlop(nullptr, clip, glop); } -static void renderTextShadow(BakedOpRenderer& renderer, - const TextOp& op, const BakedOpState& textOpState) { +static void renderTextShadow(BakedOpRenderer& renderer, const TextOp& op, + const BakedOpState& textOpState) { if (CC_LIKELY(!PaintUtils::hasTextShadow(op.paint))) return; FontRenderer& fontRenderer = renderer.caches().fontRenderer.getFontRenderer(); @@ -225,7 +222,7 @@ static void renderTextShadow(BakedOpRenderer& renderer, // Bounds should be same as text op, but with dx/dy offset and radius outset // applied in local space. auto& transform = textOpState.computedState.transform; - Rect shadowBounds = op.unmappedBounds; // STROKE + Rect shadowBounds = op.unmappedBounds; // STROKE const bool expandForStroke = op.paint->getStyle() != SkPaint::kFill_Style; if (expandForStroke) { shadowBounds.outset(op.paint->getStrokeWidth() * 0.5f); @@ -234,13 +231,12 @@ static void renderTextShadow(BakedOpRenderer& renderer, shadowBounds.outset(textShadow.radius, textShadow.radius); transform.mapRect(shadowBounds); if (CC_UNLIKELY(expandForStroke && - (!transform.isPureTranslate() || op.paint->getStrokeWidth() < 1.0f))) { + (!transform.isPureTranslate() || op.paint->getStrokeWidth() < 1.0f))) { shadowBounds.outset(0.5f); } auto clipState = textOpState.computedState.clipState; - if (clipState->mode != ClipMode::Rectangle - || !clipState->rect.contains(shadowBounds)) { + if (clipState->mode != ClipMode::Rectangle || !clipState->rect.contains(shadowBounds)) { // need clip, so pass it and clip bounds shadowBounds.doIntersect(clipState->rect); } else { @@ -251,13 +247,10 @@ static void renderTextShadow(BakedOpRenderer& renderer, renderer.renderGlop(&shadowBounds, clipState, glop); } -enum class TextRenderType { - Defer, - Flush -}; +enum class TextRenderType { Defer, Flush }; static void renderText(BakedOpRenderer& renderer, const TextOp& op, const BakedOpState& state, - const ClipBase* renderClip, TextRenderType renderType) { + const ClipBase* renderClip, TextRenderType renderType) { FontRenderer& fontRenderer = renderer.caches().fontRenderer.getFontRenderer(); float x = op.x; float y = op.y; @@ -285,23 +278,23 @@ static void renderText(BakedOpRenderer& renderer, const TextOp& op, const BakedO // font renderer which greatly simplifies the code, clipping in particular. float sx, sy; transform.decomposeScale(sx, sy); - fontRenderer.setFont(op.paint, SkMatrix::MakeScale( - roundf(std::max(1.0f, sx)), - roundf(std::max(1.0f, sy)))); + fontRenderer.setFont(op.paint, SkMatrix::MakeScale(roundf(std::max(1.0f, sx)), + roundf(std::max(1.0f, sy)))); fontRenderer.setTextureFiltering(true); } Rect layerBounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f); int alpha = PaintUtils::getAlphaDirect(op.paint) * state.alpha; SkBlendMode mode = PaintUtils::getBlendModeDirect(op.paint); - TextDrawFunctor functor(&renderer, &state, renderClip, - x, y, pureTranslate, alpha, mode, op.paint); + TextDrawFunctor functor(&renderer, &state, renderClip, x, y, pureTranslate, alpha, mode, + op.paint); bool forceFinish = (renderType == TextRenderType::Flush); bool mustDirtyRenderTarget = renderer.offscreenRenderTarget(); const Rect* localOpClip = pureTranslate ? &state.computedState.clipRect() : nullptr; - fontRenderer.renderPosText(op.paint, localOpClip, op.glyphs, op.glyphCount, x, y, - op.positions, mustDirtyRenderTarget ? &layerBounds : nullptr, &functor, forceFinish); + fontRenderer.renderPosText(op.paint, localOpClip, op.glyphs, op.glyphCount, x, y, op.positions, + mustDirtyRenderTarget ? &layerBounds : nullptr, &functor, + forceFinish); if (mustDirtyRenderTarget) { if (!pureTranslate) { @@ -312,7 +305,7 @@ static void renderText(BakedOpRenderer& renderer, const TextOp& op, const BakedO } void BakedOpDispatcher::onMergedTextOps(BakedOpRenderer& renderer, - const MergedBakedOpList& opList) { + const MergedBakedOpList& opList) { for (size_t i = 0; i < opList.count; i++) { const BakedOpState& state = *(opList.states[i]); const TextOp& op = *(static_cast<const TextOp*>(state.op)); @@ -324,26 +317,27 @@ void BakedOpDispatcher::onMergedTextOps(BakedOpRenderer& renderer, for (size_t i = 0; i < opList.count; i++) { const BakedOpState& state = *(opList.states[i]); const TextOp& op = *(static_cast<const TextOp*>(state.op)); - TextRenderType renderType = (i + 1 == opList.count) - ? TextRenderType::Flush : TextRenderType::Defer; + TextRenderType renderType = + (i + 1 == opList.count) ? TextRenderType::Flush : TextRenderType::Defer; renderText(renderer, op, state, clip, renderType); } } namespace VertexBufferRenderFlags { - enum { - Offset = 0x1, - ShadowInterp = 0x2, - }; +enum { + Offset = 0x1, + ShadowInterp = 0x2, +}; } static void renderVertexBuffer(BakedOpRenderer& renderer, const BakedOpState& state, - const VertexBuffer& vertexBuffer, float translateX, float translateY, - const SkPaint& paint, int vertexBufferRenderFlags) { + const VertexBuffer& vertexBuffer, float translateX, float translateY, + const SkPaint& paint, int vertexBufferRenderFlags) { if (CC_LIKELY(vertexBuffer.getVertexCount())) { bool shadowInterp = vertexBufferRenderFlags & VertexBufferRenderFlags::ShadowInterp; const int transformFlags = vertexBufferRenderFlags & VertexBufferRenderFlags::Offset - ? TransformFlags::OffsetByFudgeFactor : 0; + ? TransformFlags::OffsetByFudgeFactor + : 0; Glop glop; GlopBuilder(renderer.renderState(), renderer.caches(), &glop) @@ -358,24 +352,23 @@ static void renderVertexBuffer(BakedOpRenderer& renderer, const BakedOpState& st } static void renderConvexPath(BakedOpRenderer& renderer, const BakedOpState& state, - const SkPath& path, const SkPaint& paint) { + const SkPath& path, const SkPaint& paint) { VertexBuffer vertexBuffer; // TODO: try clipping large paths to viewport PathTessellator::tessellatePath(path, &paint, state.computedState.transform, vertexBuffer); renderVertexBuffer(renderer, state, vertexBuffer, 0.0f, 0.0f, paint, 0); } -static void renderPathTexture(BakedOpRenderer& renderer, const BakedOpState& state, - float xOffset, float yOffset, PathTexture& texture, const SkPaint& paint) { +static void renderPathTexture(BakedOpRenderer& renderer, const BakedOpState& state, float xOffset, + float yOffset, PathTexture& texture, const SkPaint& paint) { Rect dest(texture.width(), texture.height()); - dest.translate(xOffset + texture.left - texture.offset, - yOffset + texture.top - texture.offset); + dest.translate(xOffset + texture.left - texture.offset, yOffset + texture.top - texture.offset); Glop glop; GlopBuilder(renderer.renderState(), renderer.caches(), &glop) .setRoundRectClipState(state.roundRectClipState) .setMeshTexturedUnitQuad(nullptr) .setFillPathTexturePaint(texture, paint, state.alpha) - .setTransform(state.computedState.transform, TransformFlags::None) + .setTransform(state.computedState.transform, TransformFlags::None) .setModelViewMapUnitToRect(dest) .build(); renderer.renderGlop(state, glop); @@ -390,18 +383,18 @@ SkRect getBoundsOfFill(const RecordedOp& op) { return bounds; } -void BakedOpDispatcher::onArcOp(BakedOpRenderer& renderer, const ArcOp& op, const BakedOpState& state) { +void BakedOpDispatcher::onArcOp(BakedOpRenderer& renderer, const ArcOp& op, + const BakedOpState& state) { // TODO: support fills (accounting for concavity if useCenter && sweepAngle > 180) - if (op.paint->getStyle() != SkPaint::kStroke_Style - || op.paint->getPathEffect() != nullptr - || op.useCenter) { + if (op.paint->getStyle() != SkPaint::kStroke_Style || op.paint->getPathEffect() != nullptr || + op.useCenter) { PathTexture* texture = renderer.caches().pathCache.getArc( - op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), - op.startAngle, op.sweepAngle, op.useCenter, op.paint); + op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.startAngle, + op.sweepAngle, op.useCenter, op.paint); const AutoTexture holder(texture); if (CC_LIKELY(holder.texture)) { renderPathTexture(renderer, state, op.unmappedBounds.left, op.unmappedBounds.top, - *texture, *(op.paint)); + *texture, *(op.paint)); } } else { SkRect rect = getBoundsOfFill(op); @@ -417,13 +410,15 @@ void BakedOpDispatcher::onArcOp(BakedOpRenderer& renderer, const ArcOp& op, cons } } -void BakedOpDispatcher::onBitmapOp(BakedOpRenderer& renderer, const BitmapOp& op, const BakedOpState& state) { +void BakedOpDispatcher::onBitmapOp(BakedOpRenderer& renderer, const BitmapOp& op, + const BakedOpState& state) { Texture* texture = renderer.getTexture(op.bitmap); if (!texture) return; const AutoTexture autoCleanup(texture); const int textureFillFlags = (op.bitmap->colorType() == kAlpha_8_SkColorType) - ? TextureFillFlags::IsAlphaMaskTexture : TextureFillFlags::None; + ? TextureFillFlags::IsAlphaMaskTexture + : TextureFillFlags::None; Glop glop; GlopBuilder(renderer.renderState(), renderer.caches(), &glop) .setRoundRectClipState(state.roundRectClipState) @@ -435,7 +430,8 @@ void BakedOpDispatcher::onBitmapOp(BakedOpRenderer& renderer, const BitmapOp& op renderer.renderGlop(state, glop); } -void BakedOpDispatcher::onBitmapMeshOp(BakedOpRenderer& renderer, const BitmapMeshOp& op, const BakedOpState& state) { +void BakedOpDispatcher::onBitmapMeshOp(BakedOpRenderer& renderer, const BitmapMeshOp& op, + const BakedOpState& state) { Texture* texture = renderer.caches().textureCache.get(op.bitmap); if (!texture) { return; @@ -495,13 +491,14 @@ void BakedOpDispatcher::onBitmapMeshOp(BakedOpRenderer& renderer, const BitmapMe .setRoundRectClipState(state.roundRectClipState) .setMeshColoredTexturedMesh(mesh.get(), elementCount) .setFillTexturePaint(*texture, textureFillFlags, op.paint, state.alpha) - .setTransform(state.computedState.transform, TransformFlags::None) + .setTransform(state.computedState.transform, TransformFlags::None) .setModelViewOffsetRect(0, 0, op.unmappedBounds) .build(); renderer.renderGlop(state, glop); } -void BakedOpDispatcher::onBitmapRectOp(BakedOpRenderer& renderer, const BitmapRectOp& op, const BakedOpState& state) { +void BakedOpDispatcher::onBitmapRectOp(BakedOpRenderer& renderer, const BitmapRectOp& op, + const BakedOpState& state) { Texture* texture = renderer.getTexture(op.bitmap); if (!texture) return; const AutoTexture autoCleanup(texture); @@ -512,9 +509,10 @@ void BakedOpDispatcher::onBitmapRectOp(BakedOpRenderer& renderer, const BitmapRe std::min(1.0f, op.src.bottom / texture->height())); const int textureFillFlags = (op.bitmap->colorType() == kAlpha_8_SkColorType) - ? TextureFillFlags::IsAlphaMaskTexture : TextureFillFlags::None; - const bool tryToSnap = MathUtils::areEqual(op.src.getWidth(), op.unmappedBounds.getWidth()) - && MathUtils::areEqual(op.src.getHeight(), op.unmappedBounds.getHeight()); + ? TextureFillFlags::IsAlphaMaskTexture + : TextureFillFlags::None; + const bool tryToSnap = MathUtils::areEqual(op.src.getWidth(), op.unmappedBounds.getWidth()) && + MathUtils::areEqual(op.src.getHeight(), op.unmappedBounds.getHeight()); Glop glop; GlopBuilder(renderer.renderState(), renderer.caches(), &glop) .setRoundRectClipState(state.roundRectClipState) @@ -526,7 +524,8 @@ void BakedOpDispatcher::onBitmapRectOp(BakedOpRenderer& renderer, const BitmapRe renderer.renderGlop(state, glop); } -void BakedOpDispatcher::onColorOp(BakedOpRenderer& renderer, const ColorOp& op, const BakedOpState& state) { +void BakedOpDispatcher::onColorOp(BakedOpRenderer& renderer, const ColorOp& op, + const BakedOpState& state) { SkPaint paint; paint.setColor(op.color); paint.setBlendMode(op.mode); @@ -542,26 +541,29 @@ void BakedOpDispatcher::onColorOp(BakedOpRenderer& renderer, const ColorOp& op, renderer.renderGlop(state, glop); } -void BakedOpDispatcher::onFunctorOp(BakedOpRenderer& renderer, const FunctorOp& op, const BakedOpState& state) { +void BakedOpDispatcher::onFunctorOp(BakedOpRenderer& renderer, const FunctorOp& op, + const BakedOpState& state) { renderer.renderFunctor(op, state); } -void BakedOpDispatcher::onLinesOp(BakedOpRenderer& renderer, const LinesOp& op, const BakedOpState& state) { +void BakedOpDispatcher::onLinesOp(BakedOpRenderer& renderer, const LinesOp& op, + const BakedOpState& state) { VertexBuffer buffer; PathTessellator::tessellateLines(op.points, op.floatCount, op.paint, - state.computedState.transform, buffer); + state.computedState.transform, buffer); int displayFlags = op.paint->isAntiAlias() ? 0 : VertexBufferRenderFlags::Offset; renderVertexBuffer(renderer, state, buffer, 0, 0, *(op.paint), displayFlags); } -void BakedOpDispatcher::onOvalOp(BakedOpRenderer& renderer, const OvalOp& op, const BakedOpState& state) { +void BakedOpDispatcher::onOvalOp(BakedOpRenderer& renderer, const OvalOp& op, + const BakedOpState& state) { if (op.paint->getPathEffect() != nullptr) { PathTexture* texture = renderer.caches().pathCache.getOval( op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.paint); const AutoTexture holder(texture); if (CC_LIKELY(holder.texture)) { renderPathTexture(renderer, state, op.unmappedBounds.left, op.unmappedBounds.top, - *texture, *(op.paint)); + *texture, *(op.paint)); } } else { SkPath path; @@ -577,7 +579,8 @@ void BakedOpDispatcher::onOvalOp(BakedOpRenderer& renderer, const OvalOp& op, co } } -void BakedOpDispatcher::onPatchOp(BakedOpRenderer& renderer, const PatchOp& op, const BakedOpState& state) { +void BakedOpDispatcher::onPatchOp(BakedOpRenderer& renderer, const PatchOp& op, + const BakedOpState& state) { // 9 patches are built for stretching - always filter int textureFillFlags = TextureFillFlags::ForceFilter; if (op.bitmap->colorType() == kAlpha_8_SkColorType) { @@ -585,9 +588,9 @@ void BakedOpDispatcher::onPatchOp(BakedOpRenderer& renderer, const PatchOp& op, } // TODO: avoid redoing the below work each frame: - const Patch* mesh = renderer.caches().patchCache.get( - op.bitmap->width(), op.bitmap->height(), - op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.patch); + const Patch* mesh = renderer.caches().patchCache.get(op.bitmap->width(), op.bitmap->height(), + op.unmappedBounds.getWidth(), + op.unmappedBounds.getHeight(), op.patch); Texture* texture = renderer.caches().textureCache.get(op.bitmap); if (CC_LIKELY(texture)) { @@ -598,14 +601,16 @@ void BakedOpDispatcher::onPatchOp(BakedOpRenderer& renderer, const PatchOp& op, .setMeshPatchQuads(*mesh) .setFillTexturePaint(*texture, textureFillFlags, op.paint, state.alpha) .setTransform(state.computedState.transform, TransformFlags::None) - .setModelViewOffsetRectSnap(op.unmappedBounds.left, op.unmappedBounds.top, + .setModelViewOffsetRectSnap( + op.unmappedBounds.left, op.unmappedBounds.top, Rect(op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight())) .build(); renderer.renderGlop(state, glop); } } -void BakedOpDispatcher::onPathOp(BakedOpRenderer& renderer, const PathOp& op, const BakedOpState& state) { +void BakedOpDispatcher::onPathOp(BakedOpRenderer& renderer, const PathOp& op, + const BakedOpState& state) { PathTexture* texture = renderer.caches().pathCache.get(op.path, op.paint); const AutoTexture holder(texture); if (CC_LIKELY(holder.texture)) { @@ -615,10 +620,11 @@ void BakedOpDispatcher::onPathOp(BakedOpRenderer& renderer, const PathOp& op, co } } -void BakedOpDispatcher::onPointsOp(BakedOpRenderer& renderer, const PointsOp& op, const BakedOpState& state) { +void BakedOpDispatcher::onPointsOp(BakedOpRenderer& renderer, const PointsOp& op, + const BakedOpState& state) { VertexBuffer buffer; PathTessellator::tessellatePoints(op.points, op.floatCount, op.paint, - state.computedState.transform, buffer); + state.computedState.transform, buffer); int displayFlags = op.paint->isAntiAlias() ? 0 : VertexBufferRenderFlags::Offset; renderVertexBuffer(renderer, state, buffer, 0, 0, *(op.paint), displayFlags); } @@ -626,20 +632,21 @@ void BakedOpDispatcher::onPointsOp(BakedOpRenderer& renderer, const PointsOp& op // See SkPaintDefaults.h #define SkPaintDefaults_MiterLimit SkIntToScalar(4) -void BakedOpDispatcher::onRectOp(BakedOpRenderer& renderer, const RectOp& op, const BakedOpState& state) { +void BakedOpDispatcher::onRectOp(BakedOpRenderer& renderer, const RectOp& op, + const BakedOpState& state) { if (op.paint->getStyle() != SkPaint::kFill_Style) { // only fill + default miter is supported by drawConvexPath, since others must handle joins static_assert(SkPaintDefaults_MiterLimit == 4.0f, "Miter limit has changed"); - if (CC_UNLIKELY(op.paint->getPathEffect() != nullptr - || op.paint->getStrokeJoin() != SkPaint::kMiter_Join - || op.paint->getStrokeMiter() != SkPaintDefaults_MiterLimit)) { - PathTexture* texture = renderer.caches().pathCache.getRect( - op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.paint); - const AutoTexture holder(texture); - if (CC_LIKELY(holder.texture)) { - renderPathTexture(renderer, state, op.unmappedBounds.left, op.unmappedBounds.top, - *texture, *(op.paint)); - } + if (CC_UNLIKELY(op.paint->getPathEffect() != nullptr || + op.paint->getStrokeJoin() != SkPaint::kMiter_Join || + op.paint->getStrokeMiter() != SkPaintDefaults_MiterLimit)) { + PathTexture* texture = renderer.caches().pathCache.getRect( + op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.paint); + const AutoTexture holder(texture); + if (CC_LIKELY(holder.texture)) { + renderPathTexture(renderer, state, op.unmappedBounds.left, op.unmappedBounds.top, + *texture, *(op.paint)); + } } else { SkPath path; path.addRect(getBoundsOfFill(op)); @@ -665,29 +672,31 @@ void BakedOpDispatcher::onRectOp(BakedOpRenderer& renderer, const RectOp& op, co } } -void BakedOpDispatcher::onRoundRectOp(BakedOpRenderer& renderer, const RoundRectOp& op, const BakedOpState& state) { +void BakedOpDispatcher::onRoundRectOp(BakedOpRenderer& renderer, const RoundRectOp& op, + const BakedOpState& state) { if (op.paint->getPathEffect() != nullptr) { PathTexture* texture = renderer.caches().pathCache.getRoundRect( - op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), - op.rx, op.ry, op.paint); + op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.rx, op.ry, + op.paint); const AutoTexture holder(texture); if (CC_LIKELY(holder.texture)) { renderPathTexture(renderer, state, op.unmappedBounds.left, op.unmappedBounds.top, - *texture, *(op.paint)); + *texture, *(op.paint)); } } else { const VertexBuffer* buffer = renderer.caches().tessellationCache.getRoundRect( - state.computedState.transform, *(op.paint), - op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.rx, op.ry); - renderVertexBuffer(renderer, state, *buffer, - op.unmappedBounds.left, op.unmappedBounds.top, *(op.paint), 0); + state.computedState.transform, *(op.paint), op.unmappedBounds.getWidth(), + op.unmappedBounds.getHeight(), op.rx, op.ry); + renderVertexBuffer(renderer, state, *buffer, op.unmappedBounds.left, op.unmappedBounds.top, + *(op.paint), 0); } } static void renderShadow(BakedOpRenderer& renderer, const BakedOpState& state, float casterAlpha, - const VertexBuffer* ambientShadowVertexBuffer, const VertexBuffer* spotShadowVertexBuffer) { + const VertexBuffer* ambientShadowVertexBuffer, + const VertexBuffer* spotShadowVertexBuffer) { SkPaint paint; - paint.setAntiAlias(true); // want to use AlphaVertex + paint.setAntiAlias(true); // want to use AlphaVertex // The caller has made sure casterAlpha > 0. uint8_t ambientShadowAlpha = renderer.getLightInfo().ambientShadowAlpha; @@ -696,8 +705,8 @@ static void renderShadow(BakedOpRenderer& renderer, const BakedOpState& state, f } if (ambientShadowVertexBuffer && ambientShadowAlpha > 0) { paint.setAlpha((uint8_t)(casterAlpha * ambientShadowAlpha)); - renderVertexBuffer(renderer, state, *ambientShadowVertexBuffer, 0, 0, - paint, VertexBufferRenderFlags::ShadowInterp); + renderVertexBuffer(renderer, state, *ambientShadowVertexBuffer, 0, 0, paint, + VertexBufferRenderFlags::ShadowInterp); } uint8_t spotShadowAlpha = renderer.getLightInfo().spotShadowAlpha; @@ -706,17 +715,19 @@ static void renderShadow(BakedOpRenderer& renderer, const BakedOpState& state, f } if (spotShadowVertexBuffer && spotShadowAlpha > 0) { paint.setAlpha((uint8_t)(casterAlpha * spotShadowAlpha)); - renderVertexBuffer(renderer, state, *spotShadowVertexBuffer, 0, 0, - paint, VertexBufferRenderFlags::ShadowInterp); + renderVertexBuffer(renderer, state, *spotShadowVertexBuffer, 0, 0, paint, + VertexBufferRenderFlags::ShadowInterp); } } -void BakedOpDispatcher::onShadowOp(BakedOpRenderer& renderer, const ShadowOp& op, const BakedOpState& state) { +void BakedOpDispatcher::onShadowOp(BakedOpRenderer& renderer, const ShadowOp& op, + const BakedOpState& state) { TessellationCache::vertexBuffer_pair_t buffers = op.shadowTask->getResult(); renderShadow(renderer, state, op.casterAlpha, buffers.first, buffers.second); } -void BakedOpDispatcher::onSimpleRectsOp(BakedOpRenderer& renderer, const SimpleRectsOp& op, const BakedOpState& state) { +void BakedOpDispatcher::onSimpleRectsOp(BakedOpRenderer& renderer, const SimpleRectsOp& op, + const BakedOpState& state) { Glop glop; GlopBuilder(renderer.renderState(), renderer.caches(), &glop) .setRoundRectClipState(state.roundRectClipState) @@ -728,12 +739,14 @@ void BakedOpDispatcher::onSimpleRectsOp(BakedOpRenderer& renderer, const SimpleR renderer.renderGlop(state, glop); } -void BakedOpDispatcher::onTextOp(BakedOpRenderer& renderer, const TextOp& op, const BakedOpState& state) { +void BakedOpDispatcher::onTextOp(BakedOpRenderer& renderer, const TextOp& op, + const BakedOpState& state) { renderTextShadow(renderer, op, state); renderText(renderer, op, state, state.computedState.getClipIfNeeded(), TextRenderType::Flush); } -void BakedOpDispatcher::onTextOnPathOp(BakedOpRenderer& renderer, const TextOnPathOp& op, const BakedOpState& state) { +void BakedOpDispatcher::onTextOnPathOp(BakedOpRenderer& renderer, const TextOnPathOp& op, + const BakedOpState& state) { // Note: can't trust clipSideFlags since we record with unmappedBounds == clip. // TODO: respect clipSideFlags, once we record with bounds auto renderTargetClip = state.computedState.clipState; @@ -746,14 +759,14 @@ void BakedOpDispatcher::onTextOnPathOp(BakedOpRenderer& renderer, const TextOnPa int alpha = PaintUtils::getAlphaDirect(op.paint) * state.alpha; SkBlendMode mode = PaintUtils::getBlendModeDirect(op.paint); - TextDrawFunctor functor(&renderer, &state, renderTargetClip, - 0.0f, 0.0f, false, alpha, mode, op.paint); + TextDrawFunctor functor(&renderer, &state, renderTargetClip, 0.0f, 0.0f, false, alpha, mode, + op.paint); bool mustDirtyRenderTarget = renderer.offscreenRenderTarget(); const Rect localSpaceClip = state.computedState.computeLocalSpaceClip(); - if (fontRenderer.renderTextOnPath(op.paint, &localSpaceClip, op.glyphs, op.glyphCount, - op.path, op.hOffset, op.vOffset, - mustDirtyRenderTarget ? &layerBounds : nullptr, &functor)) { + if (fontRenderer.renderTextOnPath(op.paint, &localSpaceClip, op.glyphs, op.glyphCount, op.path, + op.hOffset, op.vOffset, + mustDirtyRenderTarget ? &layerBounds : nullptr, &functor)) { if (mustDirtyRenderTarget) { // manually dirty render target, since TextDrawFunctor won't state.computedState.transform.mapRect(layerBounds); @@ -762,7 +775,8 @@ void BakedOpDispatcher::onTextOnPathOp(BakedOpRenderer& renderer, const TextOnPa } } -void BakedOpDispatcher::onTextureLayerOp(BakedOpRenderer& renderer, const TextureLayerOp& op, const BakedOpState& state) { +void BakedOpDispatcher::onTextureLayerOp(BakedOpRenderer& renderer, const TextureLayerOp& op, + const BakedOpState& state) { GlLayer* layer = static_cast<GlLayer*>(op.layerHandle->backingLayer()); if (!layer) { return; @@ -772,16 +786,17 @@ void BakedOpDispatcher::onTextureLayerOp(BakedOpRenderer& renderer, const Textur Glop glop; GlopBuilder(renderer.renderState(), renderer.caches(), &glop) .setRoundRectClipState(state.roundRectClipState) - .setMeshTexturedUvQuad(nullptr, Rect(0, 1, 1, 0)) // TODO: simplify with VBO + .setMeshTexturedUvQuad(nullptr, Rect(0, 1, 1, 0)) // TODO: simplify with VBO .setFillTextureLayer(*(layer), alpha) .setTransform(state.computedState.transform, TransformFlags::None) - .setModelViewMapUnitToRectOptionalSnap(tryToSnap, Rect(layer->getWidth(), layer->getHeight())) + .setModelViewMapUnitToRectOptionalSnap(tryToSnap, + Rect(layer->getWidth(), layer->getHeight())) .build(); renderer.renderGlop(state, glop); } void renderRectForLayer(BakedOpRenderer& renderer, const LayerOp& op, const BakedOpState& state, - int color, SkBlendMode mode, SkColorFilter* colorFilter) { + int color, SkBlendMode mode, SkColorFilter* colorFilter) { SkPaint paint; paint.setColor(color); paint.setBlendMode(mode); @@ -790,7 +805,8 @@ void renderRectForLayer(BakedOpRenderer& renderer, const LayerOp& op, const Bake BakedOpDispatcher::onRectOp(renderer, rectOp, state); } -void BakedOpDispatcher::onLayerOp(BakedOpRenderer& renderer, const LayerOp& op, const BakedOpState& state) { +void BakedOpDispatcher::onLayerOp(BakedOpRenderer& renderer, const LayerOp& op, + const BakedOpState& state) { // Note that we don't use op->paint in this function - it's never set on a LayerOp OffscreenBuffer* buffer = *op.layerHandle; @@ -801,9 +817,11 @@ void BakedOpDispatcher::onLayerOp(BakedOpRenderer& renderer, const LayerOp& op, GlopBuilder(renderer.renderState(), renderer.caches(), &glop) .setRoundRectClipState(state.roundRectClipState) .setMeshTexturedIndexedVbo(buffer->vbo, buffer->elementCount) - .setFillLayer(buffer->texture, op.colorFilter, layerAlpha, op.mode, Blend::ModeOrderSwap::NoSwap) + .setFillLayer(buffer->texture, op.colorFilter, layerAlpha, op.mode, + Blend::ModeOrderSwap::NoSwap) .setTransform(state.computedState.transform, TransformFlags::None) - .setModelViewOffsetRectSnap(op.unmappedBounds.left, op.unmappedBounds.top, + .setModelViewOffsetRectSnap( + op.unmappedBounds.left, op.unmappedBounds.top, Rect(op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight())) .build(); renderer.renderGlop(state, glop); @@ -812,23 +830,24 @@ void BakedOpDispatcher::onLayerOp(BakedOpRenderer& renderer, const LayerOp& op, buffer->hasRenderedSinceRepaint = true; if (CC_UNLIKELY(Properties::debugLayersUpdates)) { // render debug layer highlight - renderRectForLayer(renderer, op, state, - 0x7f00ff00, SkBlendMode::kSrcOver, nullptr); + renderRectForLayer(renderer, op, state, 0x7f00ff00, SkBlendMode::kSrcOver, nullptr); } else if (CC_UNLIKELY(Properties::debugOverdraw)) { // render transparent to increment overdraw for repaint area - renderRectForLayer(renderer, op, state, - SK_ColorTRANSPARENT, SkBlendMode::kSrcOver, nullptr); + renderRectForLayer(renderer, op, state, SK_ColorTRANSPARENT, SkBlendMode::kSrcOver, + nullptr); } } } -void BakedOpDispatcher::onCopyToLayerOp(BakedOpRenderer& renderer, const CopyToLayerOp& op, const BakedOpState& state) { +void BakedOpDispatcher::onCopyToLayerOp(BakedOpRenderer& renderer, const CopyToLayerOp& op, + const BakedOpState& state) { LOG_ALWAYS_FATAL_IF(*(op.layerHandle) != nullptr, "layer already exists!"); *(op.layerHandle) = renderer.copyToLayer(state.computedState.clippedBounds); LOG_ALWAYS_FATAL_IF(*op.layerHandle == nullptr, "layer copy failed"); } -void BakedOpDispatcher::onCopyFromLayerOp(BakedOpRenderer& renderer, const CopyFromLayerOp& op, const BakedOpState& state) { +void BakedOpDispatcher::onCopyFromLayerOp(BakedOpRenderer& renderer, const CopyFromLayerOp& op, + const BakedOpState& state) { LOG_ALWAYS_FATAL_IF(*op.layerHandle == nullptr, "no layer to draw underneath!"); if (!state.computedState.clippedBounds.isEmpty()) { if (op.paint && op.paint->getAlpha() < 255) { @@ -836,7 +855,8 @@ void BakedOpDispatcher::onCopyFromLayerOp(BakedOpRenderer& renderer, const CopyF layerPaint.setAlpha(op.paint->getAlpha()); layerPaint.setBlendMode(SkBlendMode::kDstIn); layerPaint.setColorFilter(sk_ref_sp(op.paint->getColorFilter())); - RectOp rectOp(state.computedState.clippedBounds, Matrix4::identity(), nullptr, &layerPaint); + RectOp rectOp(state.computedState.clippedBounds, Matrix4::identity(), nullptr, + &layerPaint); BakedOpDispatcher::onRectOp(renderer, rectOp, state); } @@ -855,5 +875,5 @@ void BakedOpDispatcher::onCopyFromLayerOp(BakedOpRenderer& renderer, const CopyF renderer.renderState().layerPool().putOrDelete(*op.layerHandle); } -} // namespace uirenderer -} // namespace android +} // namespace uirenderer +} // namespace android diff --git a/libs/hwui/BakedOpDispatcher.h b/libs/hwui/BakedOpDispatcher.h index 4dfdd3ff619a..e3708685afc9 100644 --- a/libs/hwui/BakedOpDispatcher.h +++ b/libs/hwui/BakedOpDispatcher.h @@ -33,21 +33,20 @@ namespace uirenderer { */ class BakedOpDispatcher { public: - // Declares all "onMergedBitmapOps(...)" style methods for mergeable op types +// Declares all "onMergedBitmapOps(...)" style methods for mergeable op types #define X(Type) \ - static void onMerged##Type##s(BakedOpRenderer& renderer, const MergedBakedOpList& opList); + static void onMerged##Type##s(BakedOpRenderer& renderer, const MergedBakedOpList& opList); MAP_MERGEABLE_OPS(X) #undef X - // Declares all "onBitmapOp(...)" style methods for every op type +// Declares all "onBitmapOp(...)" style methods for every op type #define X(Type) \ - static void on##Type(BakedOpRenderer& renderer, const Type& op, const BakedOpState& state); + static void on##Type(BakedOpRenderer& renderer, const Type& op, const BakedOpState& state); MAP_RENDERABLE_OPS(X) #undef X - }; -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android -#endif // ANDROID_HWUI_BAKED_OP_DISPATCHER_H +#endif // ANDROID_HWUI_BAKED_OP_DISPATCHER_H diff --git a/libs/hwui/BakedOpRenderer.cpp b/libs/hwui/BakedOpRenderer.cpp index 3c3b3177159b..6b64b94d291e 100644 --- a/libs/hwui/BakedOpRenderer.cpp +++ b/libs/hwui/BakedOpRenderer.cpp @@ -19,10 +19,10 @@ #include "Caches.h" #include "Glop.h" #include "GlopBuilder.h" +#include "VertexBuffer.h" #include "renderstate/OffscreenBufferPool.h" #include "renderstate/RenderState.h" #include "utils/GLUtils.h" -#include "VertexBuffer.h" #include <algorithm> @@ -32,8 +32,8 @@ namespace uirenderer { OffscreenBuffer* BakedOpRenderer::startTemporaryLayer(uint32_t width, uint32_t height) { LOG_ALWAYS_FATAL_IF(mRenderTarget.offscreenBuffer, "already has layer..."); - OffscreenBuffer* buffer = mRenderState.layerPool().get( - mRenderState, width, height, mWideColorGamut); + OffscreenBuffer* buffer = + mRenderState.layerPool().get(mRenderState, width, height, mWideColorGamut); startRepaintLayer(buffer, Rect(width, height)); return buffer; } @@ -46,13 +46,13 @@ void BakedOpRenderer::startRepaintLayer(OffscreenBuffer* offscreenBuffer, const LOG_ALWAYS_FATAL_IF(mRenderTarget.offscreenBuffer, "already has layer..."); // subtract repaintRect from region, since it will be regenerated - if (repaintRect.contains(0, 0, - offscreenBuffer->viewportWidth, offscreenBuffer->viewportHeight)) { + if (repaintRect.contains(0, 0, offscreenBuffer->viewportWidth, + offscreenBuffer->viewportHeight)) { // repaint full layer, so throw away entire region offscreenBuffer->region.clear(); } else { offscreenBuffer->region.subtractSelf(android::Rect(repaintRect.left, repaintRect.top, - repaintRect.right, repaintRect.bottom)); + repaintRect.right, repaintRect.bottom)); } mRenderTarget.offscreenBuffer = offscreenBuffer; @@ -64,16 +64,14 @@ void BakedOpRenderer::startRepaintLayer(OffscreenBuffer* offscreenBuffer, const // attach the texture to the FBO glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, - offscreenBuffer->texture.id(), 0); + offscreenBuffer->texture.id(), 0); GL_CHECKPOINT(LOW); int status = glCheckFramebufferStatus(GL_FRAMEBUFFER); LOG_ALWAYS_FATAL_IF(status != GL_FRAMEBUFFER_COMPLETE, - "framebuffer incomplete, status %d, textureId %d, size %dx%d", - status, - offscreenBuffer->texture.id(), - offscreenBuffer->texture.width(), - offscreenBuffer->texture.height()); + "framebuffer incomplete, status %d, textureId %d, size %dx%d", status, + offscreenBuffer->texture.id(), offscreenBuffer->texture.width(), + offscreenBuffer->texture.height()); // Change the viewport & ortho projection setViewport(offscreenBuffer->viewportWidth, offscreenBuffer->viewportHeight); @@ -92,7 +90,7 @@ void BakedOpRenderer::endLayer() { mRenderTarget.lastStencilClip = nullptr; mRenderTarget.offscreenBuffer->updateMeshFromRegion(); - mRenderTarget.offscreenBuffer = nullptr; // It's in drawLayerOp's hands now. + mRenderTarget.offscreenBuffer = nullptr; // It's in drawLayerOp's hands now. // Detach the texture from the FBO glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); @@ -104,14 +102,14 @@ void BakedOpRenderer::endLayer() { OffscreenBuffer* BakedOpRenderer::copyToLayer(const Rect& area) { const uint32_t width = area.getWidth(); const uint32_t height = area.getHeight(); - OffscreenBuffer* buffer = mRenderState.layerPool().get( - mRenderState, width, height, mWideColorGamut); + OffscreenBuffer* buffer = + mRenderState.layerPool().get(mRenderState, width, height, mWideColorGamut); if (!area.isEmpty() && width != 0 && height != 0) { mCaches.textureState().activateTexture(0); mCaches.textureState().bindTexture(buffer->texture.id()); - glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, - area.left, mRenderTarget.viewportHeight - area.bottom, width, height); + glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, area.left, + mRenderTarget.viewportHeight - area.bottom, width, height); } return buffer; } @@ -177,7 +175,7 @@ void BakedOpRenderer::clearColorBuffer(const Rect& rect) { // Requested rect is subset of viewport - scissor to it to avoid over-clearing mRenderState.scissor().setEnabled(true); mRenderState.scissor().set(rect.left, mRenderTarget.viewportHeight - rect.bottom, - rect.getWidth(), rect.getHeight()); + rect.getWidth(), rect.getHeight()); } glClear(GL_COLOR_BUFFER_BIT); if (!mRenderTarget.frameBufferId) mHasDrawn = true; @@ -222,8 +220,7 @@ void BakedOpRenderer::drawRects(const float* rects, int count, const SkPaint* pa // clears and re-fills stencil with provided rendertarget space quads, // and then put stencil into test mode -void BakedOpRenderer::setupStencilQuads(std::vector<Vertex>& quadVertices, - int incrementThreshold) { +void BakedOpRenderer::setupStencilQuads(std::vector<Vertex>& quadVertices, int incrementThreshold) { mRenderState.stencil().enableWrite(incrementThreshold); mRenderState.stencil().clear(); Glop glop; @@ -239,7 +236,8 @@ void BakedOpRenderer::setupStencilQuads(std::vector<Vertex>& quadVertices, } void BakedOpRenderer::setupStencilRectList(const ClipBase* clip) { - LOG_ALWAYS_FATAL_IF(clip->mode != ClipMode::RectangleList, "can't rectlist clip without rectlist"); + LOG_ALWAYS_FATAL_IF(clip->mode != ClipMode::RectangleList, + "can't rectlist clip without rectlist"); auto&& rectList = reinterpret_cast<const ClipRectList*>(clip)->rectList; int quadCount = rectList.getTransformedRectanglesCount(); std::vector<Vertex> rectangleVertices; @@ -253,7 +251,7 @@ void BakedOpRenderer::setupStencilRectList(const ClipBase* clip) { transform.mapRect(bounds); bounds.doIntersect(clip->rect); if (bounds.isEmpty()) { - continue; // will be outside of scissor, skip + continue; // will be outside of scissor, skip } } @@ -309,11 +307,11 @@ void BakedOpRenderer::prepareRender(const Rect* dirtyBounds, const ClipBase* cli if (mRenderTarget.frameBufferId != 0 && !mRenderTarget.stencil) { OffscreenBuffer* layer = mRenderTarget.offscreenBuffer; mRenderTarget.stencil = mCaches.renderBufferCache.get( - Stencil::getLayerStencilFormat(), - layer->texture.width(), layer->texture.height()); + Stencil::getLayerStencilFormat(), layer->texture.width(), + layer->texture.height()); // stencil is bound + allocated - associate it with current FBO glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, - GL_RENDERBUFFER, mRenderTarget.stencil->getName()); + GL_RENDERBUFFER, mRenderTarget.stencil->getName()); } if (clip->mode == ClipMode::RectangleList) { @@ -344,17 +342,15 @@ void BakedOpRenderer::prepareRender(const Rect* dirtyBounds, const ClipBase* cli } void BakedOpRenderer::renderGlopImpl(const Rect* dirtyBounds, const ClipBase* clip, - const Glop& glop) { + const Glop& glop) { prepareRender(dirtyBounds, clip); // Disable blending if this is the first draw to the main framebuffer, in case app has defined // transparency where it doesn't make sense - as first draw in opaque window. Note that we only // apply this improvement when the blend mode is SRC_OVER - other modes (e.g. CLEAR) can be // valid draws that affect other content (e.g. draw CLEAR, then draw DST_OVER) - bool overrideDisableBlending = !mHasDrawn - && mOpaque - && !mRenderTarget.frameBufferId - && glop.blend.src == GL_ONE - && glop.blend.dst == GL_ONE_MINUS_SRC_ALPHA; + bool overrideDisableBlending = !mHasDrawn && mOpaque && !mRenderTarget.frameBufferId && + glop.blend.src == GL_ONE && + glop.blend.dst == GL_ONE_MINUS_SRC_ALPHA; mRenderState.render(glop, mRenderTarget.orthoMatrix, overrideDisableBlending); if (!mRenderTarget.frameBufferId) mHasDrawn = true; } @@ -383,5 +379,5 @@ void BakedOpRenderer::dirtyRenderTarget(const Rect& uiDirty) { } } -} // namespace uirenderer -} // namespace android +} // namespace uirenderer +} // namespace android diff --git a/libs/hwui/BakedOpRenderer.h b/libs/hwui/BakedOpRenderer.h index 01ca36742d24..7c0590d7ab48 100644 --- a/libs/hwui/BakedOpRenderer.h +++ b/libs/hwui/BakedOpRenderer.h @@ -46,23 +46,20 @@ public: */ struct LightInfo { LightInfo() : LightInfo(0, 0) {} - LightInfo(uint8_t ambientShadowAlpha, - uint8_t spotShadowAlpha) - : ambientShadowAlpha(ambientShadowAlpha) - , spotShadowAlpha(spotShadowAlpha) {} + LightInfo(uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha) + : ambientShadowAlpha(ambientShadowAlpha), spotShadowAlpha(spotShadowAlpha) {} uint8_t ambientShadowAlpha; uint8_t spotShadowAlpha; }; BakedOpRenderer(Caches& caches, RenderState& renderState, bool opaque, bool wideColorGamut, - const LightInfo& lightInfo) + const LightInfo& lightInfo) : mGlopReceiver(DefaultGlopReceiver) , mRenderState(renderState) , mCaches(caches) , mOpaque(opaque) , mWideColorGamut(wideColorGamut) - , mLightInfo(lightInfo) { - } + , mLightInfo(lightInfo) {} RenderState& renderState() { return mRenderState; } Caches& caches() { return mCaches; } @@ -79,9 +76,7 @@ public: const LightInfo& getLightInfo() const { return mLightInfo; } void renderGlop(const BakedOpState& state, const Glop& glop) { - renderGlop(&state.computedState.clippedBounds, - state.computedState.getClipIfNeeded(), - glop); + renderGlop(&state.computedState.clippedBounds, state.computedState.getClipIfNeeded(), glop); } void renderFunctor(const FunctorOp& op, const BakedOpState& state); @@ -97,15 +92,17 @@ public: // simple draw methods, to be used for end frame decoration void drawRect(float left, float top, float right, float bottom, const SkPaint* paint) { - float ltrb[4] = { left, top, right, bottom }; + float ltrb[4] = {left, top, right, bottom}; drawRects(ltrb, 4, paint); } void drawRects(const float* rects, int count, const SkPaint* paint); + protected: GlopReceiver mGlopReceiver; + private: static void DefaultGlopReceiver(BakedOpRenderer& renderer, const Rect* dirtyBounds, - const ClipBase* clip, const Glop& glop) { + const ClipBase* clip, const Glop& glop) { renderer.renderGlopImpl(dirtyBounds, clip, glop); } void renderGlopImpl(const Rect* dirtyBounds, const ClipBase* clip, const Glop& glop); @@ -148,5 +145,5 @@ private: const LightInfo mLightInfo; }; -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/BakedOpState.cpp b/libs/hwui/BakedOpState.cpp index 9823a02dc847..63edf77279e3 100644 --- a/libs/hwui/BakedOpState.cpp +++ b/libs/hwui/BakedOpState.cpp @@ -31,7 +31,8 @@ static int computeClipSideFlags(const Rect& clip, const Rect& bounds) { } ResolvedRenderState::ResolvedRenderState(LinearAllocator& allocator, Snapshot& snapshot, - const RecordedOp& recordedOp, bool expandForStroke, bool expandForPathTexture) { + const RecordedOp& recordedOp, bool expandForStroke, + bool expandForPathTexture) { // resolvedMatrix = parentMatrix * localMatrix transform.loadMultiply(*snapshot.transform, recordedOp.localMatrix); @@ -44,16 +45,16 @@ ResolvedRenderState::ResolvedRenderState(LinearAllocator& allocator, Snapshot& s clippedBounds.outset(1); } transform.mapRect(clippedBounds); - if (CC_UNLIKELY(expandForStroke - && (!transform.isPureTranslate() || recordedOp.paint->getStrokeWidth() < 1.0f))) { + if (CC_UNLIKELY(expandForStroke && + (!transform.isPureTranslate() || recordedOp.paint->getStrokeWidth() < 1.0f))) { // account for hairline stroke when stroke may be < 1 scaled pixel // Non translate || strokeWidth < 1 is conservative, but will cover all cases clippedBounds.outset(0.5f); } // resolvedClipRect = intersect(parentMatrix * localClip, parentClip) - clipState = snapshot.serializeIntersectedClip(allocator, - recordedOp.localClip, *(snapshot.transform)); + clipState = snapshot.serializeIntersectedClip(allocator, recordedOp.localClip, + *(snapshot.transform)); LOG_ALWAYS_FATAL_IF(!clipState, "must clip!"); const Rect& clipRect = clipState->rect; @@ -85,7 +86,7 @@ ResolvedRenderState::ResolvedRenderState(LinearAllocator& allocator, Snapshot& s } ResolvedRenderState::ResolvedRenderState(LinearAllocator& allocator, Snapshot& snapshot, - const Matrix4& localTransform, const ClipBase* localClip) { + const Matrix4& localTransform, const ClipBase* localClip) { transform.loadMultiply(*snapshot.transform, localTransform); clipState = snapshot.serializeIntersectedClip(allocator, localClip, *(snapshot.transform)); clippedBounds = clipState->rect; @@ -109,11 +110,11 @@ ResolvedRenderState::ResolvedRenderState(const ClipRect* clipRect, const Rect& d clippedBounds.doIntersect(clipRect->rect); } -BakedOpState* BakedOpState::tryConstruct(LinearAllocator& allocator, - Snapshot& snapshot, const RecordedOp& recordedOp) { +BakedOpState* BakedOpState::tryConstruct(LinearAllocator& allocator, Snapshot& snapshot, + const RecordedOp& recordedOp) { if (CC_UNLIKELY(snapshot.getRenderTargetClip().isEmpty())) return nullptr; - BakedOpState* bakedState = allocator.create_trivial<BakedOpState>( - allocator, snapshot, recordedOp, false, false); + BakedOpState* bakedState = + allocator.create_trivial<BakedOpState>(allocator, snapshot, recordedOp, false, false); if (bakedState->computedState.clippedBounds.isEmpty()) { // bounds are empty, so op is rejected allocator.rewindIfLastAlloc(bakedState); @@ -122,21 +123,23 @@ BakedOpState* BakedOpState::tryConstruct(LinearAllocator& allocator, return bakedState; } -BakedOpState* BakedOpState::tryConstructUnbounded(LinearAllocator& allocator, - Snapshot& snapshot, const RecordedOp& recordedOp) { +BakedOpState* BakedOpState::tryConstructUnbounded(LinearAllocator& allocator, Snapshot& snapshot, + const RecordedOp& recordedOp) { if (CC_UNLIKELY(snapshot.getRenderTargetClip().isEmpty())) return nullptr; return allocator.create_trivial<BakedOpState>(allocator, snapshot, recordedOp); } -BakedOpState* BakedOpState::tryStrokeableOpConstruct(LinearAllocator& allocator, - Snapshot& snapshot, const RecordedOp& recordedOp, StrokeBehavior strokeBehavior, - bool expandForPathTexture) { +BakedOpState* BakedOpState::tryStrokeableOpConstruct(LinearAllocator& allocator, Snapshot& snapshot, + const RecordedOp& recordedOp, + StrokeBehavior strokeBehavior, + bool expandForPathTexture) { if (CC_UNLIKELY(snapshot.getRenderTargetClip().isEmpty())) return nullptr; - bool expandForStroke = (strokeBehavior == StrokeBehavior::Forced - || (recordedOp.paint && recordedOp.paint->getStyle() != SkPaint::kFill_Style)); + bool expandForStroke = + (strokeBehavior == StrokeBehavior::Forced || + (recordedOp.paint && recordedOp.paint->getStyle() != SkPaint::kFill_Style)); BakedOpState* bakedState = allocator.create_trivial<BakedOpState>( - allocator, snapshot, recordedOp, expandForStroke, expandForPathTexture); + allocator, snapshot, recordedOp, expandForStroke, expandForPathTexture); if (bakedState->computedState.clippedBounds.isEmpty()) { // bounds are empty, so op is rejected // NOTE: this won't succeed if a clip was allocated @@ -146,26 +149,25 @@ BakedOpState* BakedOpState::tryStrokeableOpConstruct(LinearAllocator& allocator, return bakedState; } -BakedOpState* BakedOpState::tryShadowOpConstruct(LinearAllocator& allocator, - Snapshot& snapshot, const ShadowOp* shadowOpPtr) { +BakedOpState* BakedOpState::tryShadowOpConstruct(LinearAllocator& allocator, Snapshot& snapshot, + const ShadowOp* shadowOpPtr) { if (CC_UNLIKELY(snapshot.getRenderTargetClip().isEmpty())) return nullptr; // clip isn't empty, so construct the op return allocator.create_trivial<BakedOpState>(allocator, snapshot, shadowOpPtr); } -BakedOpState* BakedOpState::directConstruct(LinearAllocator& allocator, - const ClipRect* clip, const Rect& dstRect, const RecordedOp& recordedOp) { +BakedOpState* BakedOpState::directConstruct(LinearAllocator& allocator, const ClipRect* clip, + const Rect& dstRect, const RecordedOp& recordedOp) { return allocator.create_trivial<BakedOpState>(clip, dstRect, recordedOp); } void BakedOpState::setupOpacity(const SkPaint* paint) { - computedState.opaqueOverClippedBounds = computedState.transform.isSimple() - && computedState.clipState->mode == ClipMode::Rectangle - && MathUtils::areEqual(alpha, 1.0f) - && !roundRectClipState - && PaintUtils::isOpaquePaint(paint); + computedState.opaqueOverClippedBounds = computedState.transform.isSimple() && + computedState.clipState->mode == ClipMode::Rectangle && + MathUtils::areEqual(alpha, 1.0f) && + !roundRectClipState && PaintUtils::isOpaquePaint(paint); } -} // namespace uirenderer -} // namespace android +} // namespace uirenderer +} // namespace android diff --git a/libs/hwui/BakedOpState.h b/libs/hwui/BakedOpState.h index 7b0b34f3d9c0..c74475516cdc 100644 --- a/libs/hwui/BakedOpState.h +++ b/libs/hwui/BakedOpState.h @@ -26,22 +26,22 @@ namespace android { namespace uirenderer { namespace OpClipSideFlags { - enum { - None = 0x0, - Left = 0x1, - Top = 0x2, - Right = 0x4, - Bottom = 0x8, - Full = 0xF, - // ConservativeFull = 0x1F needed? - }; +enum { + None = 0x0, + Left = 0x1, + Top = 0x2, + Right = 0x4, + Bottom = 0x8, + Full = 0xF, + // ConservativeFull = 0x1F needed? +}; } /** * Holds a list of BakedOpStates of ops that can be drawn together */ struct MergedBakedOpList { - const BakedOpState*const* states; + const BakedOpState* const* states; size_t count; int clipSideFlags; Rect clip; @@ -53,11 +53,12 @@ struct MergedBakedOpList { class ResolvedRenderState { public: ResolvedRenderState(LinearAllocator& allocator, Snapshot& snapshot, - const RecordedOp& recordedOp, bool expandForStroke, bool expandForPathTexture); + const RecordedOp& recordedOp, bool expandForStroke, + bool expandForPathTexture); // Constructor for unbounded ops *with* transform/clip ResolvedRenderState(LinearAllocator& allocator, Snapshot& snapshot, - const Matrix4& localTransform, const ClipBase* localClip); + const Matrix4& localTransform, const ClipBase* localClip); // Constructor for unbounded ops without transform/clip (namely shadows) ResolvedRenderState(LinearAllocator& allocator, Snapshot& snapshot); @@ -74,19 +75,15 @@ public: return outClip; } - const Rect& clipRect() const { - return clipState->rect; - } + const Rect& clipRect() const { return clipState->rect; } bool requiresClip() const { - return clipSideFlags != OpClipSideFlags::None - || CC_UNLIKELY(clipState->mode != ClipMode::Rectangle); + return clipSideFlags != OpClipSideFlags::None || + CC_UNLIKELY(clipState->mode != ClipMode::Rectangle); } // returns the clip if it's needed to draw the operation, otherwise nullptr - const ClipBase* getClipIfNeeded() const { - return requiresClip() ? clipState : nullptr; - } + const ClipBase* getClipIfNeeded() const { return requiresClip() ? clipState : nullptr; } Matrix4 transform; const ClipBase* clipState = nullptr; @@ -103,11 +100,11 @@ public: */ class BakedOpState { public: - static BakedOpState* tryConstruct(LinearAllocator& allocator, - Snapshot& snapshot, const RecordedOp& recordedOp); + static BakedOpState* tryConstruct(LinearAllocator& allocator, Snapshot& snapshot, + const RecordedOp& recordedOp); - static BakedOpState* tryConstructUnbounded(LinearAllocator& allocator, - Snapshot& snapshot, const RecordedOp& recordedOp); + static BakedOpState* tryConstructUnbounded(LinearAllocator& allocator, Snapshot& snapshot, + const RecordedOp& recordedOp); enum class StrokeBehavior { // stroking is forced, regardless of style on paint (such as for lines) @@ -116,15 +113,16 @@ public: StyleDefined, }; - static BakedOpState* tryStrokeableOpConstruct(LinearAllocator& allocator, - Snapshot& snapshot, const RecordedOp& recordedOp, StrokeBehavior strokeBehavior, - bool expandForPathTexture); + static BakedOpState* tryStrokeableOpConstruct(LinearAllocator& allocator, Snapshot& snapshot, + const RecordedOp& recordedOp, + StrokeBehavior strokeBehavior, + bool expandForPathTexture); - static BakedOpState* tryShadowOpConstruct(LinearAllocator& allocator, - Snapshot& snapshot, const ShadowOp* shadowOpPtr); + static BakedOpState* tryShadowOpConstruct(LinearAllocator& allocator, Snapshot& snapshot, + const ShadowOp* shadowOpPtr); - static BakedOpState* directConstruct(LinearAllocator& allocator, - const ClipRect* clip, const Rect& dstRect, const RecordedOp& recordedOp); + static BakedOpState* directConstruct(LinearAllocator& allocator, const ClipRect* clip, + const Rect& dstRect, const RecordedOp& recordedOp); // Set opaqueOverClippedBounds. If this method isn't called, the op is assumed translucent. void setupOpacity(const SkPaint* paint); @@ -140,8 +138,8 @@ public: private: friend class LinearAllocator; - BakedOpState(LinearAllocator& allocator, Snapshot& snapshot, - const RecordedOp& recordedOp, bool expandForStroke, bool expandForPathTexture) + BakedOpState(LinearAllocator& allocator, Snapshot& snapshot, const RecordedOp& recordedOp, + bool expandForStroke, bool expandForPathTexture) : computedState(allocator, snapshot, recordedOp, expandForStroke, expandForPathTexture) , alpha(snapshot.alpha) , roundRectClipState(snapshot.roundRectClipState) @@ -167,7 +165,7 @@ private: , op(&recordedOp) {} }; -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android -#endif // ANDROID_HWUI_BAKED_OP_STATE_H +#endif // ANDROID_HWUI_BAKED_OP_STATE_H diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp index 0700d1fb9f70..3c774a3313d2 100644 --- a/libs/hwui/Caches.cpp +++ b/libs/hwui/Caches.cpp @@ -19,8 +19,8 @@ #include "GammaFontRenderer.h" #include "GlLayer.h" #include "Properties.h" -#include "renderstate/RenderState.h" #include "ShadowTessellator.h" +#include "renderstate/RenderState.h" #ifdef BUGREPORT_FONT_CACHE_USAGE #include "font/FontCacheHistoryTracker.h" #endif @@ -40,9 +40,9 @@ Caches* Caches::sInstance = nullptr; /////////////////////////////////////////////////////////////////////////////// #if DEBUG_CACHE_FLUSH - #define FLUSH_LOGD(...) ALOGD(__VA_ARGS__) +#define FLUSH_LOGD(...) ALOGD(__VA_ARGS__) #else - #define FLUSH_LOGD(...) +#define FLUSH_LOGD(...) #endif /////////////////////////////////////////////////////////////////////////////// @@ -98,8 +98,8 @@ void Caches::initConstraints() { void Caches::initStaticProperties() { // OpenGL ES 3.0+ specific features - gpuPixelBuffersEnabled = extensions().hasPixelBufferObjects() - && property_get_bool(PROPERTY_ENABLE_GPU_PIXEL_BUFFERS, true); + gpuPixelBuffersEnabled = extensions().hasPixelBufferObjects() && + property_get_bool(PROPERTY_ENABLE_GPU_PIXEL_BUFFERS, true); } void Caches::terminate() { @@ -143,10 +143,8 @@ void Caches::setProgram(Program* program) { /////////////////////////////////////////////////////////////////////////////// uint32_t Caches::getOverdrawColor(uint32_t amount) const { - static uint32_t sOverdrawColors[2][4] = { - { 0x2f0000ff, 0x2f00ff00, 0x3fff0000, 0x7fff0000 }, - { 0x2f0000ff, 0x4fffff00, 0x5fff8ad8, 0x7fff0000 } - }; + static uint32_t sOverdrawColors[2][4] = {{0x2f0000ff, 0x2f00ff00, 0x3fff0000, 0x7fff0000}, + {0x2f0000ff, 0x4fffff00, 0x5fff8ad8, 0x7fff0000}}; if (amount < 1) amount = 1; if (amount > 4) amount = 4; @@ -160,46 +158,44 @@ void Caches::dumpMemoryUsage() { ALOGD("%s", stringLog.string()); } -void Caches::dumpMemoryUsage(String8 &log) { +void Caches::dumpMemoryUsage(String8& log) { uint32_t total = 0; log.appendFormat("Current memory usage / total memory usage (bytes):\n"); - log.appendFormat(" TextureCache %8d / %8d\n", - textureCache.getSize(), textureCache.getMaxSize()); + log.appendFormat(" TextureCache %8d / %8d\n", textureCache.getSize(), + textureCache.getMaxSize()); if (mRenderState) { int memused = 0; for (std::set<Layer*>::iterator it = mRenderState->mActiveLayers.begin(); - it != mRenderState->mActiveLayers.end(); it++) { + it != mRenderState->mActiveLayers.end(); it++) { const Layer* layer = *it; LOG_ALWAYS_FATAL_IF(layer->getApi() != Layer::Api::OpenGL); const GlLayer* glLayer = static_cast<const GlLayer*>(layer); - log.appendFormat(" GlLayer size %dx%d; texid=%u refs=%d\n", - layer->getWidth(), layer->getHeight(), - glLayer->getTextureId(), - layer->getStrongCount()); + log.appendFormat(" GlLayer size %dx%d; texid=%u refs=%d\n", layer->getWidth(), + layer->getHeight(), glLayer->getTextureId(), layer->getStrongCount()); memused += layer->getWidth() * layer->getHeight() * 4; } - log.appendFormat(" Layers total %8d (numLayers = %zu)\n", - memused, mRenderState->mActiveLayers.size()); + log.appendFormat(" Layers total %8d (numLayers = %zu)\n", memused, + mRenderState->mActiveLayers.size()); total += memused; } - log.appendFormat(" RenderBufferCache %8d / %8d\n", - renderBufferCache.getSize(), renderBufferCache.getMaxSize()); - log.appendFormat(" GradientCache %8d / %8d\n", - gradientCache.getSize(), gradientCache.getMaxSize()); - log.appendFormat(" PathCache %8d / %8d\n", - pathCache.getSize(), pathCache.getMaxSize()); - log.appendFormat(" TessellationCache %8d / %8d\n", - tessellationCache.getSize(), tessellationCache.getMaxSize()); + log.appendFormat(" RenderBufferCache %8d / %8d\n", renderBufferCache.getSize(), + renderBufferCache.getMaxSize()); + log.appendFormat(" GradientCache %8d / %8d\n", gradientCache.getSize(), + gradientCache.getMaxSize()); + log.appendFormat(" PathCache %8d / %8d\n", pathCache.getSize(), + pathCache.getMaxSize()); + log.appendFormat(" TessellationCache %8d / %8d\n", tessellationCache.getSize(), + tessellationCache.getMaxSize()); log.appendFormat(" TextDropShadowCache %8d / %8d\n", dropShadowCache.getSize(), - dropShadowCache.getMaxSize()); - log.appendFormat(" PatchCache %8d / %8d\n", - patchCache.getSize(), patchCache.getMaxSize()); + dropShadowCache.getMaxSize()); + log.appendFormat(" PatchCache %8d / %8d\n", patchCache.getSize(), + patchCache.getMaxSize()); fontRenderer.dumpMemoryUsage(log); log.appendFormat("Other:\n"); - log.appendFormat(" FboCache %8d / %8d\n", - fboCache.getSize(), fboCache.getMaxSize()); + log.appendFormat(" FboCache %8d / %8d\n", fboCache.getSize(), + fboCache.getMaxSize()); total += textureCache.getSize(); total += renderBufferCache.getSize(); @@ -238,13 +234,13 @@ void Caches::flush(FlushMode mode) { gradientCache.clear(); fontRenderer.clear(); fboCache.clear(); - // fall through + // fall through case FlushMode::Moderate: fontRenderer.flush(); textureCache.flush(); pathCache.clear(); tessellationCache.clear(); - // fall through + // fall through case FlushMode::Layers: renderBufferCache.clear(); break; @@ -274,5 +270,5 @@ TextureVertex* Caches::getRegionMesh() { // Temporary Properties /////////////////////////////////////////////////////////////////////////////// -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h index 29eddde1e42b..97328324df04 100644 --- a/libs/hwui/Caches.h +++ b/libs/hwui/Caches.h @@ -22,20 +22,20 @@ #include "GammaFontRenderer.h" #include "GradientCache.h" #include "PatchCache.h" -#include "ProgramCache.h" #include "PathCache.h" +#include "ProgramCache.h" #include "RenderBufferCache.h" -#include "renderstate/PixelBufferState.h" -#include "renderstate/TextureState.h" #include "ResourceCache.h" #include "TessellationCache.h" #include "TextDropShadowCache.h" #include "TextureCache.h" -#include "thread/TaskProcessor.h" +#include "renderstate/PixelBufferState.h" +#include "renderstate/TextureState.h" #include "thread/TaskManager.h" +#include "thread/TaskProcessor.h" -#include <vector> #include <memory> +#include <vector> #include <GLES3/gl3.h> @@ -70,19 +70,14 @@ public: return *sInstance; } - static bool hasInstance() { - return sInstance != nullptr; - } + static bool hasInstance() { return sInstance != nullptr; } + private: explicit Caches(RenderState& renderState); static Caches* sInstance; public: - enum class FlushMode { - Layers = 0, - Moderate, - Full - }; + enum class FlushMode { Layers = 0, Moderate, Full }; /** * Initialize caches. @@ -181,9 +176,9 @@ private: void initConstraints(); void initStaticProperties(); - static void eventMarkNull(GLsizei length, const GLchar* marker) { } - static void startMarkNull(GLsizei length, const GLchar* marker) { } - static void endMarkNull() { } + static void eventMarkNull(GLsizei length, const GLchar* marker) {} + static void startMarkNull(GLsizei length, const GLchar* marker) {} + static void endMarkNull() {} RenderState* mRenderState; @@ -198,9 +193,9 @@ private: // TODO: move below to RenderState PixelBufferState* mPixelBufferState = nullptr; TextureState* mTextureState = nullptr; - Program* mProgram = nullptr; // note: object owned by ProgramCache + Program* mProgram = nullptr; // note: object owned by ProgramCache -}; // class Caches +}; // class Caches -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/CanvasProperty.h b/libs/hwui/CanvasProperty.h index 56671bba654b..fc76e8b68af8 100644 --- a/libs/hwui/CanvasProperty.h +++ b/libs/hwui/CanvasProperty.h @@ -27,6 +27,7 @@ namespace uirenderer { class CanvasPropertyPrimitive : public VirtualLightRefBase { PREVENT_COPY_AND_ASSIGN(CanvasPropertyPrimitive); + public: explicit CanvasPropertyPrimitive(float initialValue) : value(initialValue) {} @@ -35,6 +36,7 @@ public: class CanvasPropertyPaint : public VirtualLightRefBase { PREVENT_COPY_AND_ASSIGN(CanvasPropertyPaint); + public: explicit CanvasPropertyPaint(const SkPaint& initialValue) : value(initialValue) {} diff --git a/libs/hwui/CanvasState.cpp b/libs/hwui/CanvasState.cpp index 9c068b080d45..d18c4abde7f2 100644 --- a/libs/hwui/CanvasState.cpp +++ b/libs/hwui/CanvasState.cpp @@ -21,14 +21,8 @@ namespace android { namespace uirenderer { - CanvasState::CanvasState(CanvasStateClient& renderer) - : mWidth(-1) - , mHeight(-1) - , mSaveCount(1) - , mCanvas(renderer) - , mSnapshot(&mFirstSnapshot) { -} + : mWidth(-1), mHeight(-1), mSaveCount(1), mCanvas(renderer), mSnapshot(&mFirstSnapshot) {} CanvasState::~CanvasState() { // First call freeSnapshot on all but mFirstSnapshot @@ -57,10 +51,9 @@ void CanvasState::initializeRecordingSaveStack(int viewportWidth, int viewportHe mSaveCount = 1; } -void CanvasState::initializeSaveStack( - int viewportWidth, int viewportHeight, - float clipLeft, float clipTop, - float clipRight, float clipBottom, const Vector3& lightCenter) { +void CanvasState::initializeSaveStack(int viewportWidth, int viewportHeight, float clipLeft, + float clipTop, float clipRight, float clipBottom, + const Vector3& lightCenter) { if (mWidth != viewportWidth || mHeight != viewportHeight) { mWidth = viewportWidth; mHeight = viewportHeight; @@ -92,7 +85,7 @@ void CanvasState::freeSnapshot(Snapshot* snapshot) { snapshot->~Snapshot(); // Arbitrary number, just don't let this grown unbounded if (mSnapshotPoolCount > 10) { - free((void*) snapshot); + free((void*)snapshot); } else { snapshot->previous = mSnapshotPool; mSnapshotPool = snapshot; @@ -215,7 +208,7 @@ bool CanvasState::clipPath(const SkPath* path, SkClipOp op) { void CanvasState::setClippingOutline(LinearAllocator& allocator, const Outline* outline) { Rect bounds; float radius; - if (!outline->getAsRoundRect(&bounds, &radius)) return; // only RR supported + if (!outline->getAsRoundRect(&bounds, &radius)) return; // only RR supported bool outlineIsRounded = MathUtils::isPositive(radius); if (!outlineIsRounded || currentTransform()->isSimple()) { @@ -241,10 +234,9 @@ void CanvasState::setClippingOutline(LinearAllocator& allocator, const Outline* * @param snapOut if set, the geometry will be treated as having an AA ramp. * See Rect::snapGeometryToPixelBoundaries() */ -bool CanvasState::calculateQuickRejectForScissor(float left, float top, - float right, float bottom, - bool* clipRequired, bool* roundRectClipRequired, - bool snapOut) const { +bool CanvasState::calculateQuickRejectForScissor(float left, float top, float right, float bottom, + bool* clipRequired, bool* roundRectClipRequired, + bool snapOut) const { if (bottom <= top || right <= left) { return true; } @@ -265,21 +257,20 @@ bool CanvasState::calculateQuickRejectForScissor(float left, float top, // round rect clip is required if RR clip exists, and geometry intersects its corners if (roundRectClipRequired) { - *roundRectClipRequired = mSnapshot->roundRectClipState != nullptr - && mSnapshot->roundRectClipState->areaRequiresRoundRectClip(r); + *roundRectClipRequired = mSnapshot->roundRectClipState != nullptr && + mSnapshot->roundRectClipState->areaRequiresRoundRectClip(r); } return false; } -bool CanvasState::quickRejectConservative(float left, float top, - float right, float bottom) const { +bool CanvasState::quickRejectConservative(float left, float top, float right, float bottom) const { if (bottom <= top || right <= left) { return true; } Rect r(left, top, right, bottom); currentTransform()->mapRect(r); - r.roundOut(); // rounded out to be conservative + r.roundOut(); // rounded out to be conservative Rect clipRect(currentRenderTargetClip()); clipRect.snapToPixelBoundaries(); @@ -289,5 +280,5 @@ bool CanvasState::quickRejectConservative(float left, float top, return false; } -} // namespace uirenderer -} // namespace android +} // namespace uirenderer +} // namespace android diff --git a/libs/hwui/CanvasState.h b/libs/hwui/CanvasState.h index b1fe09eb1aec..9ac35ff47dab 100644 --- a/libs/hwui/CanvasState.h +++ b/libs/hwui/CanvasState.h @@ -32,8 +32,8 @@ namespace uirenderer { */ class CanvasStateClient { public: - CanvasStateClient() { } - virtual ~CanvasStateClient() { } + CanvasStateClient() {} + virtual ~CanvasStateClient() {} /** * Callback allowing embedder to take actions in the middle of a @@ -53,7 +53,7 @@ public: */ virtual GLuint getTargetFbo() const = 0; -}; // class CanvasStateClient +}; // class CanvasStateClient /** * Implements Canvas state methods on behalf of Renderers. @@ -86,13 +86,10 @@ public: * Initializes the first snapshot, computing the projection matrix, * and stores the dimensions of the render target. */ - void initializeSaveStack(int viewportWidth, int viewportHeight, - float clipLeft, float clipTop, float clipRight, float clipBottom, - const Vector3& lightCenter); + void initializeSaveStack(int viewportWidth, int viewportHeight, float clipLeft, float clipTop, + float clipRight, float clipBottom, const Vector3& lightCenter); - bool hasRectToRectTransform() const { - return CC_LIKELY(currentTransform()->rectToRect()); - } + bool hasRectToRectTransform() const { return CC_LIKELY(currentTransform()->rectToRect()); } // Save (layer) int getSaveCount() const { return mSaveCount; } @@ -112,9 +109,9 @@ public: void skew(float sx, float sy); void setMatrix(const SkMatrix& matrix); - void setMatrix(const Matrix4& matrix); // internal only convenience method + void setMatrix(const Matrix4& matrix); // internal only convenience method void concatMatrix(const SkMatrix& matrix); - void concatMatrix(const Matrix4& matrix); // internal only convenience method + void concatMatrix(const Matrix4& matrix); // internal only convenience method // Clip const Rect& getLocalClipBounds() const { return mSnapshot->getLocalClip(); } @@ -132,13 +129,11 @@ public: * outline. */ void setClippingOutline(LinearAllocator& allocator, const Outline* outline); - void setClippingRoundRect(LinearAllocator& allocator, - const Rect& rect, float radius, bool highPriority = true) { + void setClippingRoundRect(LinearAllocator& allocator, const Rect& rect, float radius, + bool highPriority = true) { mSnapshot->setClippingRoundRect(allocator, rect, radius, highPriority); } - void setProjectionPathMask(const SkPath* path) { - mSnapshot->setProjectionPathMask(path); - } + void setProjectionPathMask(const SkPath* path) { mSnapshot->setProjectionPathMask(path); } /** * Returns true if drawing in the rectangle (left, top, right, bottom) @@ -146,14 +141,19 @@ public: * perfect tests would return true. */ bool calculateQuickRejectForScissor(float left, float top, float right, float bottom, - bool* clipRequired, bool* roundRectClipRequired, bool snapOut) const; + bool* clipRequired, bool* roundRectClipRequired, + bool snapOut) const; void scaleAlpha(float alpha) { mSnapshot->alpha *= alpha; } inline const mat4* currentTransform() const { return currentSnapshot()->transform; } - inline const Rect& currentRenderTargetClip() const { return currentSnapshot()->getRenderTargetClip(); } + inline const Rect& currentRenderTargetClip() const { + return currentSnapshot()->getRenderTargetClip(); + } inline int currentFlags() const { return currentSnapshot()->flags; } - const Vector3& currentLightCenter() const { return currentSnapshot()->getRelativeLightCenter(); } + const Vector3& currentLightCenter() const { + return currentSnapshot()->getRelativeLightCenter(); + } int getViewportWidth() const { return currentSnapshot()->getViewportWidth(); } int getViewportHeight() const { return currentSnapshot()->getViewportHeight(); } int getWidth() const { return mWidth; } @@ -189,7 +189,7 @@ private: Snapshot* mSnapshotPool = nullptr; int mSnapshotPoolCount = 0; -}; // class CanvasState +}; // class CanvasState -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/ClipArea.cpp b/libs/hwui/ClipArea.cpp index 84451bacbc09..27d93cfa0391 100644 --- a/libs/hwui/ClipArea.cpp +++ b/libs/hwui/ClipArea.cpp @@ -33,7 +33,7 @@ static void handlePoint(Rect& transformedBounds, const Matrix4& transform, float Rect transformAndCalculateBounds(const Rect& r, const Matrix4& transform) { const float kMinFloat = std::numeric_limits<float>::lowest(); const float kMaxFloat = std::numeric_limits<float>::max(); - Rect transformedBounds = { kMaxFloat, kMaxFloat, kMinFloat, kMinFloat }; + Rect transformedBounds = {kMaxFloat, kMaxFloat, kMinFloat, kMinFloat}; handlePoint(transformedBounds, transform, r.left, r.top); handlePoint(transformedBounds, transform, r.right, r.top); handlePoint(transformedBounds, transform, r.left, r.bottom); @@ -49,18 +49,12 @@ void ClipBase::dump() const { * TransformedRectangle */ -TransformedRectangle::TransformedRectangle() { -} - -TransformedRectangle::TransformedRectangle(const Rect& bounds, - const Matrix4& transform) - : mBounds(bounds) - , mTransform(transform) { -} +TransformedRectangle::TransformedRectangle() {} -bool TransformedRectangle::canSimplyIntersectWith( - const TransformedRectangle& other) const { +TransformedRectangle::TransformedRectangle(const Rect& bounds, const Matrix4& transform) + : mBounds(bounds), mTransform(transform) {} +bool TransformedRectangle::canSimplyIntersectWith(const TransformedRectangle& other) const { return mTransform == other.mTransform; } @@ -76,9 +70,7 @@ bool TransformedRectangle::isEmpty() const { * RectangleList */ -RectangleList::RectangleList() - : mTransformedRectanglesCount(0) { -} +RectangleList::RectangleList() : mTransformedRectanglesCount(0) {} bool RectangleList::isEmpty() const { if (mTransformedRectanglesCount < 1) { @@ -110,8 +102,7 @@ void RectangleList::set(const Rect& bounds, const Matrix4& transform) { mTransformedRectangles[0] = TransformedRectangle(bounds, transform); } -bool RectangleList::intersectWith(const Rect& bounds, - const Matrix4& transform) { +bool RectangleList::intersectWith(const Rect& bounds, const Matrix4& transform) { TransformedRectangle newRectangle(bounds, transform); // Try to find a rectangle with a compatible transformation @@ -148,8 +139,7 @@ Rect RectangleList::calculateBounds() const { return bounds; } -static SkPath pathFromTransformedRectangle(const Rect& bounds, - const Matrix4& transform) { +static SkPath pathFromTransformedRectangle(const Rect& bounds, const Matrix4& transform) { SkPath rectPath; SkPath rectPathTransformed; rectPath.addRect(bounds.left, bounds.top, bounds.right, bounds.bottom); @@ -163,8 +153,8 @@ SkRegion RectangleList::convertToRegion(const SkRegion& clip) const { SkRegion rectangleListAsRegion; for (int index = 0; index < mTransformedRectanglesCount; index++) { const TransformedRectangle& tr(mTransformedRectangles[index]); - SkPath rectPathTransformed = pathFromTransformedRectangle( - tr.getBounds(), tr.getTransform()); + SkPath rectPathTransformed = + pathFromTransformedRectangle(tr.getBounds(), tr.getTransform()); if (index == 0) { rectangleListAsRegion.setPath(rectPathTransformed, clip); } else { @@ -186,9 +176,7 @@ void RectangleList::transform(const Matrix4& transform) { * ClipArea */ -ClipArea::ClipArea() - : mMode(ClipMode::Rectangle) { -} +ClipArea::ClipArea() : mMode(ClipMode::Rectangle) {} /* * Interface @@ -215,21 +203,20 @@ void ClipArea::setClip(float left, float top, float right, float bottom) { mClipRegion.setEmpty(); } -void ClipArea::clipRectWithTransform(const Rect& r, const mat4* transform, - SkRegion::Op op) { +void ClipArea::clipRectWithTransform(const Rect& r, const mat4* transform, SkRegion::Op op) { if (op == SkRegion::kReplace_Op) mReplaceOpObserved = true; if (!mPostViewportClipObserved && op == SkRegion::kIntersect_Op) op = SkRegion::kReplace_Op; onClipUpdated(); switch (mMode) { - case ClipMode::Rectangle: - rectangleModeClipRectWithTransform(r, transform, op); - break; - case ClipMode::RectangleList: - rectangleListModeClipRectWithTransform(r, transform, op); - break; - case ClipMode::Region: - regionModeClipRectWithTransform(r, transform, op); - break; + case ClipMode::Rectangle: + rectangleModeClipRectWithTransform(r, transform, op); + break; + case ClipMode::RectangleList: + rectangleListModeClipRectWithTransform(r, transform, op); + break; + case ClipMode::Region: + regionModeClipRectWithTransform(r, transform, op); + break; } } @@ -242,8 +229,7 @@ void ClipArea::clipRegion(const SkRegion& region, SkRegion::Op op) { onClipRegionUpdated(); } -void ClipArea::clipPathWithTransform(const SkPath& path, const mat4* transform, - SkRegion::Op op) { +void ClipArea::clipPathWithTransform(const SkPath& path, const mat4* transform, SkRegion::Op op) { if (op == SkRegion::kReplace_Op) mReplaceOpObserved = true; if (!mPostViewportClipObserved && op == SkRegion::kIntersect_Op) op = SkRegion::kReplace_Op; onClipUpdated(); @@ -269,9 +255,8 @@ void ClipArea::enterRectangleMode() { mMode = ClipMode::Rectangle; } -void ClipArea::rectangleModeClipRectWithTransform(const Rect& r, - const mat4* transform, SkRegion::Op op) { - +void ClipArea::rectangleModeClipRectWithTransform(const Rect& r, const mat4* transform, + SkRegion::Op op) { if (op == SkRegion::kReplace_Op && transform->rectToRect()) { mClipRect = r; transform->mapRect(mClipRect); @@ -306,10 +291,9 @@ void ClipArea::enterRectangleListMode() { mRectangleList.set(mClipRect, Matrix4::identity()); } -void ClipArea::rectangleListModeClipRectWithTransform(const Rect& r, - const mat4* transform, SkRegion::Op op) { - if (op != SkRegion::kIntersect_Op - || !mRectangleList.intersectWith(r, *transform)) { +void ClipArea::rectangleListModeClipRectWithTransform(const Rect& r, const mat4* transform, + SkRegion::Op op) { + if (op != SkRegion::kIntersect_Op || !mRectangleList.intersectWith(r, *transform)) { enterRegionMode(); regionModeClipRectWithTransform(r, transform, op); } @@ -332,8 +316,8 @@ void ClipArea::enterRegionMode() { } } -void ClipArea::regionModeClipRectWithTransform(const Rect& r, - const mat4* transform, SkRegion::Op op) { +void ClipArea::regionModeClipRectWithTransform(const Rect& r, const mat4* transform, + SkRegion::Op op) { SkPath transformedRect = pathFromTransformedRectangle(r, *transform); SkRegion transformedRectRegion; regionFromPath(transformedRect, transformedRectRegion); @@ -365,24 +349,24 @@ const ClipBase* ClipArea::serializeClip(LinearAllocator& allocator) { } static_assert(std::is_trivially_destructible<Rect>::value, - "expect Rect to be trivially destructible"); + "expect Rect to be trivially destructible"); static_assert(std::is_trivially_destructible<RectangleList>::value, - "expect RectangleList to be trivially destructible"); + "expect RectangleList to be trivially destructible"); if (mLastSerialization == nullptr) { ClipBase* serialization = nullptr; switch (mMode) { - case ClipMode::Rectangle: - serialization = allocator.create<ClipRect>(mClipRect); - break; - case ClipMode::RectangleList: - serialization = allocator.create<ClipRectList>(mRectangleList); - serialization->rect = mRectangleList.calculateBounds(); - break; - case ClipMode::Region: - serialization = allocator.create<ClipRegion>(mClipRegion); - serialization->rect.set(mClipRegion.getBounds()); - break; + case ClipMode::Rectangle: + serialization = allocator.create<ClipRect>(mClipRect); + break; + case ClipMode::RectangleList: + serialization = allocator.create<ClipRectList>(mRectangleList); + serialization->rect = mRectangleList.calculateBounds(); + break; + case ClipMode::Region: + serialization = allocator.create<ClipRegion>(mClipRegion); + serialization->rect.set(mClipRegion.getBounds()); + break; } serialization->intersectWithRoot = mReplaceOpObserved; // TODO: this is only done for draw time, should eventually avoid for record time @@ -404,81 +388,79 @@ inline static const SkRegion& getRegion(const ClipBase* scb) { // For simplicity, doesn't account for rect merging static bool cannotFitInRectangleList(const ClipArea& clipArea, const ClipBase* scb) { int currentRectCount = clipArea.isRectangleList() - ? clipArea.getRectangleList().getTransformedRectanglesCount() - : 1; + ? clipArea.getRectangleList().getTransformedRectanglesCount() + : 1; int recordedRectCount = (scb->mode == ClipMode::RectangleList) - ? getRectList(scb).getTransformedRectanglesCount() - : 1; + ? getRectList(scb).getTransformedRectanglesCount() + : 1; return currentRectCount + recordedRectCount > RectangleList::kMaxTransformedRectangles; } static const ClipRect sEmptyClipRect(Rect(0, 0)); const ClipBase* ClipArea::serializeIntersectedClip(LinearAllocator& allocator, - const ClipBase* recordedClip, const Matrix4& recordedClipTransform) { - + const ClipBase* recordedClip, + const Matrix4& recordedClipTransform) { // if no recordedClip passed, just serialize current state if (!recordedClip) return serializeClip(allocator); // if either is empty, clip is empty - if (CC_UNLIKELY(recordedClip->rect.isEmpty())|| mClipRect.isEmpty()) return &sEmptyClipRect; + if (CC_UNLIKELY(recordedClip->rect.isEmpty()) || mClipRect.isEmpty()) return &sEmptyClipRect; - if (!mLastResolutionResult - || recordedClip != mLastResolutionClip - || recordedClipTransform != mLastResolutionTransform) { + if (!mLastResolutionResult || recordedClip != mLastResolutionClip || + recordedClipTransform != mLastResolutionTransform) { mLastResolutionClip = recordedClip; mLastResolutionTransform = recordedClipTransform; - if (CC_LIKELY(mMode == ClipMode::Rectangle - && recordedClip->mode == ClipMode::Rectangle - && recordedClipTransform.rectToRect())) { + if (CC_LIKELY(mMode == ClipMode::Rectangle && recordedClip->mode == ClipMode::Rectangle && + recordedClipTransform.rectToRect())) { // common case - result is a single rectangle auto rectClip = allocator.create<ClipRect>(recordedClip->rect); recordedClipTransform.mapRect(rectClip->rect); rectClip->rect.doIntersect(mClipRect); rectClip->rect.snapToPixelBoundaries(); mLastResolutionResult = rectClip; - } else if (CC_UNLIKELY(mMode == ClipMode::Region - || recordedClip->mode == ClipMode::Region - || cannotFitInRectangleList(*this, recordedClip))) { + } else if (CC_UNLIKELY(mMode == ClipMode::Region || + recordedClip->mode == ClipMode::Region || + cannotFitInRectangleList(*this, recordedClip))) { // region case SkRegion other; switch (recordedClip->mode) { - case ClipMode::Rectangle: - if (CC_LIKELY(recordedClipTransform.rectToRect())) { - // simple transform, skip creating SkPath - Rect resultClip(recordedClip->rect); - recordedClipTransform.mapRect(resultClip); - other.setRect(resultClip.toSkIRect()); - } else { - SkPath transformedRect = pathFromTransformedRectangle(recordedClip->rect, - recordedClipTransform); - other.setPath(transformedRect, createViewportRegion()); + case ClipMode::Rectangle: + if (CC_LIKELY(recordedClipTransform.rectToRect())) { + // simple transform, skip creating SkPath + Rect resultClip(recordedClip->rect); + recordedClipTransform.mapRect(resultClip); + other.setRect(resultClip.toSkIRect()); + } else { + SkPath transformedRect = pathFromTransformedRectangle( + recordedClip->rect, recordedClipTransform); + other.setPath(transformedRect, createViewportRegion()); + } + break; + case ClipMode::RectangleList: { + RectangleList transformedList(getRectList(recordedClip)); + transformedList.transform(recordedClipTransform); + other = transformedList.convertToRegion(createViewportRegion()); + break; } - break; - case ClipMode::RectangleList: { - RectangleList transformedList(getRectList(recordedClip)); - transformedList.transform(recordedClipTransform); - other = transformedList.convertToRegion(createViewportRegion()); - break; - } - case ClipMode::Region: - other = getRegion(recordedClip); - applyTransformToRegion(recordedClipTransform, &other); + case ClipMode::Region: + other = getRegion(recordedClip); + applyTransformToRegion(recordedClipTransform, &other); } ClipRegion* regionClip = allocator.create<ClipRegion>(); switch (mMode) { - case ClipMode::Rectangle: - regionClip->region.op(mClipRect.toSkIRect(), other, SkRegion::kIntersect_Op); - break; - case ClipMode::RectangleList: - regionClip->region.op(mRectangleList.convertToRegion(createViewportRegion()), - other, SkRegion::kIntersect_Op); - break; - case ClipMode::Region: - regionClip->region.op(mClipRegion, other, SkRegion::kIntersect_Op); - break; + case ClipMode::Rectangle: + regionClip->region.op(mClipRect.toSkIRect(), other, SkRegion::kIntersect_Op); + break; + case ClipMode::RectangleList: + regionClip->region.op(mRectangleList.convertToRegion(createViewportRegion()), + other, SkRegion::kIntersect_Op); + break; + case ClipMode::Region: + regionClip->region.op(mClipRegion, other, SkRegion::kIntersect_Op); + break; } // Don't need to snap, since region's in int bounds regionClip->rect.set(regionClip->region.getBounds()); @@ -510,7 +492,7 @@ const ClipBase* ClipArea::serializeIntersectedClip(LinearAllocator& allocator, } void ClipArea::applyClip(const ClipBase* clip, const Matrix4& transform) { - if (!clip) return; // nothing to do + if (!clip) return; // nothing to do if (CC_LIKELY(clip->mode == ClipMode::Rectangle)) { clipRectWithTransform(clip->rect, &transform, SkRegion::kIntersect_Op); diff --git a/libs/hwui/ClipArea.h b/libs/hwui/ClipArea.h index cf5751606d12..a7a11801cfe2 100644 --- a/libs/hwui/ClipArea.h +++ b/libs/hwui/ClipArea.h @@ -39,18 +39,14 @@ public: bool isEmpty() const; - const Rect& getBounds() const { - return mBounds; - } + const Rect& getBounds() const { return mBounds; } Rect transformedBounds() const { Rect transformedBounds(transformAndCalculateBounds(mBounds, mTransform)); return transformedBounds; } - const Matrix4& getTransform() const { - return mTransform; - } + const Matrix4& getTransform() const { return mTransform; } void transform(const Matrix4& transform) { Matrix4 t; @@ -79,9 +75,7 @@ public: SkRegion convertToRegion(const SkRegion& clip) const; Rect calculateBounds() const; - enum { - kMaxTransformedRectangles = 5 - }; + enum { kMaxTransformedRectangles = 5 }; private: int mTransformedRectanglesCount; @@ -97,11 +91,8 @@ enum class ClipMode { }; struct ClipBase { - explicit ClipBase(ClipMode mode) - : mode(mode) {} - explicit ClipBase(const Rect& rect) - : mode(ClipMode::Rectangle) - , rect(rect) {} + explicit ClipBase(ClipMode mode) : mode(mode) {} + explicit ClipBase(const Rect& rect) : mode(ClipMode::Rectangle), rect(rect) {} const ClipMode mode; bool intersectWithRoot = false; // Bounds of the clipping area, used to define the scissor, and define which @@ -112,23 +103,18 @@ struct ClipBase { }; struct ClipRect : ClipBase { - explicit ClipRect(const Rect& rect) - : ClipBase(rect) {} + explicit ClipRect(const Rect& rect) : ClipBase(rect) {} }; struct ClipRectList : ClipBase { explicit ClipRectList(const RectangleList& rectList) - : ClipBase(ClipMode::RectangleList) - , rectList(rectList) {} + : ClipBase(ClipMode::RectangleList), rectList(rectList) {} RectangleList rectList; }; struct ClipRegion : ClipBase { - explicit ClipRegion(const SkRegion& region) - : ClipBase(ClipMode::Region) - , region(region) {} - ClipRegion() - : ClipBase(ClipMode::Region) {} + explicit ClipRegion(const SkRegion& region) : ClipBase(ClipMode::Region), region(region) {} + ClipRegion() : ClipBase(ClipMode::Region) {} SkRegion region; }; @@ -138,44 +124,29 @@ public: void setViewportDimensions(int width, int height); - bool isEmpty() const { - return mClipRect.isEmpty(); - } + bool isEmpty() const { return mClipRect.isEmpty(); } void setEmpty(); void setClip(float left, float top, float right, float bottom); - void clipRectWithTransform(const Rect& r, const mat4* transform, - SkRegion::Op op); - void clipPathWithTransform(const SkPath& path, const mat4* transform, - SkRegion::Op op); + void clipRectWithTransform(const Rect& r, const mat4* transform, SkRegion::Op op); + void clipPathWithTransform(const SkPath& path, const mat4* transform, SkRegion::Op op); - const Rect& getClipRect() const { - return mClipRect; - } + const Rect& getClipRect() const { return mClipRect; } - const SkRegion& getClipRegion() const { - return mClipRegion; - } + const SkRegion& getClipRegion() const { return mClipRegion; } - const RectangleList& getRectangleList() const { - return mRectangleList; - } + const RectangleList& getRectangleList() const { return mRectangleList; } - bool isRegion() const { - return ClipMode::Region == mMode; - } + bool isRegion() const { return ClipMode::Region == mMode; } - bool isSimple() const { - return mMode == ClipMode::Rectangle; - } + bool isSimple() const { return mMode == ClipMode::Rectangle; } - bool isRectangleList() const { - return mMode == ClipMode::RectangleList; - } + bool isRectangleList() const { return mMode == ClipMode::RectangleList; } WARN_UNUSED_RESULT const ClipBase* serializeClip(LinearAllocator& allocator); - WARN_UNUSED_RESULT const ClipBase* serializeIntersectedClip(LinearAllocator& allocator, - const ClipBase* recordedClip, const Matrix4& recordedClipTransform); + WARN_UNUSED_RESULT const ClipBase* serializeIntersectedClip( + LinearAllocator& allocator, const ClipBase* recordedClip, + const Matrix4& recordedClipTransform); void applyClip(const ClipBase* recordedClip, const Matrix4& recordedClipTransform); static void applyTransformToRegion(const Matrix4& transform, SkRegion* region); @@ -185,14 +156,13 @@ private: void rectangleModeClipRectWithTransform(const Rect& r, const mat4* transform, SkRegion::Op op); void enterRectangleListMode(); - void rectangleListModeClipRectWithTransform(const Rect& r, - const mat4* transform, SkRegion::Op op); + void rectangleListModeClipRectWithTransform(const Rect& r, const mat4* transform, + SkRegion::Op op); void enterRegionModeFromRectangleMode(); void enterRegionModeFromRectangleListMode(); void enterRegionMode(); - void regionModeClipRectWithTransform(const Rect& r, const mat4* transform, - SkRegion::Op op); + void regionModeClipRectWithTransform(const Rect& r, const mat4* transform, SkRegion::Op op); void clipRegion(const SkRegion& region, SkRegion::Op op); void ensureClipRegion(); @@ -205,9 +175,7 @@ private: mLastResolutionResult = nullptr; } - SkRegion createViewportRegion() { - return SkRegion(mViewportBounds.toSkIRect()); - } + SkRegion createViewportRegion() { return SkRegion(mViewportBounds.toSkIRect()); } void regionFromPath(const SkPath& path, SkRegion& pathAsRegion) { // TODO: this should not mask every path to the viewport - this makes it impossible to use diff --git a/libs/hwui/DamageAccumulator.cpp b/libs/hwui/DamageAccumulator.cpp index 2b4fe17d424d..cca0032b230e 100644 --- a/libs/hwui/DamageAccumulator.cpp +++ b/libs/hwui/DamageAccumulator.cpp @@ -57,17 +57,18 @@ static void computeTransformImpl(const DirtyStack* currentFrame, Matrix4* outMat computeTransformImpl(currentFrame->prev, outMatrix); } switch (currentFrame->type) { - case TransformRenderNode: - currentFrame->renderNode->applyViewPropertyTransforms(*outMatrix); - break; - case TransformMatrix4: - outMatrix->multiply(*currentFrame->matrix4); - break; - case TransformNone: - // nothing to be done - break; - default: - LOG_ALWAYS_FATAL("Tried to compute transform with an invalid type: %d", currentFrame->type); + case TransformRenderNode: + currentFrame->renderNode->applyViewPropertyTransforms(*outMatrix); + break; + case TransformMatrix4: + outMatrix->multiply(*currentFrame->matrix4); + break; + case TransformNone: + // nothing to be done + break; + default: + LOG_ALWAYS_FATAL("Tried to compute transform with an invalid type: %d", + currentFrame->type); } } @@ -104,17 +105,17 @@ void DamageAccumulator::popTransform() { DirtyStack* dirtyFrame = mHead; mHead = mHead->prev; switch (dirtyFrame->type) { - case TransformRenderNode: - applyRenderNodeTransform(dirtyFrame); - break; - case TransformMatrix4: - applyMatrix4Transform(dirtyFrame); - break; - case TransformNone: - mHead->pendingDirty.join(dirtyFrame->pendingDirty); - break; - default: - LOG_ALWAYS_FATAL("Tried to pop an invalid type: %d", dirtyFrame->type); + case TransformRenderNode: + applyRenderNodeTransform(dirtyFrame); + break; + case TransformMatrix4: + applyMatrix4Transform(dirtyFrame); + break; + case TransformNone: + mHead->pendingDirty.join(dirtyFrame->pendingDirty); + break; + default: + LOG_ALWAYS_FATAL("Tried to pop an invalid type: %d", dirtyFrame->type); } } @@ -168,8 +169,7 @@ static DirtyStack* findProjectionReceiver(DirtyStack* frame) { if (frame) { while (frame->prev != frame) { frame = frame->prev; - if (frame->type == TransformRenderNode - && frame->renderNode->hasProjectionReceiver()) { + if (frame->type == TransformRenderNode && frame->renderNode->hasProjectionReceiver()) { return frame; } } @@ -233,7 +233,8 @@ void DamageAccumulator::peekAtDirty(SkRect* dest) const { } void DamageAccumulator::finish(SkRect* totalDirty) { - LOG_ALWAYS_FATAL_IF(mHead->prev != mHead, "Cannot finish, mismatched push/pop calls! %p vs. %p", mHead->prev, mHead); + LOG_ALWAYS_FATAL_IF(mHead->prev != mHead, "Cannot finish, mismatched push/pop calls! %p vs. %p", + mHead->prev, mHead); // Root node never has a transform, so this is the fully mapped dirty rect *totalDirty = mHead->pendingDirty; totalDirty->roundOut(totalDirty); diff --git a/libs/hwui/DamageAccumulator.h b/libs/hwui/DamageAccumulator.h index 250296ecc89f..7d0b6877a71a 100644 --- a/libs/hwui/DamageAccumulator.h +++ b/libs/hwui/DamageAccumulator.h @@ -26,7 +26,7 @@ // Smaller than INT_MIN/INT_MAX because we offset these values // and thus don't want to be adding offsets to INT_MAX, that's bad -#define DIRTY_MIN (-0x7ffffff-1) +#define DIRTY_MIN (-0x7ffffff - 1) #define DIRTY_MAX (0x7ffffff) namespace android { @@ -38,6 +38,7 @@ class Matrix4; class DamageAccumulator { PREVENT_COPY_AND_ASSIGN(DamageAccumulator); + public: DamageAccumulator(); // mAllocator will clean everything up for us, no need for a dtor diff --git a/libs/hwui/Debug.h b/libs/hwui/Debug.h index e29699d0faf4..9b1ba69a5377 100644 --- a/libs/hwui/Debug.h +++ b/libs/hwui/Debug.h @@ -102,9 +102,9 @@ #define DEBUG_VECTOR_DRAWABLE 0 #if DEBUG_INIT - #define INIT_LOGD(...) ALOGD(__VA_ARGS__) +#define INIT_LOGD(...) ALOGD(__VA_ARGS__) #else - #define INIT_LOGD(...) +#define INIT_LOGD(...) #endif -#endif // ANDROID_HWUI_DEBUG_H +#endif // ANDROID_HWUI_DEBUG_H diff --git a/libs/hwui/DeferredLayerUpdater.cpp b/libs/hwui/DeferredLayerUpdater.cpp index ff90160b8855..be7d663b412f 100644 --- a/libs/hwui/DeferredLayerUpdater.cpp +++ b/libs/hwui/DeferredLayerUpdater.cpp @@ -26,7 +26,7 @@ namespace android { namespace uirenderer { DeferredLayerUpdater::DeferredLayerUpdater(RenderState& renderState, CreateLayerFn createLayerFn, - Layer::Api layerApi) + Layer::Api layerApi) : mRenderState(renderState) , mBlend(false) , mSurfaceTexture(nullptr) @@ -110,8 +110,8 @@ void DeferredLayerUpdater::apply() { void DeferredLayerUpdater::doUpdateTexImage() { LOG_ALWAYS_FATAL_IF(mLayer->getApi() != Layer::Api::OpenGL, - "doUpdateTexImage non GL backend %x, GL %x, VK %x", - mLayer->getApi(), Layer::Api::OpenGL, Layer::Api::Vulkan); + "doUpdateTexImage non GL backend %x, GL %x, VK %x", mLayer->getApi(), + Layer::Api::OpenGL, Layer::Api::Vulkan); if (mSurfaceTexture->updateTexImage() == NO_ERROR) { float transform[16]; @@ -132,15 +132,15 @@ void DeferredLayerUpdater::doUpdateTexImage() { sp<GraphicBuffer> buffer = mSurfaceTexture->getCurrentBuffer(); if (buffer != nullptr) { // force filtration if buffer size != layer size - forceFilter = mWidth != static_cast<int>(buffer->getWidth()) - || mHeight != static_cast<int>(buffer->getHeight()); + forceFilter = mWidth != static_cast<int>(buffer->getWidth()) || + mHeight != static_cast<int>(buffer->getHeight()); } - #if DEBUG_RENDERER +#if DEBUG_RENDERER if (dropCounter > 0) { RENDERER_LOGD("Dropped %d frames on texture layer update", dropCounter); } - #endif +#endif mSurfaceTexture->getTransformMatrix(transform); updateLayer(forceFilter, transform); @@ -149,8 +149,8 @@ void DeferredLayerUpdater::doUpdateTexImage() { void DeferredLayerUpdater::doUpdateVkTexImage() { LOG_ALWAYS_FATAL_IF(mLayer->getApi() != Layer::Api::Vulkan, - "updateLayer non Vulkan backend %x, GL %x, VK %x", - mLayer->getApi(), Layer::Api::OpenGL, Layer::Api::Vulkan); + "updateLayer non Vulkan backend %x, GL %x, VK %x", mLayer->getApi(), + Layer::Api::OpenGL, Layer::Api::Vulkan); static const mat4 identityMatrix; updateLayer(false, identityMatrix.data); diff --git a/libs/hwui/DeferredLayerUpdater.h b/libs/hwui/DeferredLayerUpdater.h index 6164e4744da5..9dc029fd3852 100644 --- a/libs/hwui/DeferredLayerUpdater.h +++ b/libs/hwui/DeferredLayerUpdater.h @@ -16,10 +16,10 @@ #pragma once -#include <cutils/compiler.h> -#include <gui/GLConsumer.h> #include <SkColorFilter.h> #include <SkMatrix.h> +#include <cutils/compiler.h> +#include <gui/GLConsumer.h> #include <utils/StrongPointer.h> #include <GLES2/gl2.h> @@ -41,10 +41,11 @@ public: // Note that DeferredLayerUpdater assumes it is taking ownership of the layer // and will not call incrementRef on it as a result. typedef std::function<Layer*(RenderState& renderState, uint32_t layerWidth, - uint32_t layerHeight, SkColorFilter* colorFilter, int alpha, - SkBlendMode mode, bool blend)> CreateLayerFn; - ANDROID_API explicit DeferredLayerUpdater(RenderState& renderState, - CreateLayerFn createLayerFn, Layer::Api layerApi); + uint32_t layerHeight, SkColorFilter* colorFilter, int alpha, + SkBlendMode mode, bool blend)> + CreateLayerFn; + ANDROID_API explicit DeferredLayerUpdater(RenderState& renderState, CreateLayerFn createLayerFn, + Layer::Api layerApi); ANDROID_API ~DeferredLayerUpdater(); @@ -74,30 +75,24 @@ public: GLenum target = texture->getCurrentTextureTarget(); LOG_ALWAYS_FATAL_IF(target != GL_TEXTURE_2D && target != GL_TEXTURE_EXTERNAL_OES, - "set unsupported GLConsumer with target %x", target); + "set unsupported GLConsumer with target %x", target); } } - ANDROID_API void updateTexImage() { - mUpdateTexImage = true; - } + ANDROID_API void updateTexImage() { mUpdateTexImage = true; } ANDROID_API void setTransform(const SkMatrix* matrix) { delete mTransform; mTransform = matrix ? new SkMatrix(*matrix) : nullptr; } - SkMatrix* getTransform() { - return mTransform; - } + SkMatrix* getTransform() { return mTransform; } ANDROID_API void setPaint(const SkPaint* paint); void apply(); - Layer* backingLayer() { - return mLayer; - } + Layer* backingLayer() { return mLayer; } void detachSurfaceTexture(); @@ -105,9 +100,7 @@ public: void destroyLayer(); - Layer::Api getBackingLayerApi() { - return mLayerApi; - } + Layer::Api getBackingLayerApi() { return mLayerApi; } private: RenderState& mRenderState; diff --git a/libs/hwui/DeviceInfo.cpp b/libs/hwui/DeviceInfo.cpp index 37965daf9a8d..e4162875279c 100644 --- a/libs/hwui/DeviceInfo.cpp +++ b/libs/hwui/DeviceInfo.cpp @@ -19,8 +19,8 @@ #include <gui/ISurfaceComposer.h> #include <gui/SurfaceComposerClient.h> -#include <thread> #include <mutex> +#include <thread> #include <log/log.h> @@ -58,8 +58,7 @@ void DeviceInfo::load() { } void DeviceInfo::loadDisplayInfo() { - sp<IBinder> dtoken(SurfaceComposerClient::getBuiltInDisplay( - ISurfaceComposer::eDisplayIdMain)); + sp<IBinder> dtoken(SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain)); status_t status = SurfaceComposerClient::getDisplayInfo(dtoken, &mDisplayInfo); LOG_ALWAYS_FATAL_IF(status, "Failed to get display info, error %d", status); } diff --git a/libs/hwui/DeviceInfo.h b/libs/hwui/DeviceInfo.h index 5bd7b14b156d..a0b2f06d8f4a 100644 --- a/libs/hwui/DeviceInfo.h +++ b/libs/hwui/DeviceInfo.h @@ -18,14 +18,15 @@ #include <ui/DisplayInfo.h> -#include "utils/Macros.h" #include "Extensions.h" +#include "utils/Macros.h" namespace android { namespace uirenderer { class DeviceInfo { PREVENT_COPY_AND_ASSIGN(DeviceInfo); + public: // returns nullptr if DeviceInfo is not initialized yet // Note this does not have a memory fence so it's up to the caller diff --git a/libs/hwui/DisplayList.cpp b/libs/hwui/DisplayList.cpp index 0ff101c6b2b4..aa87aea8b374 100644 --- a/libs/hwui/DisplayList.cpp +++ b/libs/hwui/DisplayList.cpp @@ -44,8 +44,7 @@ DisplayList::DisplayList() , regions(stdAllocator) , referenceHolders(stdAllocator) , functors(stdAllocator) - , vectorDrawables(stdAllocator) { -} + , vectorDrawables(stdAllocator) {} DisplayList::~DisplayList() { cleanupResources(); @@ -105,14 +104,16 @@ void DisplayList::updateChildren(std::function<void(RenderNode*)> updateFn) { } } -bool DisplayList::prepareListAndChildren(TreeObserver& observer, TreeInfo& info, bool functorsNeedLayer, +bool DisplayList::prepareListAndChildren( + TreeObserver& observer, TreeInfo& info, bool functorsNeedLayer, std::function<void(RenderNode*, TreeObserver&, TreeInfo&, bool)> childFn) { info.prepareTextures = info.canvasContext.pinImages(bitmapResources); for (auto&& op : children) { RenderNode* childNode = op->renderNode; info.damageAccumulator->pushTransform(&op->localMatrix); - bool childFunctorsNeedLayer = functorsNeedLayer; // TODO! || op->mRecordedWithPotentialStencilClip; + bool childFunctorsNeedLayer = + functorsNeedLayer; // TODO! || op->mRecordedWithPotentialStencilClip; childFn(childNode, observer, info, childFunctorsNeedLayer); info.damageAccumulator->popTransform(); } @@ -140,5 +141,5 @@ void DisplayList::output(std::ostream& output, uint32_t level) { } } -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/DisplayList.h b/libs/hwui/DisplayList.h index d22a764c0ea5..0cfc3b701aff 100644 --- a/libs/hwui/DisplayList.h +++ b/libs/hwui/DisplayList.h @@ -32,8 +32,8 @@ #include <androidfw/ResourceTypes.h> -#include "Debug.h" #include "CanvasProperty.h" +#include "Debug.h" #include "GlFunctorLifecycleListener.h" #include "Matrix.h" #include "RenderProperties.h" @@ -74,6 +74,7 @@ struct FunctorContainer { */ class DisplayList { friend class RecordingCanvas; + public: struct Chunk { // range of included ops in DisplayList::ops() @@ -106,14 +107,9 @@ public: size_t addChild(NodeOpType* childOp); + void ref(VirtualLightRefBase* prop) { referenceHolders.push_back(prop); } - void ref(VirtualLightRefBase* prop) { - referenceHolders.push_back(prop); - } - - size_t getUsedSize() { - return allocator.usedSize(); - } + size_t getUsedSize() { return allocator.usedSize(); } virtual bool isEmpty() const { return ops.empty(); } virtual bool hasFunctor() const { return !functors.empty(); } @@ -125,7 +121,8 @@ public: virtual void syncContents(); virtual void updateChildren(std::function<void(RenderNode*)> updateFn); - virtual bool prepareListAndChildren(TreeObserver& observer, TreeInfo& info, bool functorsNeedLayer, + virtual bool prepareListAndChildren( + TreeObserver& observer, TreeInfo& info, bool functorsNeedLayer, std::function<void(RenderNode*, TreeObserver&, TreeInfo&, bool)> childFn); virtual void output(std::ostream& output, uint32_t level); @@ -148,12 +145,13 @@ private: LsaVector<const Res_png_9patch*> patchResources; LsaVector<std::unique_ptr<const SkPaint>> paints; LsaVector<std::unique_ptr<const SkRegion>> regions; - LsaVector< sp<VirtualLightRefBase> > referenceHolders; + LsaVector<sp<VirtualLightRefBase>> referenceHolders; // List of functors LsaVector<FunctorContainer> functors; - // List of VectorDrawables that need to be notified of pushStaging. Note that this list gets nothing + // List of VectorDrawables that need to be notified of pushStaging. Note that this list gets + // nothing // but a callback during sync DisplayList, unlike the list of functors defined above, which // gets special treatment exclusive for webview. LsaVector<VectorDrawableRoot*> vectorDrawables; @@ -161,5 +159,5 @@ private: void cleanupResources(); }; -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/Extensions.cpp b/libs/hwui/Extensions.cpp index 1e71cb081b39..530e82e65a28 100644 --- a/libs/hwui/Extensions.cpp +++ b/libs/hwui/Extensions.cpp @@ -31,7 +31,10 @@ namespace android { namespace uirenderer { Extensions::Extensions() { - const char* version = (const char*) glGetString(GL_VERSION); + if (Properties::isSkiaEnabled()) { + return; + } + const char* version = (const char*)glGetString(GL_VERSION); // Section 6.1.5 of the OpenGL ES specification indicates the GL version // string strictly follows this format: @@ -51,7 +54,7 @@ Extensions::Extensions() { mVersionMinor = 0; } - auto extensions = StringUtils::split((const char*) glGetString(GL_EXTENSIONS)); + auto extensions = StringUtils::split((const char*)glGetString(GL_EXTENSIONS)); mHasNPot = extensions.has("GL_OES_texture_npot"); mHasFramebufferFetch = extensions.has("GL_NV_shader_framebuffer_fetch"); mHasDiscardFramebuffer = extensions.has("GL_EXT_discard_framebuffer"); @@ -59,6 +62,7 @@ Extensions::Extensions() { mHas1BitStencil = extensions.has("GL_OES_stencil1"); mHas4BitStencil = extensions.has("GL_OES_stencil4"); mHasUnpackSubImage = extensions.has("GL_EXT_unpack_subimage"); + mHasRenderableFloatTexture = extensions.has("GL_OES_texture_half_float"); mHasSRGB = mVersionMajor >= 3 || extensions.has("GL_EXT_sRGB"); mHasSRGBWriteControl = extensions.has("GL_EXT_sRGB_write_control"); @@ -75,5 +79,5 @@ Extensions::Extensions() { #endif } -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/Extensions.h b/libs/hwui/Extensions.h index 0ecfdb1b3e0a..214ee0bbeefd 100644 --- a/libs/hwui/Extensions.h +++ b/libs/hwui/Extensions.h @@ -38,6 +38,9 @@ public: inline bool hasPixelBufferObjects() const { return mVersionMajor >= 3; } inline bool hasOcclusionQueries() const { return mVersionMajor >= 3; } inline bool hasFloatTextures() const { return mVersionMajor >= 3; } + inline bool hasRenderableFloatTextures() const { + return (mVersionMajor >= 3 && mVersionMinor >= 2) || mHasRenderableFloatTexture; + } inline bool hasSRGB() const { return mHasSRGB; } inline bool hasSRGBWriteControl() const { return hasSRGB() && mHasSRGBWriteControl; } inline bool hasLinearBlending() const { return hasSRGB() && mHasLinearBlending; } @@ -56,12 +59,13 @@ private: bool mHasSRGB; bool mHasSRGBWriteControl; bool mHasLinearBlending; + bool mHasRenderableFloatTexture; int mVersionMajor; int mVersionMinor; -}; // class Extensions +}; // class Extensions -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android -#endif // ANDROID_HWUI_EXTENSIONS_H +#endif // ANDROID_HWUI_EXTENSIONS_H diff --git a/libs/hwui/FboCache.cpp b/libs/hwui/FboCache.cpp index a39e49f82eb0..88302cc52c2b 100644 --- a/libs/hwui/FboCache.cpp +++ b/libs/hwui/FboCache.cpp @@ -27,8 +27,7 @@ namespace uirenderer { // Constructors/destructor /////////////////////////////////////////////////////////////////////////////// -FboCache::FboCache() - : mMaxSize(0) {} +FboCache::FboCache() : mMaxSize(0) {} FboCache::~FboCache() { clear(); @@ -79,5 +78,5 @@ bool FboCache::put(GLuint fbo) { return false; } -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/FboCache.h b/libs/hwui/FboCache.h index ad6cc3ea96c9..5e8bb0c7a7a7 100644 --- a/libs/hwui/FboCache.h +++ b/libs/hwui/FboCache.h @@ -71,9 +71,9 @@ public: private: SortedVector<GLuint> mCache; uint32_t mMaxSize; -}; // class FboCache +}; // class FboCache -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android -#endif // ANDROID_HWUI_FBO_CACHE_H +#endif // ANDROID_HWUI_FBO_CACHE_H diff --git a/libs/hwui/FloatColor.h b/libs/hwui/FloatColor.h index a738ba4fe97e..b424f97a5004 100644 --- a/libs/hwui/FloatColor.h +++ b/libs/hwui/FloatColor.h @@ -33,8 +33,8 @@ struct FloatColor { void set(uint32_t color) { a = ((color >> 24) & 0xff) / 255.0f; r = a * EOCF(((color >> 16) & 0xff) / 255.0f); - g = a * EOCF(((color >> 8) & 0xff) / 255.0f); - b = a * EOCF(((color ) & 0xff) / 255.0f); + g = a * EOCF(((color >> 8) & 0xff) / 255.0f); + b = a * EOCF(((color)&0xff) / 255.0f); } // "color" is a gamma-encoded sRGB color @@ -44,27 +44,18 @@ struct FloatColor { void setUnPreMultiplied(uint32_t color) { a = ((color >> 24) & 0xff) / 255.0f; r = EOCF(((color >> 16) & 0xff) / 255.0f); - g = EOCF(((color >> 8) & 0xff) / 255.0f); - b = EOCF(((color ) & 0xff) / 255.0f); + g = EOCF(((color >> 8) & 0xff) / 255.0f); + b = EOCF(((color)&0xff) / 255.0f); } - bool isNotBlack() { - return a < 1.0f - || r > 0.0f - || g > 0.0f - || b > 0.0f; - } + bool isNotBlack() { return a < 1.0f || r > 0.0f || g > 0.0f || b > 0.0f; } bool operator==(const FloatColor& other) const { - return MathUtils::areEqual(r, other.r) - && MathUtils::areEqual(g, other.g) - && MathUtils::areEqual(b, other.b) - && MathUtils::areEqual(a, other.a); + return MathUtils::areEqual(r, other.r) && MathUtils::areEqual(g, other.g) && + MathUtils::areEqual(b, other.b) && MathUtils::areEqual(a, other.a); } - bool operator!=(const FloatColor& other) const { - return !(*this == other); - } + bool operator!=(const FloatColor& other) const { return !(*this == other); } float r; float g; diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp index 8b0346867cbc..bbcedb15335d 100644 --- a/libs/hwui/FontRenderer.cpp +++ b/libs/hwui/FontRenderer.cpp @@ -22,20 +22,20 @@ #include "Caches.h" #include "Debug.h" #include "Extensions.h" -#include "font/Font.h" #include "Glop.h" #include "GlopBuilder.h" #include "PixelBuffer.h" #include "Rect.h" +#include "font/Font.h" #include "renderstate/RenderState.h" #include "utils/Blur.h" #include "utils/Timing.h" -#include <algorithm> #include <RenderScript.h> #include <SkGlyph.h> #include <SkUtils.h> #include <utils/Log.h> +#include <algorithm> namespace android { namespace uirenderer { @@ -55,8 +55,8 @@ void TextDrawFunctor::draw(CacheTexture& texture, bool linearFiltering) { if (linearFiltering) { textureFillFlags |= TextureFillFlags::ForceFilter; } - int transformFlags = pureTranslate - ? TransformFlags::MeshIgnoresCanvasTransform : TransformFlags::None; + int transformFlags = + pureTranslate ? TransformFlags::MeshIgnoresCanvasTransform : TransformFlags::None; #ifdef ANDROID_ENABLE_LINEAR_BLENDING bool gammaCorrection = true; #else @@ -93,7 +93,6 @@ FontRenderer::FontRenderer(const uint8_t* gammaTable) , mDrawn(false) , mInitialized(false) , mLinearFiltering(false) { - if (sLogFontRendererCreate) { INIT_LOGD("Creating FontRenderer"); } @@ -118,10 +117,8 @@ FontRenderer::FontRenderer(const uint8_t* gammaTable) if (sLogFontRendererCreate) { INIT_LOGD(" Text cache sizes, in pixels: %i x %i, %i x %i, %i x %i, %i x %i", - mSmallCacheWidth, mSmallCacheHeight, - mLargeCacheWidth, mLargeCacheHeight >> 1, - mLargeCacheWidth, mLargeCacheHeight >> 1, - mLargeCacheWidth, mLargeCacheHeight); + mSmallCacheWidth, mSmallCacheHeight, mLargeCacheWidth, mLargeCacheHeight >> 1, + mLargeCacheWidth, mLargeCacheHeight >> 1, mLargeCacheWidth, mLargeCacheHeight); } sLogFontRendererCreate = false; @@ -195,7 +192,8 @@ void FontRenderer::flushLargeCaches() { } CacheTexture* FontRenderer::cacheBitmapInTexture(std::vector<CacheTexture*>& cacheTextures, - const SkGlyph& glyph, uint32_t* startX, uint32_t* startY) { + const SkGlyph& glyph, uint32_t* startX, + uint32_t* startY) { for (uint32_t i = 0; i < cacheTextures.size(); i++) { if (cacheTextures[i]->fitBitmap(glyph, startX, startY)) { return cacheTextures[i]; @@ -206,7 +204,7 @@ CacheTexture* FontRenderer::cacheBitmapInTexture(std::vector<CacheTexture*>& cac } void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyph, - uint32_t* retOriginX, uint32_t* retOriginY, bool precaching) { + uint32_t* retOriginX, uint32_t* retOriginY, bool precaching) { checkInit(); // If the glyph bitmap is empty let's assum the glyph is valid @@ -234,14 +232,14 @@ void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyp #if DEBUG_FONT_RENDERER ALOGD("getCacheTexturesForFormat: unknown SkMask format %x", format); #endif - return; + return; } // If the glyph is too tall, don't cache it if (glyph.fHeight + TEXTURE_BORDER_SIZE * 2 > - (*cacheTextures)[cacheTextures->size() - 1]->getHeight()) { - ALOGE("Font size too large to fit in cache. width, height = %i, %i", - (int) glyph.fWidth, (int) glyph.fHeight); + (*cacheTextures)[cacheTextures->size() - 1]->getHeight()) { + ALOGE("Font size too large to fit in cache. width, height = %i, %i", (int)glyph.fWidth, + (int)glyph.fHeight); return; } @@ -285,14 +283,14 @@ void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyp } uint8_t* cacheBuffer = cacheTexture->getPixelBuffer()->map(); - uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage; + uint8_t* bitmapBuffer = (uint8_t*)glyph.fImage; int srcStride = glyph.rowBytes(); // Copy the glyph image, taking the mask format into account switch (format) { case SkMask::kA8_Format: { - uint32_t row = (startY - TEXTURE_BORDER_SIZE) * cacheWidth + startX - - TEXTURE_BORDER_SIZE; + uint32_t row = + (startY - TEXTURE_BORDER_SIZE) * cacheWidth + startX - TEXTURE_BORDER_SIZE; // write leading border line memset(&cacheBuffer[row], 0, glyph.fWidth + 2 * TEXTURE_BORDER_SIZE); // write glyph data @@ -337,9 +335,9 @@ void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyp memset(dstL, 0, rowSize + 2 * borderSize); // write glyph data while (dst < dstEnd) { - memset(dstL += dstStride, 0, borderSize); // leading border column - memcpy(dst += dstStride, src += srcStride, rowSize); // glyph data - memset(dstR += dstStride, 0, borderSize); // trailing border column + memset(dstL += dstStride, 0, borderSize); // leading border column + memcpy(dst += dstStride, src += srcStride, rowSize); // glyph data + memset(dstR += dstStride, 0, borderSize); // trailing border column } // write trailing border line memset(dstL += dstStride, 0, rowSize + 2 * borderSize); @@ -347,9 +345,9 @@ void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyp } case SkMask::kBW_Format: { uint32_t cacheX = 0, cacheY = 0; - uint32_t row = (startY - TEXTURE_BORDER_SIZE) * cacheWidth + startX - - TEXTURE_BORDER_SIZE; - static const uint8_t COLORS[2] = { 0, 255 }; + uint32_t row = + (startY - TEXTURE_BORDER_SIZE) * cacheWidth + startX - TEXTURE_BORDER_SIZE; + static const uint8_t COLORS[2] = {0, 255}; // write leading border line memset(&cacheBuffer[row], 0, glyph.fWidth + 2 * TEXTURE_BORDER_SIZE); // write glyph data @@ -388,7 +386,7 @@ void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyp } CacheTexture* FontRenderer::createCacheTexture(int width, int height, GLenum format, - bool allocate) { + bool allocate) { CacheTexture* cacheTexture = new CacheTexture(width, height, format, kMaxNumberOfQuads); if (allocate) { @@ -405,18 +403,18 @@ void FontRenderer::initTextTexture() { clearCacheTextures(mRGBACacheTextures); mUploadTexture = false; - mACacheTextures.push_back(createCacheTexture(mSmallCacheWidth, mSmallCacheHeight, - GL_ALPHA, true)); - mACacheTextures.push_back(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1, - GL_ALPHA, false)); - mACacheTextures.push_back(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1, - GL_ALPHA, false)); - mACacheTextures.push_back(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight, - GL_ALPHA, false)); - mRGBACacheTextures.push_back(createCacheTexture(mSmallCacheWidth, mSmallCacheHeight, - GL_RGBA, false)); - mRGBACacheTextures.push_back(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1, - GL_RGBA, false)); + mACacheTextures.push_back( + createCacheTexture(mSmallCacheWidth, mSmallCacheHeight, GL_ALPHA, true)); + mACacheTextures.push_back( + createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1, GL_ALPHA, false)); + mACacheTextures.push_back( + createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1, GL_ALPHA, false)); + mACacheTextures.push_back( + createCacheTexture(mLargeCacheWidth, mLargeCacheHeight, GL_ALPHA, false)); + mRGBACacheTextures.push_back( + createCacheTexture(mSmallCacheWidth, mSmallCacheHeight, GL_RGBA, false)); + mRGBACacheTextures.push_back( + createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1, GL_RGBA, false)); mCurrentCacheTexture = mACacheTextures[0]; } @@ -432,7 +430,7 @@ void FontRenderer::checkInit() { } void checkTextureUpdateForCache(Caches& caches, std::vector<CacheTexture*>& cacheTextures, - bool& resetPixelStore, GLuint& lastTextureId) { + bool& resetPixelStore, GLuint& lastTextureId) { for (uint32_t i = 0; i < cacheTextures.size(); i++) { CacheTexture* cacheTexture = cacheTextures[i]; if (cacheTexture->isDirty() && cacheTexture->getPixelBuffer()) { @@ -500,24 +498,22 @@ void FontRenderer::issueDrawCommand() { issueDrawCommand(mRGBACacheTextures); } -void FontRenderer::appendMeshQuadNoClip(float x1, float y1, float u1, float v1, - float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3, - float x4, float y4, float u4, float v4, CacheTexture* texture) { +void FontRenderer::appendMeshQuadNoClip(float x1, float y1, float u1, float v1, float x2, float y2, + float u2, float v2, float x3, float y3, float u3, float v3, + float x4, float y4, float u4, float v4, + CacheTexture* texture) { if (texture != mCurrentCacheTexture) { // Now use the new texture id mCurrentCacheTexture = texture; } - mCurrentCacheTexture->addQuad(x1, y1, u1, v1, x2, y2, u2, v2, - x3, y3, u3, v3, x4, y4, u4, v4); + mCurrentCacheTexture->addQuad(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4); } -void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1, - float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3, - float x4, float y4, float u4, float v4, CacheTexture* texture) { - - if (mClip && - (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) { +void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1, float x2, float y2, + float u2, float v2, float x3, float y3, float u3, float v3, + float x4, float y4, float u4, float v4, CacheTexture* texture) { + if (mClip && (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) { return; } @@ -535,10 +531,10 @@ void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1, } } -void FontRenderer::appendRotatedMeshQuad(float x1, float y1, float u1, float v1, - float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3, - float x4, float y4, float u4, float v4, CacheTexture* texture) { - +void FontRenderer::appendRotatedMeshQuad(float x1, float y1, float u1, float v1, float x2, float y2, + float u2, float v2, float x3, float y3, float u3, float v3, + float x4, float y4, float u4, float v4, + CacheTexture* texture) { appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture); if (mBounds) { @@ -557,8 +553,9 @@ void FontRenderer::setFont(const SkPaint* paint, const SkMatrix& matrix) { mCurrentFont = Font::create(this, paint, matrix); } -FontRenderer::DropShadow FontRenderer::renderDropShadow(const SkPaint* paint, const glyph_t *glyphs, - int numGlyphs, float radius, const float* positions) { +FontRenderer::DropShadow FontRenderer::renderDropShadow(const SkPaint* paint, const glyph_t* glyphs, + int numGlyphs, float radius, + const float* positions) { checkInit(); DropShadow image; @@ -580,8 +577,8 @@ FontRenderer::DropShadow FontRenderer::renderDropShadow(const SkPaint* paint, co mCurrentFont->measure(paint, glyphs, numGlyphs, &bounds, positions); uint32_t intRadius = Blur::convertRadiusToInt(radius); - uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * intRadius; - uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * intRadius; + uint32_t paddedWidth = (uint32_t)(bounds.right - bounds.left) + 2 * intRadius; + uint32_t paddedHeight = (uint32_t)(bounds.top - bounds.bottom) + 2 * intRadius; uint32_t maxSize = Caches::getInstance().maxTextureSize; if (paddedWidth > maxSize || paddedHeight > maxSize) { @@ -593,7 +590,7 @@ FontRenderer::DropShadow FontRenderer::renderDropShadow(const SkPaint* paint, co paddedWidth += RS_CPU_ALLOCATION_ALIGNMENT - paddedWidth % RS_CPU_ALLOCATION_ALIGNMENT; } int size = paddedWidth * paddedHeight; - uint8_t* dataBuffer = (uint8_t*) memalign(RS_CPU_ALLOCATION_ALIGNMENT, size); + uint8_t* dataBuffer = (uint8_t*)memalign(RS_CPU_ALLOCATION_ALIGNMENT, size); memset(dataBuffer, 0, size); @@ -604,8 +601,8 @@ FontRenderer::DropShadow FontRenderer::renderDropShadow(const SkPaint* paint, co // text has non-whitespace, so draw and blur to create the shadow // NOTE: bounds.isEmpty() can't be used here, since vertical coordinates are inverted // TODO: don't draw pure whitespace in the first place, and avoid needing this check - mCurrentFont->render(paint, glyphs, numGlyphs, penX, penY, - Font::BITMAP, dataBuffer, paddedWidth, paddedHeight, nullptr, positions); + mCurrentFont->render(paint, glyphs, numGlyphs, penX, penY, Font::BITMAP, dataBuffer, + paddedWidth, paddedHeight, nullptr, positions); // Unbind any PBO we might have used Caches::getInstance().pixelBufferState().unbind(); @@ -639,7 +636,7 @@ void FontRenderer::finishRender() { } void FontRenderer::precache(const SkPaint* paint, const glyph_t* glyphs, int numGlyphs, - const SkMatrix& matrix) { + const SkMatrix& matrix) { Font* font = Font::create(this, paint, matrix); font->precache(paint, glyphs, numGlyphs); } @@ -649,8 +646,8 @@ void FontRenderer::endPrecaching() { } bool FontRenderer::renderPosText(const SkPaint* paint, const Rect* clip, const glyph_t* glyphs, - int numGlyphs, int x, int y, const float* positions, - Rect* bounds, TextDrawFunctor* functor, bool forceFinish) { + int numGlyphs, int x, int y, const float* positions, Rect* bounds, + TextDrawFunctor* functor, bool forceFinish) { if (!mCurrentFont) { ALOGE("No font set"); return false; @@ -667,8 +664,8 @@ bool FontRenderer::renderPosText(const SkPaint* paint, const Rect* clip, const g } bool FontRenderer::renderTextOnPath(const SkPaint* paint, const Rect* clip, const glyph_t* glyphs, - int numGlyphs, const SkPath* path, float hOffset, float vOffset, - Rect* bounds, TextDrawFunctor* functor) { + int numGlyphs, const SkPath* path, float hOffset, float vOffset, + Rect* bounds, TextDrawFunctor* functor) { if (!mCurrentFont) { ALOGE("No font set"); return false; @@ -684,7 +681,7 @@ bool FontRenderer::renderTextOnPath(const SkPaint* paint, const Rect* clip, cons void FontRenderer::blurImage(uint8_t** image, int32_t width, int32_t height, float radius) { uint32_t intRadius = Blur::convertRadiusToInt(radius); if (width * height * intRadius >= RS_MIN_INPUT_CUTOFF && radius <= 25.0f) { - uint8_t* outImage = (uint8_t*) memalign(RS_CPU_ALLOCATION_ALIGNMENT, width * height); + uint8_t* outImage = (uint8_t*)memalign(RS_CPU_ALLOCATION_ALIGNMENT, width * height); if (mRs == nullptr) { mRs = new RSC::RS(); @@ -700,14 +697,12 @@ void FontRenderer::blurImage(uint8_t** image, int32_t width, int32_t height, flo } if (mRs != nullptr) { RSC::sp<const RSC::Type> t = RSC::Type::create(mRs, mRsElement, width, height, 0); - RSC::sp<RSC::Allocation> ain = RSC::Allocation::createTyped(mRs, t, - RS_ALLOCATION_MIPMAP_NONE, - RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED, - *image); - RSC::sp<RSC::Allocation> aout = RSC::Allocation::createTyped(mRs, t, - RS_ALLOCATION_MIPMAP_NONE, - RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED, - outImage); + RSC::sp<RSC::Allocation> ain = RSC::Allocation::createTyped( + mRs, t, RS_ALLOCATION_MIPMAP_NONE, + RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED, *image); + RSC::sp<RSC::Allocation> aout = RSC::Allocation::createTyped( + mRs, t, RS_ALLOCATION_MIPMAP_NONE, + RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED, outImage); mRsScript->setRadius(radius); mRsScript->setInput(ain); @@ -768,7 +763,7 @@ const std::vector<CacheTexture*>& FontRenderer::cacheTexturesForFormat(GLenum fo } static void dumpTextures(String8& log, const char* tag, - const std::vector<CacheTexture*>& cacheTextures) { + const std::vector<CacheTexture*>& cacheTextures) { for (uint32_t i = 0; i < cacheTextures.size(); i++) { CacheTexture* cacheTexture = cacheTextures[i]; if (cacheTexture && cacheTexture->getPixelBuffer()) { @@ -803,5 +798,5 @@ uint32_t FontRenderer::getSize() const { return getCacheSize(GL_ALPHA) + getCacheSize(GL_RGBA); } -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/FontRenderer.h b/libs/hwui/FontRenderer.h index 329309c1cd5c..6b9dec4719cb 100644 --- a/libs/hwui/FontRenderer.h +++ b/libs/hwui/FontRenderer.h @@ -16,10 +16,10 @@ #pragma once -#include "font/FontUtil.h" #include "font/CacheTexture.h" #include "font/CachedGlyphInfo.h" #include "font/Font.h" +#include "font/FontUtil.h" #ifdef BUGREPORT_FONT_CACHE_USAGE #include "font/FontCacheHistoryTracker.h" #endif @@ -36,10 +36,10 @@ #include "RenderScript.h" namespace RSC { - class Element; - class RS; - class ScriptIntrinsicBlur; - class sp; +class Element; +class RS; +class ScriptIntrinsicBlur; +class sp; } namespace android { @@ -51,22 +51,18 @@ struct ClipBase; class TextDrawFunctor { public: - TextDrawFunctor( - BakedOpRenderer* renderer, - const BakedOpState* bakedState, - const ClipBase* clip, - float x, float y, bool pureTranslate, - int alpha, SkBlendMode mode, const SkPaint* paint) - : renderer(renderer) - , bakedState(bakedState) - , clip(clip) - , x(x) - , y(y) - , pureTranslate(pureTranslate) - , alpha(alpha) - , mode(mode) - , paint(paint) { - } + TextDrawFunctor(BakedOpRenderer* renderer, const BakedOpState* bakedState, const ClipBase* clip, + float x, float y, bool pureTranslate, int alpha, SkBlendMode mode, + const SkPaint* paint) + : renderer(renderer) + , bakedState(bakedState) + , clip(clip) + , x(x) + , y(y) + , pureTranslate(pureTranslate) + , alpha(alpha) + , mode(mode) + , paint(paint) {} void draw(CacheTexture& texture, bool linearFiltering); @@ -91,16 +87,17 @@ public: void setFont(const SkPaint* paint, const SkMatrix& matrix); - void precache(const SkPaint* paint, const glyph_t* glyphs, int numGlyphs, const SkMatrix& matrix); + void precache(const SkPaint* paint, const glyph_t* glyphs, int numGlyphs, + const SkMatrix& matrix); void endPrecaching(); - bool renderPosText(const SkPaint* paint, const Rect* clip, const glyph_t* glyphs, - int numGlyphs, int x, int y, const float* positions, - Rect* outBounds, TextDrawFunctor* functor, bool forceFinish = true); + bool renderPosText(const SkPaint* paint, const Rect* clip, const glyph_t* glyphs, int numGlyphs, + int x, int y, const float* positions, Rect* outBounds, + TextDrawFunctor* functor, bool forceFinish = true); bool renderTextOnPath(const SkPaint* paint, const Rect* clip, const glyph_t* glyphs, - int numGlyphs, const SkPath* path, - float hOffset, float vOffset, Rect* outBounds, TextDrawFunctor* functor); + int numGlyphs, const SkPath* path, float hOffset, float vOffset, + Rect* outBounds, TextDrawFunctor* functor); struct DropShadow { uint32_t width; @@ -112,12 +109,10 @@ public: // After renderDropShadow returns, the called owns the memory in DropShadow.image // and is responsible for releasing it when it's done with it - DropShadow renderDropShadow(const SkPaint* paint, const glyph_t *glyphs, int numGlyphs, - float radius, const float* positions); + DropShadow renderDropShadow(const SkPaint* paint, const glyph_t* glyphs, int numGlyphs, + float radius, const float* positions); - void setTextureFiltering(bool linearFiltering) { - mLinearFiltering = linearFiltering; - } + void setTextureFiltering(bool linearFiltering) { mLinearFiltering = linearFiltering; } uint32_t getSize() const; void dumpMemoryUsage(String8& log) const; @@ -135,10 +130,10 @@ private: void deallocateTextureMemory(CacheTexture* cacheTexture); void initTextTexture(); CacheTexture* createCacheTexture(int width, int height, GLenum format, bool allocate); - void cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyph, - uint32_t *retOriginX, uint32_t *retOriginY, bool precaching); - CacheTexture* cacheBitmapInTexture(std::vector<CacheTexture*>& cacheTextures, const SkGlyph& glyph, - uint32_t* startX, uint32_t* startY); + void cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyph, uint32_t* retOriginX, + uint32_t* retOriginY, bool precaching); + CacheTexture* cacheBitmapInTexture(std::vector<CacheTexture*>& cacheTextures, + const SkGlyph& glyph, uint32_t* startX, uint32_t* startY); void flushAllAndInvalidate(); @@ -148,24 +143,19 @@ private: void issueDrawCommand(std::vector<CacheTexture*>& cacheTextures); void issueDrawCommand(); - void appendMeshQuadNoClip(float x1, float y1, float u1, float v1, - float x2, float y2, float u2, float v2, - float x3, float y3, float u3, float v3, - float x4, float y4, float u4, float v4, CacheTexture* texture); - void appendMeshQuad(float x1, float y1, float u1, float v1, - float x2, float y2, float u2, float v2, - float x3, float y3, float u3, float v3, - float x4, float y4, float u4, float v4, CacheTexture* texture); - void appendRotatedMeshQuad(float x1, float y1, float u1, float v1, - float x2, float y2, float u2, float v2, - float x3, float y3, float u3, float v3, - float x4, float y4, float u4, float v4, CacheTexture* texture); + void appendMeshQuadNoClip(float x1, float y1, float u1, float v1, float x2, float y2, float u2, + float v2, float x3, float y3, float u3, float v3, float x4, float y4, + float u4, float v4, CacheTexture* texture); + void appendMeshQuad(float x1, float y1, float u1, float v1, float x2, float y2, float u2, + float v2, float x3, float y3, float u3, float v3, float x4, float y4, + float u4, float v4, CacheTexture* texture); + void appendRotatedMeshQuad(float x1, float y1, float u1, float v1, float x2, float y2, float u2, + float v2, float x3, float y3, float u3, float v3, float x4, float y4, + float u4, float v4, CacheTexture* texture); void checkTextureUpdate(); - void setTextureDirty() { - mUploadTexture = true; - } + void setTextureDirty() { mUploadTexture = true; } const std::vector<CacheTexture*>& cacheTexturesForFormat(GLenum format) const; uint32_t getCacheSize(GLenum format) const; @@ -205,14 +195,14 @@ private: RSC::sp<RSC::ScriptIntrinsicBlur> mRsScript; static void computeGaussianWeights(float* weights, int32_t radius); - static void horizontalBlur(float* weights, int32_t radius, const uint8_t *source, uint8_t *dest, - int32_t width, int32_t height); - static void verticalBlur(float* weights, int32_t radius, const uint8_t *source, uint8_t *dest, - int32_t width, int32_t height); + static void horizontalBlur(float* weights, int32_t radius, const uint8_t* source, uint8_t* dest, + int32_t width, int32_t height); + static void verticalBlur(float* weights, int32_t radius, const uint8_t* source, uint8_t* dest, + int32_t width, int32_t height); // the input image handle may have its pointer replaced (to avoid copies) void blurImage(uint8_t** image, int32_t width, int32_t height, float radius); }; -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/FrameBuilder.cpp b/libs/hwui/FrameBuilder.cpp index 86f9a5d73fd1..ced37ede0746 100644 --- a/libs/hwui/FrameBuilder.cpp +++ b/libs/hwui/FrameBuilder.cpp @@ -20,8 +20,8 @@ #include "LayerUpdateQueue.h" #include "RenderNode.h" #include "VectorDrawable.h" -#include "renderstate/OffscreenBufferPool.h" #include "hwui/Canvas.h" +#include "renderstate/OffscreenBufferPool.h" #include "utils/FatVector.h" #include "utils/PaintUtils.h" #include "utils/TraceUtils.h" @@ -32,9 +32,8 @@ namespace android { namespace uirenderer { -FrameBuilder::FrameBuilder(const SkRect& clip, - uint32_t viewportWidth, uint32_t viewportHeight, - const LightGeometry& lightGeometry, Caches& caches) +FrameBuilder::FrameBuilder(const SkRect& clip, uint32_t viewportWidth, uint32_t viewportHeight, + const LightGeometry& lightGeometry, Caches& caches) : mStdAllocator(mAllocator) , mLayerBuilders(mStdAllocator) , mLayerStack(mStdAllocator) @@ -42,18 +41,16 @@ FrameBuilder::FrameBuilder(const SkRect& clip, , mCaches(caches) , mLightRadius(lightGeometry.radius) , mDrawFbo0(true) { - // Prepare to defer Fbo0 auto fbo0 = mAllocator.create<LayerBuilder>(viewportWidth, viewportHeight, Rect(clip)); mLayerBuilders.push_back(fbo0); mLayerStack.push_back(0); - mCanvasState.initializeSaveStack(viewportWidth, viewportHeight, - clip.fLeft, clip.fTop, clip.fRight, clip.fBottom, - lightGeometry.center); + mCanvasState.initializeSaveStack(viewportWidth, viewportHeight, clip.fLeft, clip.fTop, + clip.fRight, clip.fBottom, lightGeometry.center); } -FrameBuilder::FrameBuilder(const LayerUpdateQueue& layers, - const LightGeometry& lightGeometry, Caches& caches) +FrameBuilder::FrameBuilder(const LayerUpdateQueue& layers, const LightGeometry& lightGeometry, + Caches& caches) : mStdAllocator(mAllocator) , mLayerBuilders(mStdAllocator) , mLayerStack(mStdAllocator) @@ -67,9 +64,7 @@ FrameBuilder::FrameBuilder(const LayerUpdateQueue& layers, auto fbo0 = mAllocator.create<LayerBuilder>(1, 1, Rect(1, 1)); mLayerBuilders.push_back(fbo0); mLayerStack.push_back(0); - mCanvasState.initializeSaveStack(1, 1, - 0, 0, 1, 1, - lightGeometry.center); + mCanvasState.initializeSaveStack(1, 1, 0, 0, 1, 1, lightGeometry.center); deferLayers(layers); } @@ -84,8 +79,8 @@ void FrameBuilder::deferLayers(const LayerUpdateQueue& layers) { // as not to lose info on what portion is damaged OffscreenBuffer* layer = layerNode->getLayer(); if (CC_LIKELY(layer)) { - ATRACE_FORMAT("Optimize HW Layer DisplayList %s %ux%u", - layerNode->getName(), layerNode->getWidth(), layerNode->getHeight()); + ATRACE_FORMAT("Optimize HW Layer DisplayList %s %ux%u", layerNode->getName(), + layerNode->getWidth(), layerNode->getHeight()); Rect layerDamage = layers.entries()[i].damage; // TODO: ensure layer damage can't be larger than layer @@ -96,8 +91,8 @@ void FrameBuilder::deferLayers(const LayerUpdateQueue& layers) { Vector3 lightCenter = mCanvasState.currentSnapshot()->getRelativeLightCenter(); layer->inverseTransformInWindow.mapPoint3d(lightCenter); - saveForLayer(layerNode->getWidth(), layerNode->getHeight(), 0, 0, - layerDamage, lightCenter, nullptr, layerNode); + saveForLayer(layerNode->getWidth(), layerNode->getHeight(), 0, 0, layerDamage, + lightCenter, nullptr, layerNode); if (layerNode->getDisplayList()) { deferNodeOps(*layerNode); @@ -121,19 +116,18 @@ void FrameBuilder::deferRenderNode(float tx, float ty, Rect clipRect, RenderNode mCanvasState.save(SaveFlags::MatrixClip); mCanvasState.translate(tx, ty); mCanvasState.clipRect(clipRect.left, clipRect.top, clipRect.right, clipRect.bottom, - SkClipOp::kIntersect); + SkClipOp::kIntersect); deferNodePropsAndOps(renderNode); mCanvasState.restore(); } static Rect nodeBounds(RenderNode& node) { auto& props = node.properties(); - return Rect(props.getLeft(), props.getTop(), - props.getRight(), props.getBottom()); + return Rect(props.getLeft(), props.getTop(), props.getRight(), props.getBottom()); } -void FrameBuilder::deferRenderNodeScene(const std::vector< sp<RenderNode> >& nodes, - const Rect& contentDrawBounds) { +void FrameBuilder::deferRenderNodeScene(const std::vector<sp<RenderNode> >& nodes, + const Rect& contentDrawBounds) { if (nodes.size() < 1) return; if (nodes.size() == 1) { if (!nodes[0]->nothingToDraw()) { @@ -170,14 +164,16 @@ void FrameBuilder::deferRenderNodeScene(const std::vector< sp<RenderNode> >& nod // the backdrop, so this isn't necessary. if (content.right < backdrop.right) { // draw backdrop to right side of content - deferRenderNode(0, 0, Rect(content.right, backdrop.top, - backdrop.right, backdrop.bottom), *nodes[0]); + deferRenderNode(0, 0, + Rect(content.right, backdrop.top, backdrop.right, backdrop.bottom), + *nodes[0]); } if (content.bottom < backdrop.bottom) { // draw backdrop to bottom of content // Note: bottom fill uses content left/right, to avoid overdrawing left/right fill - deferRenderNode(0, 0, Rect(content.left, content.bottom, - content.right, backdrop.bottom), *nodes[0]); + deferRenderNode(0, 0, + Rect(content.left, content.bottom, content.right, backdrop.bottom), + *nodes[0]); } } @@ -210,11 +206,9 @@ void FrameBuilder::onSnapshotRestored(const Snapshot& removed, const Snapshot& r void FrameBuilder::deferNodePropsAndOps(RenderNode& node) { const RenderProperties& properties = node.properties(); const Outline& outline = properties.getOutline(); - if (properties.getAlpha() <= 0 - || (outline.getShouldClip() && outline.isEmpty()) - || properties.getScaleX() == 0 - || properties.getScaleY() == 0) { - return; // rejected + if (properties.getAlpha() <= 0 || (outline.getShouldClip() && outline.isEmpty()) || + properties.getScaleX() == 0 || properties.getScaleY() == 0) { + return; // rejected } if (properties.getLeft() != 0 || properties.getTop() != 0) { @@ -236,12 +230,12 @@ void FrameBuilder::deferNodePropsAndOps(RenderNode& node) { const int width = properties.getWidth(); const int height = properties.getHeight(); - Rect saveLayerBounds; // will be set to non-empty if saveLayer needed + Rect saveLayerBounds; // will be set to non-empty if saveLayer needed const bool isLayer = properties.effectiveLayerType() != LayerType::None; int clipFlags = properties.getClippingFlags(); if (properties.getAlpha() < 1) { if (isLayer) { - clipFlags &= ~CLIP_TO_BOUNDS; // bounds clipping done by layer + clipFlags &= ~CLIP_TO_BOUNDS; // bounds clipping done by layer } if (CC_LIKELY(isLayer || !properties.getHasOverlappingRendering())) { // simply scale rendering content's alpha @@ -251,7 +245,7 @@ void FrameBuilder::deferNodePropsAndOps(RenderNode& node) { saveLayerBounds.set(0, 0, width, height); if (clipFlags) { properties.getClippingRectForFlags(clipFlags, &saveLayerBounds); - clipFlags = 0; // all clipping done by savelayer + clipFlags = 0; // all clipping done by savelayer } } @@ -265,21 +259,21 @@ void FrameBuilder::deferNodePropsAndOps(RenderNode& node) { Rect clipRect; properties.getClippingRectForFlags(clipFlags, &clipRect); mCanvasState.clipRect(clipRect.left, clipRect.top, clipRect.right, clipRect.bottom, - SkClipOp::kIntersect); + SkClipOp::kIntersect); } if (properties.getRevealClip().willClip()) { Rect bounds; properties.getRevealClip().getBounds(&bounds); - mCanvasState.setClippingRoundRect(mAllocator, - bounds, properties.getRevealClip().getRadius()); + mCanvasState.setClippingRoundRect(mAllocator, bounds, + properties.getRevealClip().getRadius()); } else if (properties.getOutline().willClip()) { mCanvasState.setClippingOutline(mAllocator, &(properties.getOutline())); } - bool quickRejected = mCanvasState.currentSnapshot()->getRenderTargetClip().isEmpty() - || (properties.getClipToBounds() - && mCanvasState.quickRejectConservative(0, 0, width, height)); + bool quickRejected = mCanvasState.currentSnapshot()->getRenderTargetClip().isEmpty() || + (properties.getClipToBounds() && + mCanvasState.quickRejectConservative(0, 0, width, height)); if (!quickRejected) { // not rejected, so defer render as either Layer, or direct (possibly wrapped in saveLayer) if (node.getLayer()) { @@ -296,9 +290,8 @@ void FrameBuilder::deferNodePropsAndOps(RenderNode& node) { SkPaint saveLayerPaint; saveLayerPaint.setAlpha(properties.getAlpha()); deferBeginLayerOp(*mAllocator.create_trivial<BeginLayerOp>( - saveLayerBounds, - Matrix4::identity(), - nullptr, // no record-time clip - need only respect defer-time one + saveLayerBounds, Matrix4::identity(), + nullptr, // no record-time clip - need only respect defer-time one &saveLayerPaint)); deferNodeOps(node); deferEndLayerOp(*mAllocator.create_trivial<EndLayerOp>()); @@ -311,8 +304,8 @@ void FrameBuilder::deferNodePropsAndOps(RenderNode& node) { typedef key_value_pair_t<float, const RenderNodeOp*> ZRenderNodeOpPair; template <typename V> -static void buildZSortedChildList(V* zTranslatedNodes, - const DisplayList& displayList, const DisplayList::Chunk& chunk) { +static void buildZSortedChildList(V* zTranslatedNodes, const DisplayList& displayList, + const DisplayList::Chunk& chunk) { if (chunk.beginChildIndex == chunk.endChildIndex) return; for (size_t i = chunk.beginChildIndex; i < chunk.endChildIndex; i++) { @@ -343,11 +336,10 @@ static size_t findNonNegativeIndex(const V& zTranslatedNodes) { template <typename V> void FrameBuilder::defer3dChildren(const ClipBase* reorderClip, ChildrenSelectMode mode, - const V& zTranslatedNodes) { + const V& zTranslatedNodes) { const int size = zTranslatedNodes.size(); - if (size == 0 - || (mode == ChildrenSelectMode::Negative&& zTranslatedNodes[0].key > 0.0f) - || (mode == ChildrenSelectMode::Positive && zTranslatedNodes[size - 1].key < 0.0f)) { + if (size == 0 || (mode == ChildrenSelectMode::Negative && zTranslatedNodes[0].key > 0.0f) || + (mode == ChildrenSelectMode::Positive && zTranslatedNodes[size - 1].key < 0.0f)) { // no 3d children to draw return; } @@ -364,11 +356,11 @@ void FrameBuilder::defer3dChildren(const ClipBase* reorderClip, ChildrenSelectMo if (mode == ChildrenSelectMode::Negative) { drawIndex = 0; endIndex = nonNegativeIndex; - shadowIndex = endIndex; // draw no shadows + shadowIndex = endIndex; // draw no shadows } else { drawIndex = nonNegativeIndex; endIndex = size; - shadowIndex = drawIndex; // potentially draw shadow for each pos Z child + shadowIndex = drawIndex; // potentially draw shadow for each pos Z child } float lastCasterZ = 0.0f; @@ -381,7 +373,7 @@ void FrameBuilder::defer3dChildren(const ClipBase* reorderClip, ChildrenSelectMo if (shadowIndex == drawIndex || casterZ - lastCasterZ < 0.1f) { deferShadow(reorderClip, *casterNodeOp); - lastCasterZ = casterZ; // must do this even if current caster not casting a shadow + lastCasterZ = casterZ; // must do this even if current caster not casting a shadow shadowIndex++; continue; } @@ -397,11 +389,9 @@ void FrameBuilder::deferShadow(const ClipBase* reorderClip, const RenderNodeOp& auto& node = *casterNodeOp.renderNode; auto& properties = node.properties(); - if (properties.getAlpha() <= 0.0f - || properties.getOutline().getAlpha() <= 0.0f - || !properties.getOutline().getPath() - || properties.getScaleX() == 0 - || properties.getScaleY() == 0) { + if (properties.getAlpha() <= 0.0f || properties.getOutline().getAlpha() <= 0.0f || + !properties.getOutline().getPath() || properties.getScaleX() == 0 || + properties.getScaleY() == 0) { // no shadow to draw return; } @@ -432,8 +422,8 @@ void FrameBuilder::deferShadow(const ClipBase* reorderClip, const RenderNodeOp& Rect clipBounds; properties.getClippingRectForFlags(CLIP_TO_CLIP_BOUNDS, &clipBounds); SkPath clipBoundsPath; - clipBoundsPath.addRect(clipBounds.left, clipBounds.top, - clipBounds.right, clipBounds.bottom); + clipBoundsPath.addRect(clipBounds.left, clipBounds.top, clipBounds.right, + clipBounds.bottom); Op(*casterPath, clipBoundsPath, kIntersect_SkPathOp, frameAllocatedPath); casterPath = frameAllocatedPath; @@ -442,7 +432,7 @@ void FrameBuilder::deferShadow(const ClipBase* reorderClip, const RenderNodeOp& // apply reorder clip to shadow, so it respects clip at beginning of reorderable chunk int restoreTo = mCanvasState.save(SaveFlags::MatrixClip); mCanvasState.writableSnapshot()->applyClip(reorderClip, - *mCanvasState.currentSnapshot()->transform); + *mCanvasState.currentSnapshot()->transform); if (CC_LIKELY(!mCanvasState.getRenderTargetClipBounds().isEmpty())) { Matrix4 shadowMatrixXY(casterNodeOp.localMatrix); Matrix4 shadowMatrixZ(casterNodeOp.localMatrix); @@ -450,13 +440,9 @@ void FrameBuilder::deferShadow(const ClipBase* reorderClip, const RenderNodeOp& node.applyViewPropertyTransforms(shadowMatrixZ, true); sp<TessellationCache::ShadowTask> task = mCaches.tessellationCache.getShadowTask( - mCanvasState.currentTransform(), - mCanvasState.getLocalClipBounds(), - casterAlpha >= 1.0f, - casterPath, - &shadowMatrixXY, &shadowMatrixZ, - mCanvasState.currentSnapshot()->getRelativeLightCenter(), - mLightRadius); + mCanvasState.currentTransform(), mCanvasState.getLocalClipBounds(), + casterAlpha >= 1.0f, casterPath, &shadowMatrixXY, &shadowMatrixZ, + mCanvasState.currentSnapshot()->getRelativeLightCenter(), mLightRadius); ShadowOp* shadowOp = mAllocator.create<ShadowOp>(task, casterAlpha); BakedOpState* bakedOpState = BakedOpState::tryShadowOpConstruct( mAllocator, *mCanvasState.writableSnapshot(), shadowOp); @@ -471,15 +457,13 @@ void FrameBuilder::deferProjectedChildren(const RenderNode& renderNode) { int count = mCanvasState.save(SaveFlags::MatrixClip); const SkPath* projectionReceiverOutline = renderNode.properties().getOutline().getPath(); - SkPath transformedMaskPath; // on stack, since BakedOpState makes a deep copy + SkPath transformedMaskPath; // on stack, since BakedOpState makes a deep copy if (projectionReceiverOutline) { // transform the mask for this projector into render target space // TODO: consider combining both transforms by stashing transform instead of applying SkMatrix skCurrentTransform; mCanvasState.currentTransform()->copyTo(skCurrentTransform); - projectionReceiverOutline->transform( - skCurrentTransform, - &transformedMaskPath); + projectionReceiverOutline->transform(skCurrentTransform, &transformedMaskPath); mCanvasState.setProjectionPathMask(&transformedMaskPath); } @@ -509,10 +493,12 @@ void FrameBuilder::deferProjectedChildren(const RenderNode& renderNode) { * This allows opIds embedded in the RecordedOps to be used for dispatching to these lambdas. * E.g. a BitmapOp op then would be dispatched to FrameBuilder::onBitmapOp(const BitmapOp&) */ -#define OP_RECEIVER(Type) \ - [](FrameBuilder& frameBuilder, const RecordedOp& op) { frameBuilder.defer##Type(static_cast<const Type&>(op)); }, +#define OP_RECEIVER(Type) \ + [](FrameBuilder& frameBuilder, const RecordedOp& op) { \ + frameBuilder.defer##Type(static_cast<const Type&>(op)); \ + }, void FrameBuilder::deferNodeOps(const RenderNode& renderNode) { - typedef void (*OpDispatcher) (FrameBuilder& frameBuilder, const RecordedOp& op); + typedef void (*OpDispatcher)(FrameBuilder & frameBuilder, const RecordedOp& op); static OpDispatcher receivers[] = BUILD_DEFERRABLE_OP_LUT(OP_RECEIVER); // can't be null, since DL=null node rejection happens before deferNodePropsAndOps @@ -526,9 +512,9 @@ void FrameBuilder::deferNodeOps(const RenderNode& renderNode) { const RecordedOp* op = displayList.getOps()[opIndex]; receivers[op->opId](*this, *op); - if (CC_UNLIKELY(!renderNode.mProjectedNodes.empty() - && displayList.projectionReceiveIndex >= 0 - && static_cast<int>(opIndex) == displayList.projectionReceiveIndex)) { + if (CC_UNLIKELY(!renderNode.mProjectedNodes.empty() && + displayList.projectionReceiveIndex >= 0 && + static_cast<int>(opIndex) == displayList.projectionReceiveIndex)) { deferProjectedChildren(renderNode); } } @@ -542,7 +528,7 @@ void FrameBuilder::deferRenderNodeOpImpl(const RenderNodeOp& op) { // apply state from RecordedOp (clip first, since op's clip is transformed by current matrix) mCanvasState.writableSnapshot()->applyClip(op.localClip, - *mCanvasState.currentSnapshot()->transform); + *mCanvasState.currentSnapshot()->transform); mCanvasState.concatMatrix(op.localMatrix); // then apply state from node properties, and defer ops @@ -562,12 +548,12 @@ void FrameBuilder::deferRenderNodeOp(const RenderNodeOp& op) { * for paint's style on the bounds being computed. */ BakedOpState* FrameBuilder::deferStrokeableOp(const RecordedOp& op, batchid_t batchId, - BakedOpState::StrokeBehavior strokeBehavior, bool expandForPathTexture) { + BakedOpState::StrokeBehavior strokeBehavior, + bool expandForPathTexture) { // Note: here we account for stroke when baking the op BakedOpState* bakedState = BakedOpState::tryStrokeableOpConstruct( - mAllocator, *mCanvasState.writableSnapshot(), op, - strokeBehavior, expandForPathTexture); - if (!bakedState) return nullptr; // quick rejected + mAllocator, *mCanvasState.writableSnapshot(), op, strokeBehavior, expandForPathTexture); + if (!bakedState) return nullptr; // quick rejected if (op.opId == RecordedOpId::RectOp && op.paint->getStyle() != SkPaint::kStroke_Style) { bakedState->setupOpacity(op.paint); @@ -586,8 +572,8 @@ BakedOpState* FrameBuilder::deferStrokeableOp(const RecordedOp& op, batchid_t ba static batchid_t tessBatchId(const RecordedOp& op) { const SkPaint& paint = *(op.paint); return paint.getPathEffect() - ? OpBatchType::AlphaMaskTexture - : (paint.isAntiAlias() ? OpBatchType::AlphaVertices : OpBatchType::Vertices); + ? OpBatchType::AlphaMaskTexture + : (paint.isAntiAlias() ? OpBatchType::AlphaVertices : OpBatchType::Vertices); } void FrameBuilder::deferArcOp(const ArcOp& op) { @@ -598,13 +584,13 @@ void FrameBuilder::deferArcOp(const ArcOp& op) { } static bool hasMergeableClip(const BakedOpState& state) { - return !state.computedState.clipState - || state.computedState.clipState->mode == ClipMode::Rectangle; + return !state.computedState.clipState || + state.computedState.clipState->mode == ClipMode::Rectangle; } void FrameBuilder::deferBitmapOp(const BitmapOp& op) { BakedOpState* bakedState = tryBakeOpState(op); - if (!bakedState) return; // quick rejected + if (!bakedState) return; // quick rejected if (op.bitmap->isOpaque()) { bakedState->setupOpacity(op.paint); @@ -613,11 +599,10 @@ void FrameBuilder::deferBitmapOp(const BitmapOp& op) { // Don't merge non-simply transformed or neg scale ops, SET_TEXTURE doesn't handle rotation // Don't merge A8 bitmaps - the paint's color isn't compared by mergeId, or in // MergingDrawBatch::canMergeWith() - if (bakedState->computedState.transform.isSimple() - && bakedState->computedState.transform.positiveScale() - && PaintUtils::getBlendModeDirect(op.paint) == SkBlendMode::kSrcOver - && op.bitmap->colorType() != kAlpha_8_SkColorType - && hasMergeableClip(*bakedState)) { + if (bakedState->computedState.transform.isSimple() && + bakedState->computedState.transform.positiveScale() && + PaintUtils::getBlendModeDirect(op.paint) == SkBlendMode::kSrcOver && + op.bitmap->colorType() != kAlpha_8_SkColorType && hasMergeableClip(*bakedState)) { mergeid_t mergeId = reinterpret_cast<mergeid_t>(op.bitmap->getGenerationID()); currentLayer().deferMergeableOp(mAllocator, bakedState, OpBatchType::Bitmap, mergeId); } else { @@ -627,24 +612,21 @@ void FrameBuilder::deferBitmapOp(const BitmapOp& op) { void FrameBuilder::deferBitmapMeshOp(const BitmapMeshOp& op) { BakedOpState* bakedState = tryBakeOpState(op); - if (!bakedState) return; // quick rejected + if (!bakedState) return; // quick rejected currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Bitmap); } void FrameBuilder::deferBitmapRectOp(const BitmapRectOp& op) { BakedOpState* bakedState = tryBakeOpState(op); - if (!bakedState) return; // quick rejected + if (!bakedState) return; // quick rejected currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Bitmap); } void FrameBuilder::deferVectorDrawableOp(const VectorDrawableOp& op) { Bitmap& bitmap = op.vectorDrawable->getBitmapUpdateIfDirty(); SkPaint* paint = op.vectorDrawable->getPaint(); - const BitmapRectOp* resolvedOp = mAllocator.create_trivial<BitmapRectOp>(op.unmappedBounds, - op.localMatrix, - op.localClip, - paint, - &bitmap, + const BitmapRectOp* resolvedOp = mAllocator.create_trivial<BitmapRectOp>( + op.unmappedBounds, op.localMatrix, op.localClip, paint, &bitmap, Rect(bitmap.width(), bitmap.height())); deferBitmapRectOp(*resolvedOp); } @@ -656,23 +638,20 @@ void FrameBuilder::deferCirclePropsOp(const CirclePropsOp& op) { float y = *(op.y); float radius = *(op.radius); Rect unmappedBounds(x - radius, y - radius, x + radius, y + radius); - const OvalOp* resolvedOp = mAllocator.create_trivial<OvalOp>( - unmappedBounds, - op.localMatrix, - op.localClip, - op.paint); + const OvalOp* resolvedOp = mAllocator.create_trivial<OvalOp>(unmappedBounds, op.localMatrix, + op.localClip, op.paint); deferOvalOp(*resolvedOp); } void FrameBuilder::deferColorOp(const ColorOp& op) { BakedOpState* bakedState = tryBakeUnboundedOpState(op); - if (!bakedState) return; // quick rejected + if (!bakedState) return; // quick rejected currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Vertices); } void FrameBuilder::deferFunctorOp(const FunctorOp& op) { BakedOpState* bakedState = tryBakeUnboundedOpState(op); - if (!bakedState) return; // quick rejected + if (!bakedState) return; // quick rejected currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Functor); } @@ -687,11 +666,11 @@ void FrameBuilder::deferOvalOp(const OvalOp& op) { void FrameBuilder::deferPatchOp(const PatchOp& op) { BakedOpState* bakedState = tryBakeOpState(op); - if (!bakedState) return; // quick rejected + if (!bakedState) return; // quick rejected - if (bakedState->computedState.transform.isPureTranslate() - && PaintUtils::getBlendModeDirect(op.paint) == SkBlendMode::kSrcOver - && hasMergeableClip(*bakedState)) { + if (bakedState->computedState.transform.isPureTranslate() && + PaintUtils::getBlendModeDirect(op.paint) == SkBlendMode::kSrcOver && + hasMergeableClip(*bakedState)) { mergeid_t mergeId = reinterpret_cast<mergeid_t>(op.bitmap->getGenerationID()); // Only use the MergedPatch batchId when merged, so Bitmap+Patch don't try to merge together @@ -723,7 +702,8 @@ void FrameBuilder::deferRoundRectOp(const RoundRectOp& op) { if (CC_LIKELY(state && !op.paint->getPathEffect())) { // TODO: consider storing tessellation task in BakedOpState mCaches.tessellationCache.precacheRoundRect(state->computedState.transform, *(op.paint), - op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.rx, op.ry); + op.unmappedBounds.getWidth(), + op.unmappedBounds.getHeight(), op.rx, op.ry); } } @@ -731,16 +711,14 @@ void FrameBuilder::deferRoundRectPropsOp(const RoundRectPropsOp& op) { // allocate a temporary round rect op (with mAllocator, so it persists until render), so the // renderer doesn't have to handle the RoundRectPropsOp type, and so state baking is simple. const RoundRectOp* resolvedOp = mAllocator.create_trivial<RoundRectOp>( - Rect(*(op.left), *(op.top), *(op.right), *(op.bottom)), - op.localMatrix, - op.localClip, + Rect(*(op.left), *(op.top), *(op.right), *(op.bottom)), op.localMatrix, op.localClip, op.paint, *op.rx, *op.ry); deferRoundRectOp(*resolvedOp); } void FrameBuilder::deferSimpleRectsOp(const SimpleRectsOp& op) { BakedOpState* bakedState = tryBakeOpState(op); - if (!bakedState) return; // quick rejected + if (!bakedState) return; // quick rejected currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Vertices); } @@ -753,12 +731,12 @@ void FrameBuilder::deferTextOp(const TextOp& op) { BakedOpState* bakedState = BakedOpState::tryStrokeableOpConstruct( mAllocator, *mCanvasState.writableSnapshot(), op, BakedOpState::StrokeBehavior::StyleDefined, false); - if (!bakedState) return; // quick rejected + if (!bakedState) return; // quick rejected batchid_t batchId = textBatchId(*(op.paint)); - if (bakedState->computedState.transform.isPureTranslate() - && PaintUtils::getBlendModeDirect(op.paint) == SkBlendMode::kSrcOver - && hasMergeableClip(*bakedState)) { + if (bakedState->computedState.transform.isPureTranslate() && + PaintUtils::getBlendModeDirect(op.paint) == SkBlendMode::kSrcOver && + hasMergeableClip(*bakedState)) { mergeid_t mergeId = reinterpret_cast<mergeid_t>(op.paint->getColor()); currentLayer().deferMergeableOp(mAllocator, bakedState, batchId, mergeId); } else { @@ -773,19 +751,19 @@ void FrameBuilder::deferTextOp(const TextOp& op) { // Partial transform case, see BakedOpDispatcher::renderTextOp float sx, sy; totalTransform.decomposeScale(sx, sy); - fontRenderer.precache(op.paint, op.glyphs, op.glyphCount, SkMatrix::MakeScale( - roundf(std::max(1.0f, sx)), - roundf(std::max(1.0f, sy)))); + fontRenderer.precache( + op.paint, op.glyphs, op.glyphCount, + SkMatrix::MakeScale(roundf(std::max(1.0f, sx)), roundf(std::max(1.0f, sy)))); } } void FrameBuilder::deferTextOnPathOp(const TextOnPathOp& op) { BakedOpState* bakedState = tryBakeUnboundedOpState(op); - if (!bakedState) return; // quick rejected + if (!bakedState) return; // quick rejected currentLayer().deferUnmergeableOp(mAllocator, bakedState, textBatchId(*(op.paint))); - mCaches.fontRenderer.getFontRenderer().precache( - op.paint, op.glyphs, op.glyphCount, SkMatrix::I()); + mCaches.fontRenderer.getFontRenderer().precache(op.paint, op.glyphs, op.glyphCount, + SkMatrix::I()); } void FrameBuilder::deferTextureLayerOp(const TextureLayerOp& op) { @@ -802,28 +780,27 @@ void FrameBuilder::deferTextureLayerOp(const TextureLayerOp& op) { } BakedOpState* bakedState = tryBakeOpState(*textureLayerOp); - if (!bakedState) return; // quick rejected + if (!bakedState) return; // quick rejected currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::TextureLayer); } -void FrameBuilder::saveForLayer(uint32_t layerWidth, uint32_t layerHeight, - float contentTranslateX, float contentTranslateY, - const Rect& repaintRect, - const Vector3& lightCenter, - const BeginLayerOp* beginLayerOp, RenderNode* renderNode) { +void FrameBuilder::saveForLayer(uint32_t layerWidth, uint32_t layerHeight, float contentTranslateX, + float contentTranslateY, const Rect& repaintRect, + const Vector3& lightCenter, const BeginLayerOp* beginLayerOp, + RenderNode* renderNode) { mCanvasState.save(SaveFlags::MatrixClip); mCanvasState.writableSnapshot()->initializeViewport(layerWidth, layerHeight); mCanvasState.writableSnapshot()->roundRectClipState = nullptr; mCanvasState.writableSnapshot()->setRelativeLightCenter(lightCenter); - mCanvasState.writableSnapshot()->transform->loadTranslate( - contentTranslateX, contentTranslateY, 0); - mCanvasState.writableSnapshot()->setClip( - repaintRect.left, repaintRect.top, repaintRect.right, repaintRect.bottom); + mCanvasState.writableSnapshot()->transform->loadTranslate(contentTranslateX, contentTranslateY, + 0); + mCanvasState.writableSnapshot()->setClip(repaintRect.left, repaintRect.top, repaintRect.right, + repaintRect.bottom); // create a new layer repaint, and push its index on the stack mLayerStack.push_back(mLayerBuilders.size()); - auto newFbo = mAllocator.create<LayerBuilder>(layerWidth, layerHeight, - repaintRect, beginLayerOp, renderNode); + auto newFbo = mAllocator.create<LayerBuilder>(layerWidth, layerHeight, repaintRect, + beginLayerOp, renderNode); mLayerBuilders.push_back(newFbo); } @@ -836,8 +813,8 @@ void FrameBuilder::restoreForLayer() { // TODO: defer time rejection (when bounds become empty) + tests // Option - just skip layers with no bounds at playback + defer? void FrameBuilder::deferBeginLayerOp(const BeginLayerOp& op) { - uint32_t layerWidth = (uint32_t) op.unmappedBounds.getWidth(); - uint32_t layerHeight = (uint32_t) op.unmappedBounds.getHeight(); + uint32_t layerWidth = (uint32_t)op.unmappedBounds.getWidth(); + uint32_t layerHeight = (uint32_t)op.unmappedBounds.getHeight(); auto previous = mCanvasState.currentSnapshot(); Vector3 lightCenter = previous->getRelativeLightCenter(); @@ -873,11 +850,8 @@ void FrameBuilder::deferBeginLayerOp(const BeginLayerOp& op) { float contentTranslateX = -saveLayerBounds.left; float contentTranslateY = -saveLayerBounds.top; - saveForLayer(layerWidth, layerHeight, - contentTranslateX, contentTranslateY, - Rect(layerWidth, layerHeight), - lightCenter, - &op, nullptr); + saveForLayer(layerWidth, layerHeight, contentTranslateX, contentTranslateY, + Rect(layerWidth, layerHeight), lightCenter, &op, nullptr); } void FrameBuilder::deferEndLayerOp(const EndLayerOp& /* ignored */) { @@ -890,8 +864,8 @@ void FrameBuilder::deferEndLayerOp(const EndLayerOp& /* ignored */) { // to translate the drawLayer by how much the contents was translated // TODO: Unify this with beginLayerOp so we don't have to calculate this // twice - uint32_t layerWidth = (uint32_t) beginLayerOp.unmappedBounds.getWidth(); - uint32_t layerHeight = (uint32_t) beginLayerOp.unmappedBounds.getHeight(); + uint32_t layerWidth = (uint32_t)beginLayerOp.unmappedBounds.getWidth(); + uint32_t layerHeight = (uint32_t)beginLayerOp.unmappedBounds.getHeight(); auto previous = mCanvasState.currentSnapshot(); Vector3 lightCenter = previous->getRelativeLightCenter(); @@ -900,8 +874,7 @@ void FrameBuilder::deferEndLayerOp(const EndLayerOp& /* ignored */) { // parent content transform * canvas transform * bounds offset Matrix4 contentTransform(*(previous->transform)); contentTransform.multiply(beginLayerOp.localMatrix); - contentTransform.translate(beginLayerOp.unmappedBounds.left, - beginLayerOp.unmappedBounds.top); + contentTransform.translate(beginLayerOp.unmappedBounds.left, beginLayerOp.unmappedBounds.top); Matrix4 inverseContentTransform; inverseContentTransform.loadInverse(contentTransform); @@ -927,10 +900,7 @@ void FrameBuilder::deferEndLayerOp(const EndLayerOp& /* ignored */) { // record the draw operation into the previous layer's list of draw commands // uses state from the associated beginLayerOp, since it has all the state needed for drawing LayerOp* drawLayerOp = mAllocator.create_trivial<LayerOp>( - beginLayerOp.unmappedBounds, - localMatrix, - beginLayerOp.localClip, - beginLayerOp.paint, + beginLayerOp.unmappedBounds, localMatrix, beginLayerOp.localClip, beginLayerOp.paint, &(mLayerBuilders[finishedLayerIndex]->offscreenBuffer)); BakedOpState* bakedOpState = tryBakeOpState(*drawLayerOp); @@ -959,15 +929,16 @@ void FrameBuilder::deferBeginUnclippedLayerOp(const BeginUnclippedLayerOp& op) { // Unclipped layer rejected - push a null op, so next EndUnclippedLayerOp is ignored currentLayer().activeUnclippedSaveLayers.push_back(nullptr); } else { - // Allocate a holding position for the layer object (copyTo will produce, copyFrom will consume) + // Allocate a holding position for the layer object (copyTo will produce, copyFrom will + // consume) OffscreenBuffer** layerHandle = mAllocator.create<OffscreenBuffer*>(nullptr); /** * First, defer an operation to copy out the content from the rendertarget into a layer. */ auto copyToOp = mAllocator.create_trivial<CopyToLayerOp>(op, layerHandle); - BakedOpState* bakedState = BakedOpState::directConstruct(mAllocator, - &(currentLayer().repaintClip), dstRect, *copyToOp); + BakedOpState* bakedState = BakedOpState::directConstruct( + mAllocator, &(currentLayer().repaintClip), dstRect, *copyToOp); currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::CopyToLayer); /** @@ -981,8 +952,8 @@ void FrameBuilder::deferBeginUnclippedLayerOp(const BeginUnclippedLayerOp& op) { * a balanced EndUnclippedLayerOp is seen */ auto copyFromOp = mAllocator.create_trivial<CopyFromLayerOp>(op, layerHandle); - bakedState = BakedOpState::directConstruct(mAllocator, - &(currentLayer().repaintClip), dstRect, *copyFromOp); + bakedState = BakedOpState::directConstruct(mAllocator, &(currentLayer().repaintClip), + dstRect, *copyFromOp); currentLayer().activeUnclippedSaveLayers.push_back(bakedState); } } @@ -1001,5 +972,5 @@ void FrameBuilder::finishDefer() { mCaches.fontRenderer.endPrecaching(); } -} // namespace uirenderer -} // namespace android +} // namespace uirenderer +} // namespace android diff --git a/libs/hwui/FrameBuilder.h b/libs/hwui/FrameBuilder.h index 46048f7125ce..974daf8a17bb 100644 --- a/libs/hwui/FrameBuilder.h +++ b/libs/hwui/FrameBuilder.h @@ -23,8 +23,8 @@ #include "RecordedOp.h" #include "utils/GLUtils.h" -#include <vector> #include <unordered_map> +#include <vector> struct SkRect; @@ -60,12 +60,11 @@ public: float radius; }; - FrameBuilder(const SkRect& clip, - uint32_t viewportWidth, uint32_t viewportHeight, - const LightGeometry& lightGeometry, Caches& caches); + FrameBuilder(const SkRect& clip, uint32_t viewportWidth, uint32_t viewportHeight, + const LightGeometry& lightGeometry, Caches& caches); - FrameBuilder(const LayerUpdateQueue& layerUpdateQueue, - const LightGeometry& lightGeometry, Caches& caches); + FrameBuilder(const LayerUpdateQueue& layerUpdateQueue, const LightGeometry& lightGeometry, + Caches& caches); void deferLayers(const LayerUpdateQueue& layers); @@ -73,8 +72,8 @@ public: void deferRenderNode(float tx, float ty, Rect clipRect, RenderNode& renderNode); - void deferRenderNodeScene(const std::vector< sp<RenderNode> >& nodes, - const Rect& contentDrawBounds); + void deferRenderNodeScene(const std::vector<sp<RenderNode> >& nodes, + const Rect& contentDrawBounds); virtual ~FrameBuilder() {} @@ -88,32 +87,32 @@ public: void replayBakedOps(Renderer& renderer) { std::vector<OffscreenBuffer*> temporaryLayers; finishDefer(); - /** - * Defines a LUT of lambdas which allow a recorded BakedOpState to use state->op->opId to - * dispatch the op via a method on a static dispatcher when the op is replayed. - * - * For example a BitmapOp would resolve, via the lambda lookup, to calling: - * - * StaticDispatcher::onBitmapOp(Renderer& renderer, const BitmapOp& op, const BakedOpState& state); - */ - #define X(Type) \ - [](void* renderer, const BakedOpState& state) { \ - StaticDispatcher::on##Type(*(static_cast<Renderer*>(renderer)), \ - static_cast<const Type&>(*(state.op)), state); \ - }, +/** + * Defines a LUT of lambdas which allow a recorded BakedOpState to use state->op->opId to + * dispatch the op via a method on a static dispatcher when the op is replayed. + * + * For example a BitmapOp would resolve, via the lambda lookup, to calling: + * + * StaticDispatcher::onBitmapOp(Renderer& renderer, const BitmapOp& op, const BakedOpState& state); + */ +#define X(Type) \ + [](void* renderer, const BakedOpState& state) { \ + StaticDispatcher::on##Type(*(static_cast<Renderer*>(renderer)), \ + static_cast<const Type&>(*(state.op)), state); \ + }, static BakedOpReceiver unmergedReceivers[] = BUILD_RENDERABLE_OP_LUT(X); - #undef X - - /** - * Defines a LUT of lambdas which allow merged arrays of BakedOpState* to be passed to a - * static dispatcher when the group of merged ops is replayed. - */ - #define X(Type) \ - [](void* renderer, const MergedBakedOpList& opList) { \ - StaticDispatcher::onMerged##Type##s(*(static_cast<Renderer*>(renderer)), opList); \ - }, +#undef X + +/** + * Defines a LUT of lambdas which allow merged arrays of BakedOpState* to be passed to a + * static dispatcher when the group of merged ops is replayed. + */ +#define X(Type) \ + [](void* renderer, const MergedBakedOpList& opList) { \ + StaticDispatcher::onMerged##Type##s(*(static_cast<Renderer*>(renderer)), opList); \ + }, static MergedOpReceiver mergedReceivers[] = BUILD_MERGEABLE_OP_LUT(X); - #undef X +#undef X // Relay through layers in reverse order, since layers // later in the list will be drawn by earlier ones @@ -168,15 +167,10 @@ public: private: void finishDefer(); - enum class ChildrenSelectMode { - Negative, - Positive - }; - void saveForLayer(uint32_t layerWidth, uint32_t layerHeight, - float contentTranslateX, float contentTranslateY, - const Rect& repaintRect, - const Vector3& lightCenter, - const BeginLayerOp* beginLayerOp, RenderNode* renderNode); + enum class ChildrenSelectMode { Negative, Positive }; + void saveForLayer(uint32_t layerWidth, uint32_t layerHeight, float contentTranslateX, + float contentTranslateY, const Rect& repaintRect, const Vector3& lightCenter, + const BeginLayerOp* beginLayerOp, RenderNode* renderNode); void restoreForLayer(); LayerBuilder& currentLayer() { return *(mLayerBuilders[mLayerStack.back()]); } @@ -185,16 +179,16 @@ private: return BakedOpState::tryConstruct(mAllocator, *mCanvasState.writableSnapshot(), recordedOp); } BakedOpState* tryBakeUnboundedOpState(const RecordedOp& recordedOp) { - return BakedOpState::tryConstructUnbounded(mAllocator, *mCanvasState.writableSnapshot(), recordedOp); + return BakedOpState::tryConstructUnbounded(mAllocator, *mCanvasState.writableSnapshot(), + recordedOp); } - // should always be surrounded by a save/restore pair, and not called if DisplayList is null void deferNodePropsAndOps(RenderNode& node); template <typename V> void defer3dChildren(const ClipBase* reorderClip, ChildrenSelectMode mode, - const V& zTranslatedNodes); + const V& zTranslatedNodes); void deferShadow(const ClipBase* reorderClip, const RenderNodeOp& casterOp); @@ -206,20 +200,19 @@ private: void replayBakedOpsImpl(void* arg, BakedOpReceiver* receivers); - SkPath* createFrameAllocatedPath() { - return mAllocator.create<SkPath>(); - } + SkPath* createFrameAllocatedPath() { return mAllocator.create<SkPath>(); } BakedOpState* deferStrokeableOp(const RecordedOp& op, batchid_t batchId, - BakedOpState::StrokeBehavior strokeBehavior = BakedOpState::StrokeBehavior::StyleDefined, - bool expandForPathTexture = false); + BakedOpState::StrokeBehavior strokeBehavior = + BakedOpState::StrokeBehavior::StyleDefined, + bool expandForPathTexture = false); - /** - * Declares all FrameBuilder::deferXXXXOp() methods for every RecordedOp type. - * - * These private methods are called from within deferImpl to defer each individual op - * type differently. - */ +/** + * Declares all FrameBuilder::deferXXXXOp() methods for every RecordedOp type. + * + * These private methods are called from within deferImpl to defer each individual op + * type differently. + */ #define X(Type) void defer##Type(const Type& op); MAP_DEFERRABLE_OPS(X) #undef X @@ -254,5 +247,5 @@ private: const bool mDrawFbo0; }; -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/FrameInfo.cpp b/libs/hwui/FrameInfo.cpp index 826f0bba294c..71cc9a81a09f 100644 --- a/libs/hwui/FrameInfo.cpp +++ b/libs/hwui/FrameInfo.cpp @@ -21,30 +21,30 @@ namespace android { namespace uirenderer { const std::string FrameInfoNames[] = { - "Flags", - "IntendedVsync", - "Vsync", - "OldestInputEvent", - "NewestInputEvent", - "HandleInputStart", - "AnimationStart", - "PerformTraversalsStart", - "DrawStart", - "SyncQueued", - "SyncStart", - "IssueDrawCommandsStart", - "SwapBuffers", - "FrameCompleted", - "DequeueBufferDuration", - "QueueBufferDuration", + "Flags", + "IntendedVsync", + "Vsync", + "OldestInputEvent", + "NewestInputEvent", + "HandleInputStart", + "AnimationStart", + "PerformTraversalsStart", + "DrawStart", + "SyncQueued", + "SyncStart", + "IssueDrawCommandsStart", + "SwapBuffers", + "FrameCompleted", + "DequeueBufferDuration", + "QueueBufferDuration", }; -static_assert((sizeof(FrameInfoNames)/sizeof(FrameInfoNames[0])) - == static_cast<int>(FrameInfoIndex::NumIndexes), - "size mismatch: FrameInfoNames doesn't match the enum!"); +static_assert((sizeof(FrameInfoNames) / sizeof(FrameInfoNames[0])) == + static_cast<int>(FrameInfoIndex::NumIndexes), + "size mismatch: FrameInfoNames doesn't match the enum!"); static_assert(static_cast<int>(FrameInfoIndex::NumIndexes) == 16, - "Must update value in FrameMetrics.java#FRAME_STATS_COUNT (and here)"); + "Must update value in FrameMetrics.java#FRAME_STATS_COUNT (and here)"); void FrameInfo::importUiThreadInfo(int64_t* info) { memcpy(mFrameInfo, info, UI_THREAD_FRAME_INFO_SIZE * sizeof(int64_t)); diff --git a/libs/hwui/FrameInfo.h b/libs/hwui/FrameInfo.h index bac9d12d273c..0aab58c38ba0 100644 --- a/libs/hwui/FrameInfo.h +++ b/libs/hwui/FrameInfo.h @@ -59,12 +59,12 @@ enum class FrameInfoIndex { extern const std::string FrameInfoNames[]; namespace FrameInfoFlags { - enum { - WindowLayoutChanged = 1 << 0, - RTAnimation = 1 << 1, - SurfaceCanvas = 1 << 2, - SkippedFrame = 1 << 3, - }; +enum { + WindowLayoutChanged = 1 << 0, + RTAnimation = 1 << 1, + SurfaceCanvas = 1 << 2, + SkippedFrame = 1 << 3, +}; }; class ANDROID_API UiFrameInfoBuilder { @@ -91,9 +91,7 @@ public: } private: - inline int64_t& set(FrameInfoIndex index) { - return mBuffer[static_cast<int>(index)]; - } + inline int64_t& set(FrameInfoIndex index) { return mBuffer[static_cast<int>(index)]; } int64_t* mBuffer; }; @@ -102,33 +100,23 @@ class FrameInfo { public: void importUiThreadInfo(int64_t* info); - void markSyncStart() { - set(FrameInfoIndex::SyncStart) = systemTime(CLOCK_MONOTONIC); - } + void markSyncStart() { set(FrameInfoIndex::SyncStart) = systemTime(CLOCK_MONOTONIC); } void markIssueDrawCommandsStart() { set(FrameInfoIndex::IssueDrawCommandsStart) = systemTime(CLOCK_MONOTONIC); } - void markSwapBuffers() { - set(FrameInfoIndex::SwapBuffers) = systemTime(CLOCK_MONOTONIC); - } + void markSwapBuffers() { set(FrameInfoIndex::SwapBuffers) = systemTime(CLOCK_MONOTONIC); } - void markFrameCompleted() { - set(FrameInfoIndex::FrameCompleted) = systemTime(CLOCK_MONOTONIC); - } + void markFrameCompleted() { set(FrameInfoIndex::FrameCompleted) = systemTime(CLOCK_MONOTONIC); } void addFlag(int frameInfoFlag) { set(FrameInfoIndex::Flags) |= static_cast<uint64_t>(frameInfoFlag); } - const int64_t* data() const { - return mFrameInfo; - } + const int64_t* data() const { return mFrameInfo; } - inline int64_t operator[](FrameInfoIndex index) const { - return get(index); - } + inline int64_t operator[](FrameInfoIndex index) const { return get(index); } inline int64_t operator[](int index) const { if (index < 0 || index >= static_cast<int>(FrameInfoIndex::NumIndexes)) return 0; @@ -140,12 +128,10 @@ public: int64_t starttime = get(start); int64_t gap = endtime - starttime; gap = starttime > 0 ? gap : 0; - if (end > FrameInfoIndex::SyncQueued && - start < FrameInfoIndex::SyncQueued) { + if (end > FrameInfoIndex::SyncQueued && start < FrameInfoIndex::SyncQueued) { // Need to subtract out the time spent in a stalled state // as this will be captured by the previous frame's info - int64_t offset = get(FrameInfoIndex::SyncStart) - - get(FrameInfoIndex::SyncQueued); + int64_t offset = get(FrameInfoIndex::SyncStart) - get(FrameInfoIndex::SyncQueued); if (offset > 0) { gap -= offset; } @@ -157,9 +143,7 @@ public: return duration(FrameInfoIndex::IntendedVsync, FrameInfoIndex::FrameCompleted); } - inline int64_t& set(FrameInfoIndex index) { - return mFrameInfo[static_cast<int>(index)]; - } + inline int64_t& set(FrameInfoIndex index) { return mFrameInfo[static_cast<int>(index)]; } inline int64_t get(FrameInfoIndex index) const { if (index == FrameInfoIndex::NumIndexes) return 0; diff --git a/libs/hwui/FrameInfoVisualizer.cpp b/libs/hwui/FrameInfoVisualizer.cpp index d3adc32da848..5aea04d94cbe 100644 --- a/libs/hwui/FrameInfoVisualizer.cpp +++ b/libs/hwui/FrameInfoVisualizer.cpp @@ -22,8 +22,10 @@ #include <cutils/compiler.h> #include <array> -#define RETURN_IF_PROFILING_DISABLED() if (CC_LIKELY(mType == ProfileType::None)) return -#define RETURN_IF_DISABLED() if (CC_LIKELY(mType == ProfileType::None && !mShowDirtyRegions)) return +#define RETURN_IF_PROFILING_DISABLED() \ + if (CC_LIKELY(mType == ProfileType::None)) return +#define RETURN_IF_DISABLED() \ + if (CC_LIKELY(mType == ProfileType::None && !mShowDirtyRegions)) return #define PROFILE_DRAW_WIDTH 3 #define PROFILE_DRAW_THRESHOLD_STROKE_WIDTH 2 @@ -48,22 +50,22 @@ struct BarSegment { SkColor color; }; -static const std::array<BarSegment,7> Bar {{ - { FrameInfoIndex::IntendedVsync, FrameInfoIndex::HandleInputStart, Color::Teal_700 }, - { FrameInfoIndex::HandleInputStart, FrameInfoIndex::PerformTraversalsStart, Color::Green_700 }, - { FrameInfoIndex::PerformTraversalsStart, FrameInfoIndex::DrawStart, Color::LightGreen_700 }, - { FrameInfoIndex::DrawStart, FrameInfoIndex::SyncStart, Color::Blue_500 }, - { FrameInfoIndex::SyncStart, FrameInfoIndex::IssueDrawCommandsStart, Color::LightBlue_300 }, - { FrameInfoIndex::IssueDrawCommandsStart, FrameInfoIndex::SwapBuffers, Color::Red_500}, - { FrameInfoIndex::SwapBuffers, FrameInfoIndex::FrameCompleted, Color::Orange_500}, +static const std::array<BarSegment, 7> Bar{{ + {FrameInfoIndex::IntendedVsync, FrameInfoIndex::HandleInputStart, Color::Teal_700}, + {FrameInfoIndex::HandleInputStart, FrameInfoIndex::PerformTraversalsStart, + Color::Green_700}, + {FrameInfoIndex::PerformTraversalsStart, FrameInfoIndex::DrawStart, Color::LightGreen_700}, + {FrameInfoIndex::DrawStart, FrameInfoIndex::SyncStart, Color::Blue_500}, + {FrameInfoIndex::SyncStart, FrameInfoIndex::IssueDrawCommandsStart, Color::LightBlue_300}, + {FrameInfoIndex::IssueDrawCommandsStart, FrameInfoIndex::SwapBuffers, Color::Red_500}, + {FrameInfoIndex::SwapBuffers, FrameInfoIndex::FrameCompleted, Color::Orange_500}, }}; static int dpToPx(int dp, float density) { - return (int) (dp * density + 0.5f); + return (int)(dp * density + 0.5f); } -FrameInfoVisualizer::FrameInfoVisualizer(FrameInfoSource& source) - : mFrameSource(source) { +FrameInfoVisualizer::FrameInfoVisualizer(FrameInfoSource& source) : mFrameSource(source) { setDensity(1); } @@ -97,8 +99,8 @@ void FrameInfoVisualizer::draw(IProfileRenderer& renderer) { if (mFlashToggle) { SkPaint paint; paint.setColor(0x7fff0000); - renderer.drawRect(mDirtyRegion.fLeft, mDirtyRegion.fTop, - mDirtyRegion.fRight, mDirtyRegion.fBottom, paint); + renderer.drawRect(mDirtyRegion.fLeft, mDirtyRegion.fTop, mDirtyRegion.fRight, + mDirtyRegion.fBottom, paint); } } @@ -169,7 +171,8 @@ void FrameInfoVisualizer::initializeRects(const int baseline, const int width) { void FrameInfoVisualizer::nextBarSegment(FrameInfoIndex start, FrameInfoIndex end) { int fast_i = (mNumFastRects - 1) * 4; - int janky_i = (mNumJankyRects - 1) * 4;; + int janky_i = (mNumJankyRects - 1) * 4; + ; for (size_t fi = 0; fi < mFrameSource.size(); fi++) { if (mFrameSource[fi][FrameInfoIndex::Flags] & FrameInfoFlags::SkippedFrame) { continue; @@ -210,11 +213,8 @@ void FrameInfoVisualizer::drawThreshold(IProfileRenderer& renderer) { SkPaint paint; paint.setColor(THRESHOLD_COLOR); float yLocation = renderer.getViewportHeight() - (FRAME_THRESHOLD * mVerticalUnit); - renderer.drawRect(0.0f, - yLocation - mThresholdStroke/2, - renderer.getViewportWidth(), - yLocation + mThresholdStroke/2, - paint); + renderer.drawRect(0.0f, yLocation - mThresholdStroke / 2, renderer.getViewportWidth(), + yLocation + mThresholdStroke / 2, paint); } bool FrameInfoVisualizer::consumeProperties() { @@ -245,22 +245,19 @@ void FrameInfoVisualizer::dumpData(int fd) { // last call to dumpData(). In other words if there's a dumpData(), draw frame, // dumpData(), the last dumpData() should only log 1 frame. - FILE *file = fdopen(fd, "a"); - fprintf(file, "\n\tDraw\tPrepare\tProcess\tExecute\n"); + dprintf(fd, "\n\tDraw\tPrepare\tProcess\tExecute\n"); for (size_t i = 0; i < mFrameSource.size(); i++) { if (mFrameSource[i][FrameInfoIndex::IntendedVsync] <= mLastFrameLogged) { continue; } mLastFrameLogged = mFrameSource[i][FrameInfoIndex::IntendedVsync]; - fprintf(file, "\t%3.2f\t%3.2f\t%3.2f\t%3.2f\n", + dprintf(fd, "\t%3.2f\t%3.2f\t%3.2f\t%3.2f\n", durationMS(i, FrameInfoIndex::IntendedVsync, FrameInfoIndex::SyncStart), durationMS(i, FrameInfoIndex::SyncStart, FrameInfoIndex::IssueDrawCommandsStart), durationMS(i, FrameInfoIndex::IssueDrawCommandsStart, FrameInfoIndex::SwapBuffers), durationMS(i, FrameInfoIndex::SwapBuffers, FrameInfoIndex::FrameCompleted)); } - - fflush(file); } } /* namespace uirenderer */ diff --git a/libs/hwui/FrameMetricsObserver.h b/libs/hwui/FrameMetricsObserver.h index 4f81c8681fc8..ba72e937095f 100644 --- a/libs/hwui/FrameMetricsObserver.h +++ b/libs/hwui/FrameMetricsObserver.h @@ -26,5 +26,5 @@ public: virtual void notify(const int64_t* buffer); }; -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/FrameMetricsReporter.h b/libs/hwui/FrameMetricsReporter.h index c1cd0a9224b6..d920a99f5ee3 100644 --- a/libs/hwui/FrameMetricsReporter.h +++ b/libs/hwui/FrameMetricsReporter.h @@ -16,8 +16,8 @@ #pragma once -#include <utils/RefBase.h> #include <utils/Log.h> +#include <utils/RefBase.h> #include "FrameInfo.h" #include "FrameMetricsObserver.h" @@ -32,9 +32,7 @@ class FrameMetricsReporter { public: FrameMetricsReporter() {} - void addObserver(FrameMetricsObserver* observer) { - mObservers.push_back(observer); - } + void addObserver(FrameMetricsObserver* observer) { mObservers.push_back(observer); } bool removeObserver(FrameMetricsObserver* observer) { for (size_t i = 0; i < mObservers.size(); i++) { @@ -46,9 +44,7 @@ public: return false; } - bool hasObservers() { - return mObservers.size() > 0; - } + bool hasObservers() { return mObservers.size() > 0; } void reportFrameMetrics(const int64_t* stats) { for (size_t i = 0; i < mObservers.size(); i++) { @@ -57,9 +53,8 @@ public: } private: - std::vector< sp<FrameMetricsObserver> > mObservers; + std::vector<sp<FrameMetricsObserver> > mObservers; }; -}; // namespace uirenderer -}; // namespace android - +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/GammaFontRenderer.cpp b/libs/hwui/GammaFontRenderer.cpp index 8aff0a24b4f0..88fb16234388 100644 --- a/libs/hwui/GammaFontRenderer.cpp +++ b/libs/hwui/GammaFontRenderer.cpp @@ -14,8 +14,8 @@ * limitations under the License. */ -#include "Debug.h" #include "GammaFontRenderer.h" +#include "Debug.h" #include "Properties.h" namespace android { @@ -39,5 +39,5 @@ void GammaFontRenderer::endPrecaching() { } } -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/GammaFontRenderer.h b/libs/hwui/GammaFontRenderer.h index c9cf69bc7d9f..e9002442c64f 100644 --- a/libs/hwui/GammaFontRenderer.h +++ b/libs/hwui/GammaFontRenderer.h @@ -26,9 +26,7 @@ class GammaFontRenderer { public: GammaFontRenderer(); - void clear() { - mRenderer.reset(nullptr); - } + void clear() { mRenderer.reset(nullptr); } void flush() { if (mRenderer) { @@ -55,9 +53,7 @@ public: } } - uint32_t getSize() const { - return mRenderer ? mRenderer->getSize() : 0; - } + uint32_t getSize() const { return mRenderer ? mRenderer->getSize() : 0; } void endPrecaching(); @@ -68,7 +64,7 @@ private: #endif }; -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android -#endif // ANDROID_HWUI_GAMMA_FONT_RENDERER_H +#endif // ANDROID_HWUI_GAMMA_FONT_RENDERER_H diff --git a/libs/hwui/GlFunctorLifecycleListener.h b/libs/hwui/GlFunctorLifecycleListener.h index 357090eb31ca..5d07b46919d4 100644 --- a/libs/hwui/GlFunctorLifecycleListener.h +++ b/libs/hwui/GlFunctorLifecycleListener.h @@ -28,5 +28,5 @@ public: virtual void onGlFunctorReleased(Functor* functor) = 0; }; -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/GlLayer.cpp b/libs/hwui/GlLayer.cpp index 070e95432a11..32a30149e476 100644 --- a/libs/hwui/GlLayer.cpp +++ b/libs/hwui/GlLayer.cpp @@ -23,17 +23,16 @@ #include <utils/Log.h> -#define ATRACE_LAYER_WORK(label) \ - ATRACE_FORMAT("%s HW Layer DisplayList %s %ux%u", \ - label, \ - (renderNode.get() != NULL) ? renderNode->getName() : "", \ - getWidth(), getHeight()) +#define ATRACE_LAYER_WORK(label) \ + ATRACE_FORMAT("%s HW Layer DisplayList %s %ux%u", label, \ + (renderNode.get() != NULL) ? renderNode->getName() : "", getWidth(), \ + getHeight()) namespace android { namespace uirenderer { GlLayer::GlLayer(RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight, - SkColorFilter* colorFilter, int alpha, SkBlendMode mode, bool blend) + SkColorFilter* colorFilter, int alpha, SkBlendMode mode, bool blend) : Layer(renderState, Api::OpenGL, colorFilter, alpha, mode) , caches(Caches::getInstance()) , texture(caches) { @@ -73,5 +72,5 @@ void GlLayer::generateTexture() { } } -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/GlLayer.h b/libs/hwui/GlLayer.h index c4f7fe2a56b8..1b091914f318 100644 --- a/libs/hwui/GlLayer.h +++ b/libs/hwui/GlLayer.h @@ -35,42 +35,26 @@ public: SkColorFilter* colorFilter, int alpha, SkBlendMode mode, bool blend); virtual ~GlLayer(); - uint32_t getWidth() const override { - return texture.mWidth; - } + uint32_t getWidth() const override { return texture.mWidth; } - uint32_t getHeight() const override { - return texture.mHeight; - } + uint32_t getHeight() const override { return texture.mHeight; } void setSize(uint32_t width, uint32_t height) override { texture.updateLayout(width, height, texture.internalFormat(), texture.format(), - texture.target()); + texture.target()); } - void setBlend(bool blend) override { - texture.blend = blend; - } + void setBlend(bool blend) override { texture.blend = blend; } - bool isBlend() const override { - return texture.blend; - } + bool isBlend() const override { return texture.blend; } - inline GLuint getTextureId() const { - return texture.id(); - } + inline GLuint getTextureId() const { return texture.id(); } - inline Texture& getTexture() { - return texture; - } + inline Texture& getTexture() { return texture; } - inline GLenum getRenderTarget() const { - return texture.target(); - } + inline GLenum getRenderTarget() const { return texture.target(); } - inline bool isRenderable() const { - return texture.target() != GL_NONE; - } + inline bool isRenderable() const { return texture.target() != GL_NONE; } void setRenderTarget(GLenum renderTarget); @@ -89,7 +73,7 @@ private: * The texture backing this layer. */ Texture texture; -}; // struct GlLayer +}; // struct GlLayer -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/Glop.h b/libs/hwui/Glop.h index e91c08d5a351..c68d51604bea 100644 --- a/libs/hwui/Glop.h +++ b/libs/hwui/Glop.h @@ -41,34 +41,34 @@ class Texture; */ namespace VertexAttribFlags { - enum { - // Mesh is pure x,y vertex pairs - None = 0, - // Mesh has texture coordinates embedded. Note that texture can exist without this flag - // being set, if coordinates passed to sampler are determined another way. - TextureCoord = 1 << 0, - // Mesh has color embedded (to export to varying) - Color = 1 << 1, - // Mesh has alpha embedded (to export to varying) - Alpha = 1 << 2, - }; +enum { + // Mesh is pure x,y vertex pairs + None = 0, + // Mesh has texture coordinates embedded. Note that texture can exist without this flag + // being set, if coordinates passed to sampler are determined another way. + TextureCoord = 1 << 0, + // Mesh has color embedded (to export to varying) + Color = 1 << 1, + // Mesh has alpha embedded (to export to varying) + Alpha = 1 << 2, +}; }; /* * Enumerates transform features */ namespace TransformFlags { - enum { - None = 0, +enum { + None = 0, - // offset the eventual drawing matrix by a tiny amount to - // disambiguate sampling patterns with non-AA rendering - OffsetByFudgeFactor = 1 << 0, + // offset the eventual drawing matrix by a tiny amount to + // disambiguate sampling patterns with non-AA rendering + OffsetByFudgeFactor = 1 << 0, - // Canvas transform isn't applied to the mesh at draw time, - // since it's already built in. - MeshIgnoresCanvasTransform = 1 << 1, // TODO: remove for HWUI_NEW_OPS - }; + // Canvas transform isn't applied to the mesh at draw time, + // since it's already built in. + MeshIgnoresCanvasTransform = 1 << 1, // TODO: remove for HWUI_NEW_OPS +}; }; /** @@ -86,10 +86,11 @@ namespace TransformFlags { */ struct Glop { PREVENT_COPY_AND_ASSIGN(Glop); + public: - Glop() { } + Glop() {} struct Mesh { - GLuint primitiveMode; // GL_TRIANGLES and GL_TRIANGLE_STRIP supported + GLuint primitiveMode; // GL_TRIANGLES and GL_TRIANGLE_STRIP supported // buffer object and void* are mutually exclusive. // Only GL_UNSIGNED_SHORT supported. @@ -110,7 +111,7 @@ public: } vertices; int elementCount; - int vertexCount; // only used for meshes (for glDrawRangeElements) + int vertexCount; // only used for meshes (for glDrawRangeElements) TextureVertex mappedVertices[4]; } mesh; @@ -148,10 +149,11 @@ public: Matrix4 canvas; int transformFlags; - const Matrix4& meshTransform() const { - return (transformFlags & TransformFlags::MeshIgnoresCanvasTransform) - ? Matrix4::identity() : canvas; - } + const Matrix4& meshTransform() const { + return (transformFlags & TransformFlags::MeshIgnoresCanvasTransform) + ? Matrix4::identity() + : canvas; + } } transform; const RoundRectClipState* roundRectClipState = nullptr; diff --git a/libs/hwui/GlopBuilder.cpp b/libs/hwui/GlopBuilder.cpp index c19c1a11e3e2..2f107a004731 100644 --- a/libs/hwui/GlopBuilder.cpp +++ b/libs/hwui/GlopBuilder.cpp @@ -22,12 +22,12 @@ #include "Matrix.h" #include "Patch.h" #include "PathCache.h" -#include "renderstate/MeshState.h" -#include "renderstate/RenderState.h" #include "SkiaShader.h" #include "Texture.h" -#include "utils/PaintUtils.h" #include "VertexBuffer.h" +#include "renderstate/MeshState.h" +#include "renderstate/RenderState.h" +#include "utils/PaintUtils.h" #include <GLES2/gl2.h> #include <SkPaint.h> @@ -36,13 +36,13 @@ #if DEBUG_GLOP_BUILDER -#define TRIGGER_STAGE(stageFlag) \ - LOG_ALWAYS_FATAL_IF((stageFlag) & mStageFlags, "Stage %d cannot be run twice", (stageFlag)); \ +#define TRIGGER_STAGE(stageFlag) \ + LOG_ALWAYS_FATAL_IF((stageFlag)&mStageFlags, "Stage %d cannot be run twice", (stageFlag)); \ mStageFlags = static_cast<StageFlags>(mStageFlags | (stageFlag)) -#define REQUIRE_STAGES(requiredFlags) \ +#define REQUIRE_STAGES(requiredFlags) \ LOG_ALWAYS_FATAL_IF((mStageFlags & (requiredFlags)) != (requiredFlags), \ - "not prepared for current stage") + "not prepared for current stage") #else @@ -62,10 +62,7 @@ static void setUnitQuadTextureCoords(Rect uvs, TextureVertex* quadVertex) { } GlopBuilder::GlopBuilder(RenderState& renderState, Caches& caches, Glop* outGlop) - : mRenderState(renderState) - , mCaches(caches) - , mShader(nullptr) - , mOutGlop(outGlop) { + : mRenderState(renderState), mCaches(caches), mShader(nullptr), mOutGlop(outGlop) { mStageFlags = kInitialStage; } @@ -77,12 +74,10 @@ GlopBuilder& GlopBuilder::setMeshTexturedIndexedVbo(GLuint vbo, GLsizei elementC TRIGGER_STAGE(kMeshStage); mOutGlop->mesh.primitiveMode = GL_TRIANGLES; - mOutGlop->mesh.indices = { mRenderState.meshState().getQuadListIBO(), nullptr }; - mOutGlop->mesh.vertices = { - vbo, - VertexAttribFlags::TextureCoord, - nullptr, (const void*) kMeshTextureOffset, nullptr, - kTextureVertexStride }; + mOutGlop->mesh.indices = {mRenderState.meshState().getQuadListIBO(), nullptr}; + mOutGlop->mesh.vertices = {vbo, VertexAttribFlags::TextureCoord, + nullptr, (const void*)kMeshTextureOffset, + nullptr, kTextureVertexStride}; mOutGlop->mesh.elementCount = elementCount; return *this; } @@ -91,12 +86,13 @@ GlopBuilder& GlopBuilder::setMeshUnitQuad() { TRIGGER_STAGE(kMeshStage); mOutGlop->mesh.primitiveMode = GL_TRIANGLE_STRIP; - mOutGlop->mesh.indices = { 0, nullptr }; - mOutGlop->mesh.vertices = { - mRenderState.meshState().getUnitQuadVBO(), - VertexAttribFlags::None, - nullptr, nullptr, nullptr, - kTextureVertexStride }; + mOutGlop->mesh.indices = {0, nullptr}; + mOutGlop->mesh.vertices = {mRenderState.meshState().getUnitQuadVBO(), + VertexAttribFlags::None, + nullptr, + nullptr, + nullptr, + kTextureVertexStride}; mOutGlop->mesh.elementCount = 4; return *this; } @@ -110,12 +106,13 @@ GlopBuilder& GlopBuilder::setMeshTexturedUnitQuad(const UvMapper* uvMapper) { TRIGGER_STAGE(kMeshStage); mOutGlop->mesh.primitiveMode = GL_TRIANGLE_STRIP; - mOutGlop->mesh.indices = { 0, nullptr }; - mOutGlop->mesh.vertices = { - mRenderState.meshState().getUnitQuadVBO(), - VertexAttribFlags::TextureCoord, - nullptr, (const void*) kMeshTextureOffset, nullptr, - kTextureVertexStride }; + mOutGlop->mesh.indices = {0, nullptr}; + mOutGlop->mesh.vertices = {mRenderState.meshState().getUnitQuadVBO(), + VertexAttribFlags::TextureCoord, + nullptr, + (const void*)kMeshTextureOffset, + nullptr, + kTextureVertexStride}; mOutGlop->mesh.elementCount = 4; return *this; } @@ -130,12 +127,13 @@ GlopBuilder& GlopBuilder::setMeshTexturedUvQuad(const UvMapper* uvMapper, Rect u const TextureVertex* textureVertex = mOutGlop->mesh.mappedVertices; mOutGlop->mesh.primitiveMode = GL_TRIANGLE_STRIP; - mOutGlop->mesh.indices = { 0, nullptr }; - mOutGlop->mesh.vertices = { - 0, - VertexAttribFlags::TextureCoord, - &textureVertex[0].x, &textureVertex[0].u, nullptr, - kTextureVertexStride }; + mOutGlop->mesh.indices = {0, nullptr}; + mOutGlop->mesh.vertices = {0, + VertexAttribFlags::TextureCoord, + &textureVertex[0].x, + &textureVertex[0].u, + nullptr, + kTextureVertexStride}; mOutGlop->mesh.elementCount = 4; return *this; } @@ -144,12 +142,9 @@ GlopBuilder& GlopBuilder::setMeshIndexedQuads(Vertex* vertexData, int quadCount) TRIGGER_STAGE(kMeshStage); mOutGlop->mesh.primitiveMode = GL_TRIANGLES; - mOutGlop->mesh.indices = { mRenderState.meshState().getQuadListIBO(), nullptr }; + mOutGlop->mesh.indices = {mRenderState.meshState().getQuadListIBO(), nullptr}; mOutGlop->mesh.vertices = { - 0, - VertexAttribFlags::None, - vertexData, nullptr, nullptr, - kVertexStride }; + 0, VertexAttribFlags::None, vertexData, nullptr, nullptr, kVertexStride}; mOutGlop->mesh.elementCount = 6 * quadCount; return *this; } @@ -158,26 +153,29 @@ GlopBuilder& GlopBuilder::setMeshTexturedIndexedQuads(TextureVertex* vertexData, TRIGGER_STAGE(kMeshStage); mOutGlop->mesh.primitiveMode = GL_TRIANGLES; - mOutGlop->mesh.indices = { mRenderState.meshState().getQuadListIBO(), nullptr }; - mOutGlop->mesh.vertices = { - 0, - VertexAttribFlags::TextureCoord, - &vertexData[0].x, &vertexData[0].u, nullptr, - kTextureVertexStride }; + mOutGlop->mesh.indices = {mRenderState.meshState().getQuadListIBO(), nullptr}; + mOutGlop->mesh.vertices = {0, + VertexAttribFlags::TextureCoord, + &vertexData[0].x, + &vertexData[0].u, + nullptr, + kTextureVertexStride}; mOutGlop->mesh.elementCount = elementCount; return *this; } -GlopBuilder& GlopBuilder::setMeshColoredTexturedMesh(ColorTextureVertex* vertexData, int elementCount) { +GlopBuilder& GlopBuilder::setMeshColoredTexturedMesh(ColorTextureVertex* vertexData, + int elementCount) { TRIGGER_STAGE(kMeshStage); mOutGlop->mesh.primitiveMode = GL_TRIANGLES; - mOutGlop->mesh.indices = { 0, nullptr }; - mOutGlop->mesh.vertices = { - 0, - VertexAttribFlags::TextureCoord | VertexAttribFlags::Color, - &vertexData[0].x, &vertexData[0].u, &vertexData[0].r, - kColorTextureVertexStride }; + mOutGlop->mesh.indices = {0, nullptr}; + mOutGlop->mesh.vertices = {0, + VertexAttribFlags::TextureCoord | VertexAttribFlags::Color, + &vertexData[0].x, + &vertexData[0].u, + &vertexData[0].r, + kColorTextureVertexStride}; mOutGlop->mesh.elementCount = elementCount; return *this; } @@ -191,15 +189,16 @@ GlopBuilder& GlopBuilder::setMeshVertexBuffer(const VertexBuffer& vertexBuffer) bool indices = flags & VertexBuffer::kIndices; mOutGlop->mesh.primitiveMode = GL_TRIANGLE_STRIP; - mOutGlop->mesh.indices = { 0, vertexBuffer.getIndices() }; - mOutGlop->mesh.vertices = { - 0, - alphaVertex ? VertexAttribFlags::Alpha : VertexAttribFlags::None, - vertexBuffer.getBuffer(), nullptr, nullptr, - alphaVertex ? kAlphaVertexStride : kVertexStride }; - mOutGlop->mesh.elementCount = indices - ? vertexBuffer.getIndexCount() : vertexBuffer.getVertexCount(); - mOutGlop->mesh.vertexCount = vertexBuffer.getVertexCount(); // used for glDrawRangeElements() + mOutGlop->mesh.indices = {0, vertexBuffer.getIndices()}; + mOutGlop->mesh.vertices = {0, + alphaVertex ? VertexAttribFlags::Alpha : VertexAttribFlags::None, + vertexBuffer.getBuffer(), + nullptr, + nullptr, + alphaVertex ? kAlphaVertexStride : kVertexStride}; + mOutGlop->mesh.elementCount = + indices ? vertexBuffer.getIndexCount() : vertexBuffer.getVertexCount(); + mOutGlop->mesh.vertexCount = vertexBuffer.getVertexCount(); // used for glDrawRangeElements() return *this; } @@ -207,12 +206,13 @@ GlopBuilder& GlopBuilder::setMeshPatchQuads(const Patch& patch) { TRIGGER_STAGE(kMeshStage); mOutGlop->mesh.primitiveMode = GL_TRIANGLES; - mOutGlop->mesh.indices = { mRenderState.meshState().getQuadListIBO(), nullptr }; - mOutGlop->mesh.vertices = { - mCaches.patchCache.getMeshBuffer(), - VertexAttribFlags::TextureCoord, - (void*)patch.positionOffset, (void*)patch.textureOffset, nullptr, - kTextureVertexStride }; + mOutGlop->mesh.indices = {mRenderState.meshState().getQuadListIBO(), nullptr}; + mOutGlop->mesh.vertices = {mCaches.patchCache.getMeshBuffer(), + VertexAttribFlags::TextureCoord, + (void*)patch.positionOffset, + (void*)patch.textureOffset, + nullptr, + kTextureVertexStride}; mOutGlop->mesh.elementCount = patch.indexCount; return *this; } @@ -221,9 +221,9 @@ GlopBuilder& GlopBuilder::setMeshPatchQuads(const Patch& patch) { // Fill //////////////////////////////////////////////////////////////////////////////// -void GlopBuilder::setFill(int color, float alphaScale, - SkBlendMode mode, Blend::ModeOrderSwap modeUsage, - const SkShader* shader, const SkColorFilter* colorFilter) { +void GlopBuilder::setFill(int color, float alphaScale, SkBlendMode mode, + Blend::ModeOrderSwap modeUsage, const SkShader* shader, + const SkColorFilter* colorFilter) { if (mode != SkBlendMode::kClear) { if (!shader) { FloatColor c; @@ -235,23 +235,20 @@ void GlopBuilder::setFill(int color, float alphaScale, mOutGlop->fill.color = c; } else { float alpha = (SkColorGetA(color) / 255.0f) * alphaScale; - mOutGlop->fill.color = { 1, 1, 1, alpha }; + mOutGlop->fill.color = {1, 1, 1, alpha}; } } else { - mOutGlop->fill.color = { 0, 0, 0, 1 }; + mOutGlop->fill.color = {0, 0, 0, 1}; } - mOutGlop->blend = { GL_ZERO, GL_ZERO }; - if (mOutGlop->fill.color.a < 1.0f - || (mOutGlop->mesh.vertices.attribFlags & VertexAttribFlags::Alpha) - || (mOutGlop->fill.texture.texture && mOutGlop->fill.texture.texture->blend) - || mOutGlop->roundRectClipState - || PaintUtils::isBlendedShader(shader) - || PaintUtils::isBlendedColorFilter(colorFilter) - || mode != SkBlendMode::kSrcOver) { + mOutGlop->blend = {GL_ZERO, GL_ZERO}; + if (mOutGlop->fill.color.a < 1.0f || + (mOutGlop->mesh.vertices.attribFlags & VertexAttribFlags::Alpha) || + (mOutGlop->fill.texture.texture && mOutGlop->fill.texture.texture->blend) || + mOutGlop->roundRectClipState || PaintUtils::isBlendedShader(shader) || + PaintUtils::isBlendedColorFilter(colorFilter) || mode != SkBlendMode::kSrcOver) { if (CC_LIKELY(mode <= SkBlendMode::kScreen)) { - Blend::getFactors(mode, modeUsage, - &mOutGlop->blend.src, &mOutGlop->blend.dst); + Blend::getFactors(mode, modeUsage, &mOutGlop->blend.src, &mOutGlop->blend.dst); } else { // These blend modes are not supported by OpenGL directly and have // to be implemented using shaders. Since the shader will perform @@ -264,23 +261,25 @@ void GlopBuilder::setFill(int color, float alphaScale, // blending in shader, don't enable } else { // unsupported - Blend::getFactors(SkBlendMode::kSrcOver, modeUsage, - &mOutGlop->blend.src, &mOutGlop->blend.dst); + Blend::getFactors(SkBlendMode::kSrcOver, modeUsage, &mOutGlop->blend.src, + &mOutGlop->blend.dst); } } } - mShader = shader; // shader resolved in ::build() + mShader = shader; // shader resolved in ::build() if (colorFilter) { SkColor color; SkBlendMode bmode; SkScalar srcColorMatrix[20]; if (colorFilter->asColorMode(&color, &bmode)) { - mOutGlop->fill.filterMode = mDescription.colorOp = ProgramDescription::ColorFilterMode::Blend; + mOutGlop->fill.filterMode = mDescription.colorOp = + ProgramDescription::ColorFilterMode::Blend; mDescription.colorMode = bmode; mOutGlop->fill.filter.color.set(color); } else if (colorFilter->asColorMatrix(srcColorMatrix)) { - mOutGlop->fill.filterMode = mDescription.colorOp = ProgramDescription::ColorFilterMode::Matrix; + mOutGlop->fill.filterMode = mDescription.colorOp = + ProgramDescription::ColorFilterMode::Matrix; float* colorMatrix = mOutGlop->fill.filter.matrix.matrix; memcpy(colorMatrix, srcColorMatrix, 4 * sizeof(float)); @@ -291,11 +290,12 @@ void GlopBuilder::setFill(int color, float alphaScale, // Skia uses the range [0..255] for the addition vector, but we need // the [0..1] range to apply the vector in GLSL float* colorVector = mOutGlop->fill.filter.matrix.vector; - colorVector[0] = EOCF(srcColorMatrix[4] / 255.0f); - colorVector[1] = EOCF(srcColorMatrix[9] / 255.0f); + colorVector[0] = EOCF(srcColorMatrix[4] / 255.0f); + colorVector[1] = EOCF(srcColorMatrix[9] / 255.0f); colorVector[2] = EOCF(srcColorMatrix[14] / 255.0f); - colorVector[3] = srcColorMatrix[19] / 255.0f; // alpha is linear + colorVector[3] = srcColorMatrix[19] / 255.0f; // alpha is linear } else { + ALOGE("unsupported ColorFilter type: %s", colorFilter->getTypeName()); LOG_ALWAYS_FATAL("unsupported ColorFilter"); } } else { @@ -303,14 +303,15 @@ void GlopBuilder::setFill(int color, float alphaScale, } } -GlopBuilder& GlopBuilder::setFillTexturePaint(Texture& texture, - const int textureFillFlags, const SkPaint* paint, float alphaScale) { +GlopBuilder& GlopBuilder::setFillTexturePaint(Texture& texture, const int textureFillFlags, + const SkPaint* paint, float alphaScale) { TRIGGER_STAGE(kFillStage); REQUIRE_STAGES(kMeshStage | kRoundRectClipStage); GLenum filter = (textureFillFlags & TextureFillFlags::ForceFilter) - ? GL_LINEAR : PaintUtils::getFilter(paint); - mOutGlop->fill.texture = { &texture, filter, GL_CLAMP_TO_EDGE, nullptr }; + ? GL_LINEAR + : PaintUtils::getFilter(paint); + mOutGlop->fill.texture = {&texture, filter, GL_CLAMP_TO_EDGE, nullptr}; if (paint) { int color = paint->getColor(); @@ -321,20 +322,17 @@ GlopBuilder& GlopBuilder::setFillTexturePaint(Texture& texture, color |= 0x00FFFFFF; shader = nullptr; } - setFill(color, alphaScale, - paint->getBlendMode(), Blend::ModeOrderSwap::NoSwap, - shader, paint->getColorFilter()); + setFill(color, alphaScale, paint->getBlendMode(), Blend::ModeOrderSwap::NoSwap, shader, + paint->getColorFilter()); } else { - mOutGlop->fill.color = { alphaScale, alphaScale, alphaScale, alphaScale }; + mOutGlop->fill.color = {alphaScale, alphaScale, alphaScale, alphaScale}; - if (alphaScale < 1.0f - || (mOutGlop->mesh.vertices.attribFlags & VertexAttribFlags::Alpha) - || texture.blend - || mOutGlop->roundRectClipState) { + if (alphaScale < 1.0f || (mOutGlop->mesh.vertices.attribFlags & VertexAttribFlags::Alpha) || + texture.blend || mOutGlop->roundRectClipState) { Blend::getFactors(SkBlendMode::kSrcOver, Blend::ModeOrderSwap::NoSwap, - &mOutGlop->blend.src, &mOutGlop->blend.dst); + &mOutGlop->blend.src, &mOutGlop->blend.dst); } else { - mOutGlop->blend = { GL_ZERO, GL_ZERO }; + mOutGlop->blend = {GL_ZERO, GL_ZERO}; } } @@ -352,32 +350,28 @@ GlopBuilder& GlopBuilder::setFillPaint(const SkPaint& paint, float alphaScale, b REQUIRE_STAGES(kMeshStage | kRoundRectClipStage); if (CC_LIKELY(!shadowInterp)) { - mOutGlop->fill.texture = { - nullptr, GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr }; + mOutGlop->fill.texture = {nullptr, GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr}; } else { - mOutGlop->fill.texture = { - mCaches.textureState().getShadowLutTexture(), - GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr }; + mOutGlop->fill.texture = {mCaches.textureState().getShadowLutTexture(), GL_INVALID_ENUM, + GL_INVALID_ENUM, nullptr}; } - setFill(paint.getColor(), alphaScale, - paint.getBlendMode(), Blend::ModeOrderSwap::NoSwap, + setFill(paint.getColor(), alphaScale, paint.getBlendMode(), Blend::ModeOrderSwap::NoSwap, paint.getShader(), paint.getColorFilter()); mDescription.useShadowAlphaInterp = shadowInterp; mDescription.modulate = mOutGlop->fill.color.a < 1.0f; return *this; } -GlopBuilder& GlopBuilder::setFillPathTexturePaint(PathTexture& texture, - const SkPaint& paint, float alphaScale) { +GlopBuilder& GlopBuilder::setFillPathTexturePaint(PathTexture& texture, const SkPaint& paint, + float alphaScale) { TRIGGER_STAGE(kFillStage); REQUIRE_STAGES(kMeshStage | kRoundRectClipStage); - //specify invalid filter/clamp, since these are always static for PathTextures - mOutGlop->fill.texture = { &texture, GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr }; + // specify invalid filter/clamp, since these are always static for PathTextures + mOutGlop->fill.texture = {&texture, GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr}; - setFill(paint.getColor(), alphaScale, - paint.getBlendMode(), Blend::ModeOrderSwap::NoSwap, + setFill(paint.getColor(), alphaScale, paint.getBlendMode(), Blend::ModeOrderSwap::NoSwap, paint.getShader(), paint.getColorFilter()); mDescription.hasAlpha8Texture = true; @@ -386,12 +380,12 @@ GlopBuilder& GlopBuilder::setFillPathTexturePaint(PathTexture& texture, } GlopBuilder& GlopBuilder::setFillShadowTexturePaint(ShadowTexture& texture, int shadowColor, - const SkPaint& paint, float alphaScale) { + const SkPaint& paint, float alphaScale) { TRIGGER_STAGE(kFillStage); REQUIRE_STAGES(kMeshStage | kRoundRectClipStage); - //specify invalid filter/clamp, since these are always static for ShadowTextures - mOutGlop->fill.texture = { &texture, GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr }; + // specify invalid filter/clamp, since these are always static for ShadowTextures + mOutGlop->fill.texture = {&texture, GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr}; const int ALPHA_BITMASK = SK_ColorBLACK; const int COLOR_BITMASK = ~ALPHA_BITMASK; @@ -400,8 +394,7 @@ GlopBuilder& GlopBuilder::setFillShadowTexturePaint(ShadowTexture& texture, int shadowColor &= paint.getColor() | COLOR_BITMASK; } - setFill(shadowColor, alphaScale, - paint.getBlendMode(), Blend::ModeOrderSwap::NoSwap, + setFill(shadowColor, alphaScale, paint.getBlendMode(), Blend::ModeOrderSwap::NoSwap, paint.getShader(), paint.getColorFilter()); mDescription.hasAlpha8Texture = true; @@ -413,9 +406,9 @@ GlopBuilder& GlopBuilder::setFillBlack() { TRIGGER_STAGE(kFillStage); REQUIRE_STAGES(kMeshStage | kRoundRectClipStage); - mOutGlop->fill.texture = { nullptr, GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr }; - setFill(SK_ColorBLACK, 1.0f, SkBlendMode::kSrcOver, Blend::ModeOrderSwap::NoSwap, - nullptr, nullptr); + mOutGlop->fill.texture = {nullptr, GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr}; + setFill(SK_ColorBLACK, 1.0f, SkBlendMode::kSrcOver, Blend::ModeOrderSwap::NoSwap, nullptr, + nullptr); return *this; } @@ -423,18 +416,19 @@ GlopBuilder& GlopBuilder::setFillClear() { TRIGGER_STAGE(kFillStage); REQUIRE_STAGES(kMeshStage | kRoundRectClipStage); - mOutGlop->fill.texture = { nullptr, GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr }; - setFill(SK_ColorBLACK, 1.0f, SkBlendMode::kClear, Blend::ModeOrderSwap::NoSwap, - nullptr, nullptr); + mOutGlop->fill.texture = {nullptr, GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr}; + setFill(SK_ColorBLACK, 1.0f, SkBlendMode::kClear, Blend::ModeOrderSwap::NoSwap, nullptr, + nullptr); return *this; } GlopBuilder& GlopBuilder::setFillLayer(Texture& texture, const SkColorFilter* colorFilter, - float alpha, SkBlendMode mode, Blend::ModeOrderSwap modeUsage) { + float alpha, SkBlendMode mode, + Blend::ModeOrderSwap modeUsage) { TRIGGER_STAGE(kFillStage); REQUIRE_STAGES(kMeshStage | kRoundRectClipStage); - mOutGlop->fill.texture = { &texture, GL_LINEAR, GL_CLAMP_TO_EDGE, nullptr }; + mOutGlop->fill.texture = {&texture, GL_LINEAR, GL_CLAMP_TO_EDGE, nullptr}; setFill(SK_ColorWHITE, alpha, mode, modeUsage, nullptr, colorFilter); @@ -446,11 +440,11 @@ GlopBuilder& GlopBuilder::setFillTextureLayer(GlLayer& layer, float alpha) { TRIGGER_STAGE(kFillStage); REQUIRE_STAGES(kMeshStage | kRoundRectClipStage); - mOutGlop->fill.texture = { &(layer.getTexture()), - GL_LINEAR, GL_CLAMP_TO_EDGE, &layer.getTexTransform() }; + mOutGlop->fill.texture = {&(layer.getTexture()), GL_LINEAR, GL_CLAMP_TO_EDGE, + &layer.getTexTransform()}; - setFill(SK_ColorWHITE, alpha, layer.getMode(), Blend::ModeOrderSwap::NoSwap, - nullptr, layer.getColorFilter()); + setFill(SK_ColorWHITE, alpha, layer.getMode(), Blend::ModeOrderSwap::NoSwap, nullptr, + layer.getColorFilter()); mDescription.modulate = mOutGlop->fill.color.a < 1.0f; mDescription.hasTextureTransform = true; @@ -458,15 +452,14 @@ GlopBuilder& GlopBuilder::setFillTextureLayer(GlLayer& layer, float alpha) { } GlopBuilder& GlopBuilder::setFillExternalTexture(Texture& texture, Matrix4& textureTransform, - bool requiresFilter) { + bool requiresFilter) { TRIGGER_STAGE(kFillStage); REQUIRE_STAGES(kMeshStage | kRoundRectClipStage); GLenum filter = requiresFilter ? GL_LINEAR : GL_NEAREST; - mOutGlop->fill.texture = { &texture, filter, GL_CLAMP_TO_EDGE, &textureTransform }; + mOutGlop->fill.texture = {&texture, filter, GL_CLAMP_TO_EDGE, &textureTransform}; - setFill(SK_ColorWHITE, 1.0f, SkBlendMode::kSrc, Blend::ModeOrderSwap::NoSwap, - nullptr, nullptr); + setFill(SK_ColorWHITE, 1.0f, SkBlendMode::kSrc, Blend::ModeOrderSwap::NoSwap, nullptr, nullptr); mDescription.modulate = mOutGlop->fill.color.a < 1.0f; mDescription.hasTextureTransform = true; @@ -517,8 +510,8 @@ GlopBuilder& GlopBuilder::setModelViewMapUnitToRectSnap(const Rect destination) const float translateX = meshTransform.getTranslateX(); const float translateY = meshTransform.getTranslateY(); - left = (int) floorf(left + translateX + 0.5f) - translateX; - top = (int) floorf(top + translateY + 0.5f) - translateY; + left = (int)floorf(left + translateX + 0.5f) - translateX; + top = (int)floorf(top + translateY + 0.5f) - translateY; mOutGlop->fill.texture.filter = GL_NEAREST; } @@ -534,7 +527,8 @@ GlopBuilder& GlopBuilder::setModelViewOffsetRect(float offsetX, float offsetY, c return *this; } -GlopBuilder& GlopBuilder::setModelViewOffsetRectSnap(float offsetX, float offsetY, const Rect source) { +GlopBuilder& GlopBuilder::setModelViewOffsetRectSnap(float offsetX, float offsetY, + const Rect source) { TRIGGER_STAGE(kModelViewStage); REQUIRE_STAGES(kTransformStage | kFillStage); @@ -544,8 +538,8 @@ GlopBuilder& GlopBuilder::setModelViewOffsetRectSnap(float offsetX, float offset const float translateX = meshTransform.getTranslateX(); const float translateY = meshTransform.getTranslateY(); - offsetX = (int) floorf(offsetX + translateX + source.left + 0.5f) - translateX - source.left; - offsetY = (int) floorf(offsetY + translateY + source.top + 0.5f) - translateY - source.top; + offsetX = (int)floorf(offsetX + translateX + source.left + 0.5f) - translateX - source.left; + offsetY = (int)floorf(offsetY + translateY + source.top + 0.5f) - translateY - source.top; mOutGlop->fill.texture.filter = GL_NEAREST; } @@ -571,27 +565,25 @@ GlopBuilder& GlopBuilder::setRoundRectClipState(const RoundRectClipState* roundR void verify(const ProgramDescription& description, const Glop& glop) { if (glop.fill.texture.texture != nullptr) { - LOG_ALWAYS_FATAL_IF(((description.hasTexture && description.hasExternalTexture) - || (!description.hasTexture - && !description.hasExternalTexture - && !description.useShadowAlphaInterp) - || ((glop.mesh.vertices.attribFlags & VertexAttribFlags::TextureCoord) == 0 - && !description.useShadowAlphaInterp)), - "Texture %p, hT%d, hET %d, attribFlags %x", - glop.fill.texture.texture, + LOG_ALWAYS_FATAL_IF( + ((description.hasTexture && description.hasExternalTexture) || + (!description.hasTexture && !description.hasExternalTexture && + !description.useShadowAlphaInterp) || + ((glop.mesh.vertices.attribFlags & VertexAttribFlags::TextureCoord) == 0 && + !description.useShadowAlphaInterp)), + "Texture %p, hT%d, hET %d, attribFlags %x", glop.fill.texture.texture, description.hasTexture, description.hasExternalTexture, glop.mesh.vertices.attribFlags); } else { - LOG_ALWAYS_FATAL_IF((description.hasTexture - || description.hasExternalTexture - || ((glop.mesh.vertices.attribFlags & VertexAttribFlags::TextureCoord) != 0)), - "No texture, hT%d, hET %d, attribFlags %x", - description.hasTexture, description.hasExternalTexture, - glop.mesh.vertices.attribFlags); + LOG_ALWAYS_FATAL_IF( + (description.hasTexture || description.hasExternalTexture || + ((glop.mesh.vertices.attribFlags & VertexAttribFlags::TextureCoord) != 0)), + "No texture, hT%d, hET %d, attribFlags %x", description.hasTexture, + description.hasExternalTexture, glop.mesh.vertices.attribFlags); } - if ((glop.mesh.vertices.attribFlags & VertexAttribFlags::Alpha) - && glop.mesh.vertices.bufferObject) { + if ((glop.mesh.vertices.attribFlags & VertexAttribFlags::Alpha) && + glop.mesh.vertices.bufferObject) { LOG_ALWAYS_FATAL("VBO and alpha attributes are not currently compatible"); } @@ -620,9 +612,10 @@ void GlopBuilder::build() { // Enable debug highlight when what we're about to draw is tested against // the stencil buffer and if stencil highlight debugging is on - mDescription.hasDebugHighlight = !Properties::debugOverdraw - && Properties::debugStencilClip == StencilClipDebug::ShowHighlight - && mRenderState.stencil().isTestEnabled(); + mDescription.hasDebugHighlight = + !Properties::debugOverdraw && + Properties::debugStencilClip == StencilClipDebug::ShowHighlight && + mRenderState.stencil().isTestEnabled(); // serialize shader info into ShaderData GLuint textureUnit = mOutGlop->fill.texture.texture ? 1 : 0; @@ -639,15 +632,13 @@ void GlopBuilder::build() { } else { shaderMatrix = mOutGlop->transform.modelView; } - SkiaShader::store(mCaches, *mShader, shaderMatrix, - &textureUnit, &mDescription, &(mOutGlop->fill.skiaShaderData)); + SkiaShader::store(mCaches, *mShader, shaderMatrix, &textureUnit, &mDescription, + &(mOutGlop->fill.skiaShaderData)); } // duplicates ProgramCache's definition of color uniform presence - const bool singleColor = !mDescription.hasTexture - && !mDescription.hasExternalTexture - && !mDescription.hasGradient - && !mDescription.hasBitmap; + const bool singleColor = !mDescription.hasTexture && !mDescription.hasExternalTexture && + !mDescription.hasGradient && !mDescription.hasBitmap; mOutGlop->fill.colorEnabled = mDescription.modulate || singleColor; verify(mDescription, *mOutGlop); @@ -660,31 +651,31 @@ void GlopBuilder::dump(const Glop& glop) { ALOGD("Glop Mesh"); const Glop::Mesh& mesh = glop.mesh; ALOGD(" primitive mode: %d", mesh.primitiveMode); - ALOGD(" indices: buffer obj %x, indices %p", mesh.indices.bufferObject, mesh.indices.indices); + ALOGD(" indices: buffer obj %x, indices %p", mesh.indices.bufferObject, + mesh.indices.indices); const Glop::Mesh::Vertices& vertices = glop.mesh.vertices; ALOGD(" vertices: buffer obj %x, flags %x, pos %p, tex %p, clr %p, stride %d", - vertices.bufferObject, vertices.attribFlags, - vertices.position, vertices.texCoord, vertices.color, vertices.stride); + vertices.bufferObject, vertices.attribFlags, vertices.position, vertices.texCoord, + vertices.color, vertices.stride); ALOGD(" element count: %d", mesh.elementCount); ALOGD("Glop Fill"); const Glop::Fill& fill = glop.fill; ALOGD(" program %p", fill.program); if (fill.texture.texture) { - ALOGD(" texture %p, target %d, filter %d, clamp %d", - fill.texture.texture, fill.texture.texture->target(), - fill.texture.filter, fill.texture.clamp); + ALOGD(" texture %p, target %d, filter %d, clamp %d", fill.texture.texture, + fill.texture.texture->target(), fill.texture.filter, fill.texture.clamp); if (fill.texture.textureTransform) { fill.texture.textureTransform->dump("texture transform"); } } - ALOGD_IF(fill.colorEnabled, " color (argb) %.2f %.2f %.2f %.2f", - fill.color.a, fill.color.r, fill.color.g, fill.color.b); - ALOGD_IF(fill.filterMode != ProgramDescription::ColorFilterMode::None, - " filterMode %d", (int)fill.filterMode); + ALOGD_IF(fill.colorEnabled, " color (argb) %.2f %.2f %.2f %.2f", fill.color.a, fill.color.r, + fill.color.g, fill.color.b); + ALOGD_IF(fill.filterMode != ProgramDescription::ColorFilterMode::None, " filterMode %d", + (int)fill.filterMode); ALOGD_IF(fill.skiaShaderData.skiaShaderType, " shader type %d", - fill.skiaShaderData.skiaShaderType); + fill.skiaShaderData.skiaShaderType); ALOGD("Glop transform"); glop.transform.modelView.dump(" model view"); diff --git a/libs/hwui/GlopBuilder.h b/libs/hwui/GlopBuilder.h index 6d11da19e138..dac38223b877 100644 --- a/libs/hwui/GlopBuilder.h +++ b/libs/hwui/GlopBuilder.h @@ -39,15 +39,16 @@ struct PathTexture; struct ShadowTexture; namespace TextureFillFlags { - enum { - None = 0, - IsAlphaMaskTexture = 1 << 0, - ForceFilter = 1 << 1, - }; +enum { + None = 0, + IsAlphaMaskTexture = 1 << 0, + ForceFilter = 1 << 1, +}; } class GlopBuilder { PREVENT_COPY_AND_ASSIGN(GlopBuilder); + public: GlopBuilder(RenderState& renderState, Caches& caches, Glop* outGlop); @@ -57,26 +58,29 @@ public: GlopBuilder& setMeshTexturedUvQuad(const UvMapper* uvMapper, const Rect uvs); GlopBuilder& setMeshVertexBuffer(const VertexBuffer& vertexBuffer); GlopBuilder& setMeshIndexedQuads(Vertex* vertexData, int quadCount); - GlopBuilder& setMeshColoredTexturedMesh(ColorTextureVertex* vertexData, int elementCount); // TODO: use indexed quads - GlopBuilder& setMeshTexturedIndexedQuads(TextureVertex* vertexData, int elementCount); // TODO: take quadCount + GlopBuilder& setMeshColoredTexturedMesh(ColorTextureVertex* vertexData, + int elementCount); // TODO: use indexed quads + GlopBuilder& setMeshTexturedIndexedQuads(TextureVertex* vertexData, + int elementCount); // TODO: take quadCount GlopBuilder& setMeshPatchQuads(const Patch& patch); - GlopBuilder& setFillPaint(const SkPaint& paint, float alphaScale, bool shadowInterp = false); // TODO: avoid boolean with default + GlopBuilder& setFillPaint(const SkPaint& paint, float alphaScale, + bool shadowInterp = false); // TODO: avoid boolean with default GlopBuilder& setFillTexturePaint(Texture& texture, const int textureFillFlags, - const SkPaint* paint, float alphaScale); - GlopBuilder& setFillPathTexturePaint(PathTexture& texture, - const SkPaint& paint, float alphaScale); + const SkPaint* paint, float alphaScale); + GlopBuilder& setFillPathTexturePaint(PathTexture& texture, const SkPaint& paint, + float alphaScale); GlopBuilder& setFillShadowTexturePaint(ShadowTexture& texture, int shadowColor, - const SkPaint& paint, float alphaScale); + const SkPaint& paint, float alphaScale); GlopBuilder& setFillBlack(); GlopBuilder& setFillClear(); - GlopBuilder& setFillLayer(Texture& texture, const SkColorFilter* colorFilter, - float alpha, SkBlendMode mode, Blend::ModeOrderSwap modeUsage); + GlopBuilder& setFillLayer(Texture& texture, const SkColorFilter* colorFilter, float alpha, + SkBlendMode mode, Blend::ModeOrderSwap modeUsage); GlopBuilder& setFillTextureLayer(GlLayer& layer, float alpha); // TODO: setFillLayer normally forces its own wrap & filter mode, // which isn't always correct. GlopBuilder& setFillExternalTexture(Texture& texture, Matrix4& textureTransform, - bool requiresFilter); + bool requiresFilter); GlopBuilder& setTransform(const Matrix4& canvas, const int transformFlags); @@ -91,8 +95,8 @@ public: } GlopBuilder& setModelViewOffsetRect(float offsetX, float offsetY, const Rect source); GlopBuilder& setModelViewOffsetRectSnap(float offsetX, float offsetY, const Rect source); - GlopBuilder& setModelViewOffsetRectOptionalSnap(bool snap, - float offsetX, float offsetY, const Rect& source) { + GlopBuilder& setModelViewOffsetRectOptionalSnap(bool snap, float offsetX, float offsetY, + const Rect& source) { if (snap) { return setModelViewOffsetRectSnap(offsetX, offsetY, source); } else { @@ -111,10 +115,10 @@ public: void build(); static void dump(const Glop& glop); + private: - void setFill(int color, float alphaScale, - SkBlendMode mode, Blend::ModeOrderSwap modeUsage, - const SkShader* shader, const SkColorFilter* colorFilter); + void setFill(int color, float alphaScale, SkBlendMode mode, Blend::ModeOrderSwap modeUsage, + const SkShader* shader, const SkColorFilter* colorFilter); enum StageFlags { kInitialStage = 0, @@ -123,7 +127,8 @@ private: kModelViewStage = 1 << 2, kFillStage = 1 << 3, kRoundRectClipStage = 1 << 4, - kAllStages = kMeshStage | kFillStage | kTransformStage | kModelViewStage | kRoundRectClipStage, + kAllStages = + kMeshStage | kFillStage | kTransformStage | kModelViewStage | kRoundRectClipStage, } mStageFlags; ProgramDescription mDescription; diff --git a/libs/hwui/GpuMemoryTracker.cpp b/libs/hwui/GpuMemoryTracker.cpp index a52ec8738015..612bfde1a3fa 100644 --- a/libs/hwui/GpuMemoryTracker.cpp +++ b/libs/hwui/GpuMemoryTracker.cpp @@ -14,11 +14,11 @@ * limitations under the License. */ -#include "utils/StringUtils.h" #include "Texture.h" +#include "utils/StringUtils.h" -#include <cutils/compiler.h> #include <GpuMemoryTracker.h> +#include <cutils/compiler.h> #include <utils/Trace.h> #include <array> #include <sstream> @@ -33,9 +33,7 @@ pthread_t gGpuThread = 0; #define NUM_TYPES static_cast<int>(GpuObjectType::TypeCount) const char* TYPE_NAMES[] = { - "Texture", - "OffscreenBuffer", - "Layer", + "Texture", "OffscreenBuffer", "Layer", }; struct TypeStats { @@ -55,21 +53,22 @@ void GpuMemoryTracker::notifySizeChanged(int newSize) { void GpuMemoryTracker::startTrackingObject() { auto result = gObjectSet.insert(this); LOG_ALWAYS_FATAL_IF(!result.second, - "startTrackingObject() on %p failed, already being tracked!", this); + "startTrackingObject() on %p failed, already being tracked!", this); gObjectStats[static_cast<int>(mType)].count++; } void GpuMemoryTracker::stopTrackingObject() { size_t removed = gObjectSet.erase(this); - LOG_ALWAYS_FATAL_IF(removed != 1, - "stopTrackingObject removed %zd, is %p not being tracked?", - removed, this); + LOG_ALWAYS_FATAL_IF(removed != 1, "stopTrackingObject removed %zd, is %p not being tracked?", + removed, this); gObjectStats[static_cast<int>(mType)].count--; } void GpuMemoryTracker::onGpuContextCreated() { - LOG_ALWAYS_FATAL_IF(gGpuThread != 0, "We already have a gpu thread? " - "current = %lu, gpu thread = %lu", pthread_self(), gGpuThread); + LOG_ALWAYS_FATAL_IF(gGpuThread != 0, + "We already have a gpu thread? " + "current = %lu, gpu thread = %lu", + pthread_self(), gGpuThread); gGpuThread = pthread_self(); } @@ -124,8 +123,8 @@ void GpuMemoryTracker::onFrameCompleted() { if (obj->objectType() == GpuObjectType::Texture) { const Texture* texture = static_cast<Texture*>(obj); if (texture->cleanup) { - ALOGE("Leaked texture marked for cleanup! id=%u, size %ux%u", - texture->id(), texture->width(), texture->height()); + ALOGE("Leaked texture marked for cleanup! id=%u, size %ux%u", texture->id(), + texture->width(), texture->height()); freeList.push_back(texture); } } @@ -136,5 +135,5 @@ void GpuMemoryTracker::onFrameCompleted() { } } -} // namespace uirenderer -} // namespace android; +} // namespace uirenderer +} // namespace android; diff --git a/libs/hwui/GpuMemoryTracker.h b/libs/hwui/GpuMemoryTracker.h index 18e2330668b0..de3ca99ef14b 100644 --- a/libs/hwui/GpuMemoryTracker.h +++ b/libs/hwui/GpuMemoryTracker.h @@ -25,11 +25,11 @@ namespace uirenderer { extern pthread_t gGpuThread; -#define ASSERT_GPU_THREAD() LOG_ALWAYS_FATAL_IF( \ - !pthread_equal(gGpuThread, pthread_self()), \ - "Error, %p of type %d (size=%d) used on wrong thread! cur thread %lu " \ - "!= gpu thread %lu", this, static_cast<int>(mType), mSize, \ - pthread_self(), gGpuThread) +#define ASSERT_GPU_THREAD() \ + LOG_ALWAYS_FATAL_IF(!pthread_equal(gGpuThread, pthread_self()), \ + "Error, %p of type %d (size=%d) used on wrong thread! cur thread %lu " \ + "!= gpu thread %lu", \ + this, static_cast<int>(mType), mSize, pthread_self(), gGpuThread) enum class GpuObjectType { Texture = 0, @@ -73,5 +73,5 @@ private: GpuObjectType mType; }; -} // namespace uirenderer -} // namespace android; +} // namespace uirenderer +} // namespace android; diff --git a/libs/hwui/GradientCache.cpp b/libs/hwui/GradientCache.cpp index 20262349dda4..21e3c730cbec 100644 --- a/libs/hwui/GradientCache.cpp +++ b/libs/hwui/GradientCache.cpp @@ -18,9 +18,9 @@ #include "Caches.h" #include "Debug.h" +#include "DeviceInfo.h" #include "GradientCache.h" #include "Properties.h" -#include "DeviceInfo.h" #include <cutils/properties.h> @@ -31,7 +31,7 @@ namespace uirenderer { // Functions /////////////////////////////////////////////////////////////////////////////// -template<typename T> +template <typename T> static inline T min(T a, T b) { return a < b ? a : b; } @@ -122,8 +122,7 @@ void GradientCache::clear() { mCache.clear(); } -void GradientCache::getGradientInfo(const uint32_t* colors, const int count, - GradientInfo& info) { +void GradientCache::getGradientInfo(const uint32_t* colors, const int count, GradientInfo& info) { uint32_t width = 256 * (count - 1); // If the npot extension is not supported we cannot use non-clamp @@ -145,9 +144,8 @@ void GradientCache::getGradientInfo(const uint32_t* colors, const int count, info.hasAlpha = hasAlpha; } -Texture* GradientCache::addLinearGradient(GradientCacheEntry& gradient, - uint32_t* colors, float* positions, int count) { - +Texture* GradientCache::addLinearGradient(GradientCacheEntry& gradient, uint32_t* colors, + float* positions, int count) { GradientInfo info; getGradientInfo(colors, count, info); @@ -159,18 +157,19 @@ Texture* GradientCache::addLinearGradient(GradientCacheEntry& gradient, const uint32_t size = info.width * 2 * bytesPerPixel(); while (getSize() + size > mMaxSize) { LOG_ALWAYS_FATAL_IF(!mCache.removeOldest(), - "Ran out of things to remove from the cache? getSize() = %" PRIu32 - ", size = %" PRIu32 ", mMaxSize = %" PRIu32 ", width = %" PRIu32, - getSize(), size, mMaxSize, info.width); + "Ran out of things to remove from the cache? getSize() = %" PRIu32 + ", size = %" PRIu32 ", mMaxSize = %" PRIu32 ", width = %" PRIu32, + getSize(), size, mMaxSize, info.width); } generateTexture(colors, positions, info.width, 2, texture); mSize += size; LOG_ALWAYS_FATAL_IF((int)size != texture->objectSize(), - "size != texture->objectSize(), size %" PRIu32 ", objectSize %d" - " width = %" PRIu32 " bytesPerPixel() = %zu", - size, texture->objectSize(), info.width, bytesPerPixel()); + "size != texture->objectSize(), size %" PRIu32 + ", objectSize %d" + " width = %" PRIu32 " bytesPerPixel() = %zu", + size, texture->objectSize(), info.width, bytesPerPixel()); mCache.put(gradient, texture); return texture; @@ -186,8 +185,8 @@ size_t GradientCache::sourceBytesPerPixel() const { return 4 * (mUseFloatTexture ? sizeof(float) : sizeof(uint8_t)); } -void GradientCache::mixBytes(const FloatColor& start, const FloatColor& end, - float amount, uint8_t*& dst) const { +void GradientCache::mixBytes(const FloatColor& start, const FloatColor& end, float amount, + uint8_t*& dst) const { float oppAmount = 1.0f - amount; float a = start.a * oppAmount + end.a * amount; *dst++ = uint8_t(OECF(start.r * oppAmount + end.r * amount) * 255.0f); @@ -196,11 +195,11 @@ void GradientCache::mixBytes(const FloatColor& start, const FloatColor& end, *dst++ = uint8_t(a * 255.0f); } -void GradientCache::mixFloats(const FloatColor& start, const FloatColor& end, - float amount, uint8_t*& dst) const { +void GradientCache::mixFloats(const FloatColor& start, const FloatColor& end, float amount, + uint8_t*& dst) const { float oppAmount = 1.0f - amount; float a = start.a * oppAmount + end.a * amount; - float* d = (float*) dst; + float* d = (float*)dst; #ifdef ANDROID_ENABLE_LINEAR_BLENDING // We want to stay linear *d++ = (start.r * oppAmount + end.r * amount); @@ -215,8 +214,8 @@ void GradientCache::mixFloats(const FloatColor& start, const FloatColor& end, dst += 4 * sizeof(float); } -void GradientCache::generateTexture(uint32_t* colors, float* positions, - const uint32_t width, const uint32_t height, Texture* texture) { +void GradientCache::generateTexture(uint32_t* colors, float* positions, const uint32_t width, + const uint32_t height, Texture* texture) { const GLsizei rowBytes = width * sourceBytesPerPixel(); uint8_t pixels[rowBytes * height]; @@ -269,5 +268,5 @@ void GradientCache::generateTexture(uint32_t* colors, float* positions, texture->setWrap(GL_CLAMP_TO_EDGE); } -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/GradientCache.h b/libs/hwui/GradientCache.h index d95589c514eb..ff426cd3425b 100644 --- a/libs/hwui/GradientCache.h +++ b/libs/hwui/GradientCache.h @@ -60,13 +60,9 @@ struct GradientCacheEntry { static int compare(const GradientCacheEntry& lhs, const GradientCacheEntry& rhs); - bool operator==(const GradientCacheEntry& other) const { - return compare(*this, other) == 0; - } + bool operator==(const GradientCacheEntry& other) const { return compare(*this, other) == 0; } - bool operator!=(const GradientCacheEntry& other) const { - return compare(*this, other) != 0; - } + bool operator!=(const GradientCacheEntry& other) const { return compare(*this, other) != 0; } std::unique_ptr<uint32_t[]> colors; std::unique_ptr<float[]> positions; @@ -82,7 +78,7 @@ private: memcpy(this->positions.get(), positions, count * sizeof(float)); } -}; // GradientCacheEntry +}; // GradientCacheEntry // Caching support @@ -103,7 +99,7 @@ inline hash_t hash_type(const GradientCacheEntry& entry) { * Any texture added to the cache causing the cache to grow beyond the maximum * allowed size will also cause the oldest texture to be kicked out. */ -class GradientCache: public OnEntryRemoved<GradientCacheEntry, Texture*> { +class GradientCache : public OnEntryRemoved<GradientCacheEntry, Texture*> { public: explicit GradientCache(const Extensions& extensions); ~GradientCache(); @@ -138,11 +134,11 @@ private: * Adds a new linear gradient to the cache. The generated texture is * returned. */ - Texture* addLinearGradient(GradientCacheEntry& gradient, - uint32_t* colors, float* positions, int count); + Texture* addLinearGradient(GradientCacheEntry& gradient, uint32_t* colors, float* positions, + int count); - void generateTexture(uint32_t* colors, float* positions, - const uint32_t width, const uint32_t height, Texture* texture); + void generateTexture(uint32_t* colors, float* positions, const uint32_t width, + const uint32_t height, Texture* texture); struct GradientInfo { uint32_t width; @@ -155,12 +151,12 @@ private: size_t sourceBytesPerPixel() const; typedef void (GradientCache::*ChannelMixer)(const FloatColor& start, const FloatColor& end, - float amount, uint8_t*& dst) const; + float amount, uint8_t*& dst) const; - void mixBytes(const FloatColor& start, const FloatColor& end, - float amount, uint8_t*& dst) const; - void mixFloats(const FloatColor& start, const FloatColor& end, - float amount, uint8_t*& dst) const; + void mixBytes(const FloatColor& start, const FloatColor& end, float amount, + uint8_t*& dst) const; + void mixFloats(const FloatColor& start, const FloatColor& end, float amount, + uint8_t*& dst) const; LruCache<GradientCacheEntry, Texture*> mCache; @@ -173,9 +169,9 @@ private: bool mHasLinearBlending; mutable Mutex mLock; -}; // class GradientCache +}; // class GradientCache -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android -#endif // ANDROID_HWUI_GRADIENT_CACHE_H +#endif // ANDROID_HWUI_GRADIENT_CACHE_H diff --git a/libs/hwui/IProfileRenderer.h b/libs/hwui/IProfileRenderer.h index 947ed34cc070..935ab4e35f26 100644 --- a/libs/hwui/IProfileRenderer.h +++ b/libs/hwui/IProfileRenderer.h @@ -22,7 +22,7 @@ namespace uirenderer { class IProfileRenderer { public: virtual void drawRect(float left, float top, float right, float bottom, - const SkPaint& paint) = 0; + const SkPaint& paint) = 0; virtual void drawRects(const float* rects, int count, const SkPaint& paint) = 0; virtual uint32_t getViewportWidth() = 0; virtual uint32_t getViewportHeight() = 0; diff --git a/libs/hwui/Image.cpp b/libs/hwui/Image.cpp index 68a356ba1be0..d30796d01479 100644 --- a/libs/hwui/Image.cpp +++ b/libs/hwui/Image.cpp @@ -25,11 +25,11 @@ namespace uirenderer { Image::Image(sp<GraphicBuffer> buffer) { // Create the EGLImage object that maps the GraphicBuffer EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); - EGLClientBuffer clientBuffer = (EGLClientBuffer) buffer->getNativeBuffer(); - EGLint attrs[] = { EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE }; + EGLClientBuffer clientBuffer = (EGLClientBuffer)buffer->getNativeBuffer(); + EGLint attrs[] = {EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE}; - mImage = eglCreateImageKHR(display, EGL_NO_CONTEXT, - EGL_NATIVE_BUFFER_ANDROID, clientBuffer, attrs); + mImage = eglCreateImageKHR(display, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, clientBuffer, + attrs); if (mImage == EGL_NO_IMAGE_KHR) { ALOGW("Error creating image (%#x)", eglGetError()); @@ -57,5 +57,5 @@ Image::~Image() { } } -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/Image.h b/libs/hwui/Image.h index b8f5a5bec087..989b6ff3648d 100644 --- a/libs/hwui/Image.h +++ b/libs/hwui/Image.h @@ -45,23 +45,19 @@ public: * Returns the name of the GL texture that can be used to sample * from this image. */ - GLuint getTexture() const { - return mTexture; - } + GLuint getTexture() const { return mTexture; } /** * Returns the name of the EGL image represented by this object. */ - EGLImageKHR getImage() const { - return mImage; - } + EGLImageKHR getImage() const { return mImage; } private: GLuint mTexture; EGLImageKHR mImage; -}; // class Image +}; // class Image -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android -#endif // ANDROID_HWUI_IMAGE_H +#endif // ANDROID_HWUI_IMAGE_H diff --git a/libs/hwui/Interpolator.cpp b/libs/hwui/Interpolator.cpp index d740c038f36d..7bdca2ee0f00 100644 --- a/libs/hwui/Interpolator.cpp +++ b/libs/hwui/Interpolator.cpp @@ -54,8 +54,10 @@ static float o(float t, float s) { } float AnticipateOvershootInterpolator::interpolate(float t) { - if (t < 0.5f) return 0.5f * a(t * 2.0f, mTension); - else return 0.5f * (o(t * 2.0f - 2.0f, mTension) + 2.0f); + if (t < 0.5f) + return 0.5f * a(t * 2.0f, mTension); + else + return 0.5f * (o(t * 2.0f - 2.0f, mTension) + 2.0f); } static float bounce(float t) { @@ -64,10 +66,14 @@ static float bounce(float t) { float BounceInterpolator::interpolate(float t) { t *= 1.1226f; - if (t < 0.3535f) return bounce(t); - else if (t < 0.7408f) return bounce(t - 0.54719f) + 0.7f; - else if (t < 0.9644f) return bounce(t - 0.8526f) + 0.9f; - else return bounce(t - 1.0435f) + 0.95f; + if (t < 0.3535f) + return bounce(t); + else if (t < 0.7408f) + return bounce(t - 0.54719f) + 0.7f; + else if (t < 0.9644f) + return bounce(t - 0.8526f) + 0.9f; + else + return bounce(t - 1.0435f) + 0.95f; } float CycleInterpolator::interpolate(float input) { @@ -119,16 +125,11 @@ float PathInterpolator::interpolate(float t) { float startY = mY[startIndex]; float endY = mY[endIndex]; return startY + (fraction * (endY - startY)); - } -LUTInterpolator::LUTInterpolator(float* values, size_t size) - : mValues(values) - , mSize(size) { -} +LUTInterpolator::LUTInterpolator(float* values, size_t size) : mValues(values), mSize(size) {} -LUTInterpolator::~LUTInterpolator() { -} +LUTInterpolator::~LUTInterpolator() {} float LUTInterpolator::interpolate(float input) { // lut position should only be at the end of the table when input is 1f. @@ -140,10 +141,12 @@ float LUTInterpolator::interpolate(float input) { float ipart, weight; weight = modff(lutpos, &ipart); - int i1 = (int) ipart; - int i2 = std::min(i1 + 1, (int) mSize - 1); + int i1 = (int)ipart; + int i2 = std::min(i1 + 1, (int)mSize - 1); - LOG_ALWAYS_FATAL_IF(i1 < 0 || i2 < 0, "negatives in interpolation!" + LOG_ALWAYS_FATAL_IF( + i1 < 0 || i2 < 0, + "negatives in interpolation!" " i1=%d, i2=%d, input=%f, lutpos=%f, size=%zu, values=%p, ipart=%f, weight=%f", i1, i2, input, lutpos, mSize, mValues.get(), ipart, weight); @@ -153,6 +156,5 @@ float LUTInterpolator::interpolate(float input) { return MathUtils::lerp(v1, v2, weight); } - } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/Interpolator.h b/libs/hwui/Interpolator.h index 224cee70acc7..452988fc8711 100644 --- a/libs/hwui/Interpolator.h +++ b/libs/hwui/Interpolator.h @@ -44,8 +44,9 @@ public: class ANDROID_API AccelerateInterpolator : public Interpolator { public: - explicit AccelerateInterpolator(float factor) : mFactor(factor), mDoubleFactor(factor*2) {} + explicit AccelerateInterpolator(float factor) : mFactor(factor), mDoubleFactor(factor * 2) {} virtual float interpolate(float input) override; + private: const float mFactor; const float mDoubleFactor; @@ -55,6 +56,7 @@ class ANDROID_API AnticipateInterpolator : public Interpolator { public: explicit AnticipateInterpolator(float tension) : mTension(tension) {} virtual float interpolate(float input) override; + private: const float mTension; }; @@ -63,6 +65,7 @@ class ANDROID_API AnticipateOvershootInterpolator : public Interpolator { public: explicit AnticipateOvershootInterpolator(float tension) : mTension(tension) {} virtual float interpolate(float input) override; + private: const float mTension; }; @@ -76,6 +79,7 @@ class ANDROID_API CycleInterpolator : public Interpolator { public: explicit CycleInterpolator(float cycles) : mCycles(cycles) {} virtual float interpolate(float input) override; + private: const float mCycles; }; @@ -84,6 +88,7 @@ class ANDROID_API DecelerateInterpolator : public Interpolator { public: explicit DecelerateInterpolator(float factor) : mFactor(factor) {} virtual float interpolate(float input) override; + private: const float mFactor; }; @@ -97,15 +102,16 @@ class ANDROID_API OvershootInterpolator : public Interpolator { public: explicit OvershootInterpolator(float tension) : mTension(tension) {} virtual float interpolate(float input) override; + private: const float mTension; }; class ANDROID_API PathInterpolator : public Interpolator { public: - explicit PathInterpolator(std::vector<float>&& x, std::vector<float>&& y) - : mX (x), mY(y) {} + explicit PathInterpolator(std::vector<float>&& x, std::vector<float>&& y) : mX(x), mY(y) {} virtual float interpolate(float input) override; + private: std::vector<float> mX; std::vector<float> mY; diff --git a/libs/hwui/JankTracker.cpp b/libs/hwui/JankTracker.cpp index 9d1182819444..f41956cdebeb 100644 --- a/libs/hwui/JankTracker.cpp +++ b/libs/hwui/JankTracker.cpp @@ -27,9 +27,11 @@ #include <cutils/ashmem.h> #include <log/log.h> +#include <sstream> #include "Properties.h" #include "utils/TimeUtils.h" +#include "utils/Trace.h" namespace android { namespace uirenderer { @@ -106,25 +108,23 @@ void JankTracker::setFrameInterval(nsecs_t frameInterval) { mThresholds[kSlowUI] = static_cast<int64_t>(.5 * frameInterval); mThresholds[kSlowSync] = static_cast<int64_t>(.2 * frameInterval); mThresholds[kSlowRT] = static_cast<int64_t>(.75 * frameInterval); - } void JankTracker::finishFrame(const FrameInfo& frame) { // Fast-path for jank-free frames int64_t totalDuration = frame.duration(sFrameStart, FrameInfoIndex::FrameCompleted); - if (mDequeueTimeForgiveness - && frame[FrameInfoIndex::DequeueBufferDuration] > 500_us) { - nsecs_t expectedDequeueDuration = - mDequeueTimeForgiveness + frame[FrameInfoIndex::Vsync] - - frame[FrameInfoIndex::IssueDrawCommandsStart]; + if (mDequeueTimeForgiveness && frame[FrameInfoIndex::DequeueBufferDuration] > 500_us) { + nsecs_t expectedDequeueDuration = mDequeueTimeForgiveness + frame[FrameInfoIndex::Vsync] - + frame[FrameInfoIndex::IssueDrawCommandsStart]; if (expectedDequeueDuration > 0) { // Forgive only up to the expected amount, but not more than // the actual time spent blocked. - nsecs_t forgiveAmount = std::min(expectedDequeueDuration, - frame[FrameInfoIndex::DequeueBufferDuration]); + nsecs_t forgiveAmount = + std::min(expectedDequeueDuration, frame[FrameInfoIndex::DequeueBufferDuration]); LOG_ALWAYS_FATAL_IF(forgiveAmount >= totalDuration, - "Impossible dequeue duration! dequeue duration reported %" PRId64 - ", total duration %" PRId64, forgiveAmount, totalDuration); + "Impossible dequeue duration! dequeue duration reported %" PRId64 + ", total duration %" PRId64, + forgiveAmount, totalDuration); totalDuration -= forgiveAmount; } } @@ -148,13 +148,27 @@ void JankTracker::finishFrame(const FrameInfo& frame) { for (int i = 0; i < NUM_BUCKETS; i++) { int64_t delta = frame.duration(COMPARISONS[i].start, COMPARISONS[i].end); if (delta >= mThresholds[i] && delta < IGNORE_EXCEEDING) { - mData->reportJankType((JankType) i); - (*mGlobalData)->reportJankType((JankType) i); + mData->reportJankType((JankType)i); + (*mGlobalData)->reportJankType((JankType)i); + } + } + + // Log daveys since they are weird and we don't know what they are (b/70339576) + if (totalDuration >= 700_ms) { + static int sDaveyCount = 0; + std::stringstream ss; + ss << "Davey! duration=" << ns2ms(totalDuration) << "ms; "; + for (size_t i = 0; i < static_cast<size_t>(FrameInfoIndex::NumIndexes); i++) { + ss << FrameInfoNames[i] << "=" << frame[i] << ", "; } + ALOGI("%s", ss.str().c_str()); + // Just so we have something that counts up, the value is largely irrelevant + ATRACE_INT(ss.str().c_str(), ++sDaveyCount); } } -void JankTracker::dumpData(int fd, const ProfileDataDescription* description, const ProfileData* data) { +void JankTracker::dumpData(int fd, const ProfileDataDescription* description, + const ProfileData* data) { if (description) { switch (description->type) { case JankTrackerType::Generic: @@ -175,33 +189,30 @@ void JankTracker::dumpData(int fd, const ProfileDataDescription* description, co } void JankTracker::dumpFrames(int fd) { - FILE* file = fdopen(fd, "a"); - fprintf(file, "\n\n---PROFILEDATA---\n"); + dprintf(fd, "\n\n---PROFILEDATA---\n"); for (size_t i = 0; i < static_cast<size_t>(FrameInfoIndex::NumIndexes); i++) { - fprintf(file, "%s", FrameInfoNames[i].c_str()); - fprintf(file, ","); + dprintf(fd, "%s", FrameInfoNames[i].c_str()); + dprintf(fd, ","); } for (size_t i = 0; i < mFrames.size(); i++) { FrameInfo& frame = mFrames[i]; if (frame[FrameInfoIndex::SyncStart] == 0) { continue; } - fprintf(file, "\n"); + dprintf(fd, "\n"); for (int i = 0; i < static_cast<int>(FrameInfoIndex::NumIndexes); i++) { - fprintf(file, "%" PRId64 ",", frame[i]); + dprintf(fd, "%" PRId64 ",", frame[i]); } } - fprintf(file, "\n---PROFILEDATA---\n\n"); - fflush(file); + dprintf(fd, "\n---PROFILEDATA---\n\n"); } void JankTracker::reset() { mFrames.clear(); mData->reset(); (*mGlobalData)->reset(); - sFrameStart = Properties::filterOutTestOverhead - ? FrameInfoIndex::HandleInputStart - : FrameInfoIndex::IntendedVsync; + sFrameStart = Properties::filterOutTestOverhead ? FrameInfoIndex::HandleInputStart + : FrameInfoIndex::IntendedVsync; } } /* namespace uirenderer */ diff --git a/libs/hwui/JankTracker.h b/libs/hwui/JankTracker.h index e56c0791d3a4..dc6a7ff5ddb7 100644 --- a/libs/hwui/JankTracker.h +++ b/libs/hwui/JankTracker.h @@ -70,7 +70,8 @@ public: private: void setFrameInterval(nsecs_t frameIntervalNanos); - static void dumpData(int fd, const ProfileDataDescription* description, const ProfileData* data); + static void dumpData(int fd, const ProfileDataDescription* description, + const ProfileData* data); std::array<int64_t, NUM_BUCKETS> mThresholds; int64_t mFrameInterval; diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp index b58dfce787ef..86950d5ebb10 100644 --- a/libs/hwui/Layer.cpp +++ b/libs/hwui/Layer.cpp @@ -24,7 +24,7 @@ namespace android { namespace uirenderer { Layer::Layer(RenderState& renderState, Api api, SkColorFilter* colorFilter, int alpha, - SkBlendMode mode) + SkBlendMode mode) : GpuMemoryTracker(GpuObjectType::Layer) , mRenderState(renderState) , mApi(api) @@ -52,5 +52,5 @@ void Layer::postDecStrong() { mRenderState.postDecStrong(this); } -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h index e5520ea0e811..69213817f8af 100644 --- a/libs/hwui/Layer.h +++ b/libs/hwui/Layer.h @@ -16,11 +16,11 @@ #pragma once -#include <utils/RefBase.h> #include <GpuMemoryTracker.h> +#include <utils/RefBase.h> -#include <SkPaint.h> #include <SkBlendMode.h> +#include <SkPaint.h> #include "Matrix.h" @@ -43,9 +43,7 @@ public: Vulkan = 1, }; - Api getApi() const { - return mApi; - } + Api getApi() const { return mApi; } ~Layer(); @@ -59,44 +57,28 @@ public: virtual bool isBlend() const = 0; - inline void setForceFilter(bool forceFilter) { - this->forceFilter = forceFilter; - } + inline void setForceFilter(bool forceFilter) { this->forceFilter = forceFilter; } - inline bool getForceFilter() const { - return forceFilter; - } + inline bool getForceFilter() const { return forceFilter; } - inline void setAlpha(int alpha) { - this->alpha = alpha; - } + inline void setAlpha(int alpha) { this->alpha = alpha; } inline void setAlpha(int alpha, SkBlendMode mode) { this->alpha = alpha; this->mode = mode; } - inline int getAlpha() const { - return alpha; - } + inline int getAlpha() const { return alpha; } - inline SkBlendMode getMode() const { - return mode; - } + inline SkBlendMode getMode() const { return mode; } - inline SkColorFilter* getColorFilter() const { - return colorFilter; - } + inline SkColorFilter* getColorFilter() const { return colorFilter; } void setColorFilter(SkColorFilter* filter); - inline mat4& getTexTransform() { - return texTransform; - } + inline mat4& getTexTransform() { return texTransform; } - inline mat4& getTransform() { - return transform; - } + inline mat4& getTransform() { return transform; } /** * Posts a decStrong call to the appropriate thread. @@ -106,7 +88,7 @@ public: protected: Layer(RenderState& renderState, Api api, SkColorFilter* colorFilter, int alpha, - SkBlendMode mode); + SkBlendMode mode); RenderState& mRenderState; @@ -143,7 +125,7 @@ private: */ mat4 transform; -}; // struct Layer +}; // struct Layer -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/LayerBuilder.cpp b/libs/hwui/LayerBuilder.cpp index c5d5492d4fd1..15ede4ca148a 100644 --- a/libs/hwui/LayerBuilder.cpp +++ b/libs/hwui/LayerBuilder.cpp @@ -29,8 +29,7 @@ namespace uirenderer { class BatchBase { public: BatchBase(batchid_t batchId, BakedOpState* op, bool merging) - : mBatchId(batchId) - , mMerging(merging) { + : mBatchId(batchId), mMerging(merging) { mBounds = op->computedState.clippedBounds; mOps.push_back(op); } @@ -52,9 +51,10 @@ public: const std::vector<BakedOpState*>& getOps() const { return mOps; } void dump() const { - ALOGD(" Batch %p, id %d, merging %d, count %d, bounds " RECT_STRING, - this, mBatchId, mMerging, (int) mOps.size(), RECT_ARGS(mBounds)); + ALOGD(" Batch %p, id %d, merging %d, count %d, bounds " RECT_STRING, this, mBatchId, + mMerging, (int)mOps.size(), RECT_ARGS(mBounds)); } + protected: batchid_t mBatchId; Rect mBounds; @@ -64,9 +64,7 @@ protected: class OpBatch : public BatchBase { public: - OpBatch(batchid_t batchId, BakedOpState* op) - : BatchBase(batchId, op, false) { - } + OpBatch(batchid_t batchId, BakedOpState* op) : BatchBase(batchId, op, false) {} void batchOp(BakedOpState* op) { mBounds.unionWith(op->computedState.clippedBounds); @@ -77,16 +75,14 @@ public: class MergingOpBatch : public BatchBase { public: MergingOpBatch(batchid_t batchId, BakedOpState* op) - : BatchBase(batchId, op, true) - , mClipSideFlags(op->computedState.clipSideFlags) { - } + : BatchBase(batchId, op, true), mClipSideFlags(op->computedState.clipSideFlags) {} /* * Helper for determining if a new op can merge with a MergingDrawBatch based on their bounds * and clip side flags. Positive bounds delta means new bounds fit in old. */ static inline bool checkSide(const int currentFlags, const int newFlags, const int side, - float boundsDelta) { + float boundsDelta) { bool currentClipExists = currentFlags & side; bool newClipExists = newFlags & side; @@ -100,16 +96,14 @@ public: } static bool paintIsDefault(const SkPaint& paint) { - return paint.getAlpha() == 255 - && paint.getColorFilter() == nullptr - && paint.getShader() == nullptr; + return paint.getAlpha() == 255 && paint.getColorFilter() == nullptr && + paint.getShader() == nullptr; } static bool paintsAreEquivalent(const SkPaint& a, const SkPaint& b) { // Note: don't check color, since all currently mergeable ops can merge across colors - return a.getAlpha() == b.getAlpha() - && a.getColorFilter() == b.getColorFilter() - && a.getShader() == b.getShader(); + return a.getAlpha() == b.getAlpha() && a.getColorFilter() == b.getColorFilter() && + a.getShader() == b.getShader(); } /* @@ -123,8 +117,8 @@ public: * dropped, so we make simplifying qualifications on the ops that can merge, per op type. */ bool canMergeWith(BakedOpState* op) const { - bool isTextBatch = getBatchId() == OpBatchType::Text - || getBatchId() == OpBatchType::ColorText; + bool isTextBatch = + getBatchId() == OpBatchType::Text || getBatchId() == OpBatchType::ColorText; // Overlapping other operations is only allowed for text without shadow. For other ops, // multiDraw isn't guaranteed to overdraw correctly @@ -142,8 +136,9 @@ public: if (lhs->roundRectClipState != rhs->roundRectClipState) return false; // Local masks prevent merge, since they're potentially in different coordinate spaces - if (lhs->computedState.localProjectionPathMask - || rhs->computedState.localProjectionPathMask) return false; + if (lhs->computedState.localProjectionPathMask || + rhs->computedState.localProjectionPathMask) + return false; /* Clipping compatibility check * @@ -155,15 +150,18 @@ public: if (currentFlags != OpClipSideFlags::None || newFlags != OpClipSideFlags::None) { const Rect& opBounds = op->computedState.clippedBounds; float boundsDelta = mBounds.left - opBounds.left; - if (!checkSide(currentFlags, newFlags, OpClipSideFlags::Left, boundsDelta)) return false; + if (!checkSide(currentFlags, newFlags, OpClipSideFlags::Left, boundsDelta)) + return false; boundsDelta = mBounds.top - opBounds.top; if (!checkSide(currentFlags, newFlags, OpClipSideFlags::Top, boundsDelta)) return false; // right and bottom delta calculation reversed to account for direction boundsDelta = opBounds.right - mBounds.right; - if (!checkSide(currentFlags, newFlags, OpClipSideFlags::Right, boundsDelta)) return false; + if (!checkSide(currentFlags, newFlags, OpClipSideFlags::Right, boundsDelta)) + return false; boundsDelta = opBounds.bottom - mBounds.bottom; - if (!checkSide(currentFlags, newFlags, OpClipSideFlags::Bottom, boundsDelta)) return false; + if (!checkSide(currentFlags, newFlags, OpClipSideFlags::Bottom, boundsDelta)) + return false; } const SkPaint* newPaint = op->op->paint; @@ -197,8 +195,8 @@ private: int mClipSideFlags; }; -LayerBuilder::LayerBuilder(uint32_t width, uint32_t height, - const Rect& repaintRect, const BeginLayerOp* beginLayerOp, RenderNode* renderNode) +LayerBuilder::LayerBuilder(uint32_t width, uint32_t height, const Rect& repaintRect, + const BeginLayerOp* beginLayerOp, RenderNode* renderNode) : width(width) , height(height) , repaintRect(repaintRect) @@ -210,7 +208,7 @@ LayerBuilder::LayerBuilder(uint32_t width, uint32_t height, // iterate back toward target to see if anything drawn since should overlap the new op // if no target, merging ops still iterate to find similar batch to insert after void LayerBuilder::locateInsertIndex(int batchId, const Rect& clippedBounds, - BatchBase** targetBatch, size_t* insertBatchIndex) const { + BatchBase** targetBatch, size_t* insertBatchIndex) const { for (int i = mBatches.size() - 1; i >= 0; i--) { BatchBase* overBatch = mBatches[i]; @@ -219,7 +217,7 @@ void LayerBuilder::locateInsertIndex(int batchId, const Rect& clippedBounds, // TODO: also consider shader shared between batch types if (batchId == overBatch->getBatchId()) { *insertBatchIndex = i + 1; - if (!*targetBatch) break; // found insert position, quit + if (!*targetBatch) break; // found insert position, quit } if (overBatch->intersects(clippedBounds)) { @@ -242,10 +240,10 @@ void LayerBuilder::onDeferOp(LinearAllocator& allocator, const BakedOpState* bak // and issue them together in one draw. flushLayerClears(allocator); - if (CC_UNLIKELY(activeUnclippedSaveLayers.empty() - && bakedState->computedState.opaqueOverClippedBounds - && bakedState->computedState.clippedBounds.contains(repaintRect) - && !Properties::debugOverdraw)) { + if (CC_UNLIKELY(activeUnclippedSaveLayers.empty() && + bakedState->computedState.opaqueOverClippedBounds && + bakedState->computedState.clippedBounds.contains(repaintRect) && + !Properties::debugOverdraw)) { // discard all deferred drawing ops, since new one will occlude them clear(); } @@ -258,7 +256,7 @@ void LayerBuilder::flushLayerClears(LinearAllocator& allocator) { // put the verts in the frame allocator, since // 1) SimpleRectsOps needs verts, not rects // 2) even if mClearRects stored verts, std::vectors will move their contents - Vertex* const verts = (Vertex*) allocator.create_trivial_array<Vertex>(vertCount); + Vertex* const verts = (Vertex*)allocator.create_trivial_array<Vertex>(vertCount); Vertex* currentVert = verts; Rect bounds = mClearRects[0]; @@ -269,35 +267,34 @@ void LayerBuilder::flushLayerClears(LinearAllocator& allocator) { Vertex::set(currentVert++, rect.left, rect.bottom); Vertex::set(currentVert++, rect.right, rect.bottom); } - mClearRects.clear(); // discard rects before drawing so this method isn't reentrant + mClearRects.clear(); // discard rects before drawing so this method isn't reentrant // One or more unclipped saveLayers have been enqueued, with deferred clears. // Flush all of these clears with a single draw SkPaint* paint = allocator.create<SkPaint>(); paint->setBlendMode(SkBlendMode::kClear); - SimpleRectsOp* op = allocator.create_trivial<SimpleRectsOp>(bounds, - Matrix4::identity(), nullptr, paint, - verts, vertCount); - BakedOpState* bakedState = BakedOpState::directConstruct(allocator, - &repaintClip, bounds, *op); + SimpleRectsOp* op = allocator.create_trivial<SimpleRectsOp>( + bounds, Matrix4::identity(), nullptr, paint, verts, vertCount); + BakedOpState* bakedState = + BakedOpState::directConstruct(allocator, &repaintClip, bounds, *op); deferUnmergeableOp(allocator, bakedState, OpBatchType::Vertices); } } -void LayerBuilder::deferUnmergeableOp(LinearAllocator& allocator, - BakedOpState* op, batchid_t batchId) { +void LayerBuilder::deferUnmergeableOp(LinearAllocator& allocator, BakedOpState* op, + batchid_t batchId) { onDeferOp(allocator, op); OpBatch* targetBatch = mBatchLookup[batchId]; size_t insertBatchIndex = mBatches.size(); if (targetBatch) { - locateInsertIndex(batchId, op->computedState.clippedBounds, - (BatchBase**)(&targetBatch), &insertBatchIndex); + locateInsertIndex(batchId, op->computedState.clippedBounds, (BatchBase**)(&targetBatch), + &insertBatchIndex); } if (targetBatch) { targetBatch->batchOp(op); - } else { + } else { // new non-merging batch targetBatch = allocator.create<OpBatch>(batchId, op); mBatchLookup[batchId] = targetBatch; @@ -305,8 +302,8 @@ void LayerBuilder::deferUnmergeableOp(LinearAllocator& allocator, } } -void LayerBuilder::deferMergeableOp(LinearAllocator& allocator, - BakedOpState* op, batchid_t batchId, mergeid_t mergeId) { +void LayerBuilder::deferMergeableOp(LinearAllocator& allocator, BakedOpState* op, batchid_t batchId, + mergeid_t mergeId) { onDeferOp(allocator, op); MergingOpBatch* targetBatch = nullptr; @@ -320,12 +317,12 @@ void LayerBuilder::deferMergeableOp(LinearAllocator& allocator, } size_t insertBatchIndex = mBatches.size(); - locateInsertIndex(batchId, op->computedState.clippedBounds, - (BatchBase**)(&targetBatch), &insertBatchIndex); + locateInsertIndex(batchId, op->computedState.clippedBounds, (BatchBase**)(&targetBatch), + &insertBatchIndex); if (targetBatch) { targetBatch->mergeOp(op); - } else { + } else { // new merging batch targetBatch = allocator.create<MergingOpBatch>(batchId, op); mMergingBatchLookup[batchId].insert(std::make_pair(mergeId, targetBatch)); @@ -334,11 +331,11 @@ void LayerBuilder::deferMergeableOp(LinearAllocator& allocator, } } -void LayerBuilder::replayBakedOpsImpl(void* arg, - BakedOpReceiver* unmergedReceivers, MergedOpReceiver* mergedReceivers) const { +void LayerBuilder::replayBakedOpsImpl(void* arg, BakedOpReceiver* unmergedReceivers, + MergedOpReceiver* mergedReceivers) const { if (renderNode) { - ATRACE_FORMAT_BEGIN("Issue HW Layer DisplayList %s %ux%u", - renderNode->getName(), width, height); + ATRACE_FORMAT_BEGIN("Issue HW Layer DisplayList %s %ux%u", renderNode->getName(), width, + height); } else { ATRACE_BEGIN("flush drawing commands"); } @@ -348,12 +345,9 @@ void LayerBuilder::replayBakedOpsImpl(void* arg, if (size > 1 && batch->isMerging()) { int opId = batch->getOps()[0]->op->opId; const MergingOpBatch* mergingBatch = static_cast<const MergingOpBatch*>(batch); - MergedBakedOpList data = { - batch->getOps().data(), - size, - mergingBatch->getClipSideFlags(), - mergingBatch->getClipRect() - }; + MergedBakedOpList data = {batch->getOps().data(), size, + mergingBatch->getClipSideFlags(), + mergingBatch->getClipRect()}; mergedReceivers[opId](arg, data); } else { for (const BakedOpState* op : batch->getOps()) { @@ -373,13 +367,12 @@ void LayerBuilder::clear() { } void LayerBuilder::dump() const { - ALOGD("LayerBuilder %p, %ux%u buffer %p, blo %p, rn %p (%s)", - this, width, height, offscreenBuffer, beginLayerOp, - renderNode, renderNode ? renderNode->getName() : "-"); + ALOGD("LayerBuilder %p, %ux%u buffer %p, blo %p, rn %p (%s)", this, width, height, + offscreenBuffer, beginLayerOp, renderNode, renderNode ? renderNode->getName() : "-"); for (const BatchBase* batch : mBatches) { batch->dump(); } } -} // namespace uirenderer -} // namespace android +} // namespace uirenderer +} // namespace android diff --git a/libs/hwui/LayerBuilder.h b/libs/hwui/LayerBuilder.h index 4de432c5e7be..c799d48f7821 100644 --- a/libs/hwui/LayerBuilder.h +++ b/libs/hwui/LayerBuilder.h @@ -20,8 +20,8 @@ #include "Rect.h" #include "utils/Macros.h" -#include <vector> #include <unordered_map> +#include <vector> struct SkRect; @@ -42,22 +42,22 @@ typedef int batchid_t; typedef const void* mergeid_t; namespace OpBatchType { - enum { - Bitmap, - MergedPatch, - AlphaVertices, - Vertices, - AlphaMaskTexture, - Text, - ColorText, - Shadow, - TextureLayer, - Functor, - CopyToLayer, - CopyFromLayer, - - Count // must be last - }; +enum { + Bitmap, + MergedPatch, + AlphaVertices, + Vertices, + AlphaMaskTexture, + Text, + ColorText, + Shadow, + TextureLayer, + Functor, + CopyToLayer, + CopyFromLayer, + + Count // must be last +}; } typedef void (*BakedOpReceiver)(void*, const BakedOpState&); @@ -68,37 +68,36 @@ typedef void (*MergedOpReceiver)(void*, const MergedBakedOpList& opList); * for a single FBO/layer. */ class LayerBuilder { -// Prevent copy/assign because users may stash pointer to offscreenBuffer and viewportClip -PREVENT_COPY_AND_ASSIGN(LayerBuilder); + // Prevent copy/assign because users may stash pointer to offscreenBuffer and viewportClip + PREVENT_COPY_AND_ASSIGN(LayerBuilder); + public: // Create LayerBuilder for Fbo0 LayerBuilder(uint32_t width, uint32_t height, const Rect& repaintRect) - : LayerBuilder(width, height, repaintRect, nullptr, nullptr) {}; + : LayerBuilder(width, height, repaintRect, nullptr, nullptr){}; // Create LayerBuilder for an offscreen layer, where beginLayerOp is present for a // saveLayer, renderNode is present for a HW layer. - LayerBuilder(uint32_t width, uint32_t height, - const Rect& repaintRect, const BeginLayerOp* beginLayerOp, RenderNode* renderNode); + LayerBuilder(uint32_t width, uint32_t height, const Rect& repaintRect, + const BeginLayerOp* beginLayerOp, RenderNode* renderNode); // iterate back toward target to see if anything drawn since should overlap the new op // if no target, merging ops still iterate to find similar batch to insert after - void locateInsertIndex(int batchId, const Rect& clippedBounds, - BatchBase** targetBatch, size_t* insertBatchIndex) const; + void locateInsertIndex(int batchId, const Rect& clippedBounds, BatchBase** targetBatch, + size_t* insertBatchIndex) const; void deferUnmergeableOp(LinearAllocator& allocator, BakedOpState* op, batchid_t batchId); // insertion point of a new batch, will hopefully be immediately after similar batch // (generally, should be similar shader) - void deferMergeableOp(LinearAllocator& allocator, - BakedOpState* op, batchid_t batchId, mergeid_t mergeId); + void deferMergeableOp(LinearAllocator& allocator, BakedOpState* op, batchid_t batchId, + mergeid_t mergeId); void replayBakedOpsImpl(void* arg, BakedOpReceiver* receivers, MergedOpReceiver*) const; void deferLayerClear(const Rect& dstRect); - bool empty() const { - return mBatches.empty(); - } + bool empty() const { return mBatches.empty(); } void clear(); @@ -114,6 +113,7 @@ public: // list of deferred CopyFromLayer ops, to be deferred upon encountering EndUnclippedLayerOps std::vector<BakedOpState*> activeUnclippedSaveLayers; + private: void onDeferOp(LinearAllocator& allocator, const BakedOpState* bakedState); void flushLayerClears(LinearAllocator& allocator); @@ -128,10 +128,10 @@ private: std::unordered_map<mergeid_t, MergingOpBatch*> mMergingBatchLookup[OpBatchType::Count]; // Maps batch ids to the most recent *non-merging* batch of that id - OpBatch* mBatchLookup[OpBatchType::Count] = { nullptr }; + OpBatch* mBatchLookup[OpBatchType::Count] = {nullptr}; std::vector<Rect> mClearRects; }; -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/LayerUpdateQueue.cpp b/libs/hwui/LayerUpdateQueue.cpp index 95f5cfb33474..fd5c66101cd3 100644 --- a/libs/hwui/LayerUpdateQueue.cpp +++ b/libs/hwui/LayerUpdateQueue.cpp @@ -39,5 +39,5 @@ void LayerUpdateQueue::enqueueLayerWithDamage(RenderNode* renderNode, Rect damag } } -} // namespace uirenderer -} // namespace android +} // namespace uirenderer +} // namespace android diff --git a/libs/hwui/LayerUpdateQueue.h b/libs/hwui/LayerUpdateQueue.h index 38f3596d70df..b14b80cc598a 100644 --- a/libs/hwui/LayerUpdateQueue.h +++ b/libs/hwui/LayerUpdateQueue.h @@ -17,12 +17,12 @@ #ifndef ANDROID_HWUI_LAYER_UPDATE_QUEUE_H #define ANDROID_HWUI_LAYER_UPDATE_QUEUE_H +#include <utils/StrongPointer.h> #include "Rect.h" #include "utils/Macros.h" -#include <utils/StrongPointer.h> -#include <vector> #include <unordered_map> +#include <vector> namespace android { namespace uirenderer { @@ -31,11 +31,11 @@ class RenderNode; class LayerUpdateQueue { PREVENT_COPY_AND_ASSIGN(LayerUpdateQueue); + public: struct Entry { Entry(RenderNode* renderNode, const Rect& damage) - : renderNode(renderNode) - , damage(damage) {} + : renderNode(renderNode), damage(damage) {} sp<RenderNode> renderNode; Rect damage; }; @@ -44,11 +44,12 @@ public: void enqueueLayerWithDamage(RenderNode* renderNode, Rect dirty); void clear(); const std::vector<Entry>& entries() const { return mEntries; } + private: std::vector<Entry> mEntries; }; -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android -#endif // ANDROID_HWUI_LAYER_UPDATE_QUEUE_H +#endif // ANDROID_HWUI_LAYER_UPDATE_QUEUE_H diff --git a/libs/hwui/Matrix.cpp b/libs/hwui/Matrix.cpp index a936661f86f9..d84ed321a4cb 100644 --- a/libs/hwui/Matrix.cpp +++ b/libs/hwui/Matrix.cpp @@ -43,24 +43,24 @@ const Matrix4& Matrix4::identity() { } void Matrix4::loadIdentity() { - data[kScaleX] = 1.0f; - data[kSkewY] = 0.0f; - data[2] = 0.0f; + data[kScaleX] = 1.0f; + data[kSkewY] = 0.0f; + data[2] = 0.0f; data[kPerspective0] = 0.0f; - data[kSkewX] = 0.0f; - data[kScaleY] = 1.0f; - data[6] = 0.0f; + data[kSkewX] = 0.0f; + data[kScaleY] = 1.0f; + data[6] = 0.0f; data[kPerspective1] = 0.0f; - data[8] = 0.0f; - data[9] = 0.0f; - data[kScaleZ] = 1.0f; - data[11] = 0.0f; + data[8] = 0.0f; + data[9] = 0.0f; + data[kScaleZ] = 1.0f; + data[11] = 0.0f; - data[kTranslateX] = 0.0f; - data[kTranslateY] = 0.0f; - data[kTranslateZ] = 0.0f; + data[kTranslateX] = 0.0f; + data[kTranslateY] = 0.0f; + data[kTranslateZ] = 0.0f; data[kPerspective2] = 1.0f; mType = kTypeIdentity | kTypeRectToRect; @@ -75,7 +75,7 @@ uint8_t Matrix4::getType() const { mType = kTypeIdentity; if (data[kPerspective0] != 0.0f || data[kPerspective1] != 0.0f || - data[kPerspective2] != 1.0f) { + data[kPerspective2] != 1.0f) { mType |= kTypePerspective; } @@ -105,7 +105,7 @@ uint8_t Matrix4::getType() const { // it doesn't preserve rectangles. if (!(mType & kTypePerspective)) { if ((isZero(m00) && isZero(m11) && !isZero(m01) && !isZero(m10)) || - (isZero(m01) && isZero(m10) && !isZero(m00) && !isZero(m11))) { + (isZero(m01) && isZero(m10) && !isZero(m00) && !isZero(m11))) { mType |= kTypeRectToRect; } } @@ -155,17 +155,17 @@ void Matrix4::load(const float* v) { void Matrix4::load(const SkMatrix& v) { memset(data, 0, sizeof(data)); - data[kScaleX] = v[SkMatrix::kMScaleX]; - data[kSkewX] = v[SkMatrix::kMSkewX]; + data[kScaleX] = v[SkMatrix::kMScaleX]; + data[kSkewX] = v[SkMatrix::kMSkewX]; data[kTranslateX] = v[SkMatrix::kMTransX]; - data[kSkewY] = v[SkMatrix::kMSkewY]; - data[kScaleY] = v[SkMatrix::kMScaleY]; + data[kSkewY] = v[SkMatrix::kMSkewY]; + data[kScaleY] = v[SkMatrix::kMScaleY]; data[kTranslateY] = v[SkMatrix::kMTransY]; - data[kPerspective0] = v[SkMatrix::kMPersp0]; - data[kPerspective1] = v[SkMatrix::kMPersp1]; - data[kPerspective2] = v[SkMatrix::kMPersp2]; + data[kPerspective0] = v[SkMatrix::kMPersp0]; + data[kPerspective1] = v[SkMatrix::kMPersp1]; + data[kPerspective2] = v[SkMatrix::kMPersp2]; data[kScaleZ] = 1.0f; @@ -183,10 +183,10 @@ void Matrix4::copyTo(SkMatrix& v) const { v.reset(); v.set(SkMatrix::kMScaleX, data[kScaleX]); - v.set(SkMatrix::kMSkewX, data[kSkewX]); + v.set(SkMatrix::kMSkewX, data[kSkewX]); v.set(SkMatrix::kMTransX, data[kTranslateX]); - v.set(SkMatrix::kMSkewY, data[kSkewY]); + v.set(SkMatrix::kMSkewY, data[kSkewY]); v.set(SkMatrix::kMScaleY, data[kScaleY]); v.set(SkMatrix::kMTransY, data[kTranslateY]); @@ -201,13 +201,13 @@ void Matrix4::loadInverse(const Matrix4& v) { // Reset the matrix // Unnamed fields are never written to except by // loadIdentity(), they don't need to be reset - data[kScaleX] = 1.0f; - data[kSkewX] = 0.0f; + data[kScaleX] = 1.0f; + data[kSkewX] = 0.0f; - data[kScaleY] = 1.0f; - data[kSkewY] = 0.0f; + data[kScaleY] = 1.0f; + data[kSkewY] = 0.0f; - data[kScaleZ] = 1.0f; + data[kScaleZ] = 1.0f; data[kPerspective0] = 0.0f; data[kPerspective1] = 0.0f; @@ -215,43 +215,48 @@ void Matrix4::loadInverse(const Matrix4& v) { // No need to deal with kTranslateZ because isPureTranslate() // only returns true when the kTranslateZ component is 0 - data[kTranslateX] = -v.data[kTranslateX]; - data[kTranslateY] = -v.data[kTranslateY]; - data[kTranslateZ] = 0.0f; + data[kTranslateX] = -v.data[kTranslateX]; + data[kTranslateY] = -v.data[kTranslateY]; + data[kTranslateZ] = 0.0f; // A "pure translate" matrix can be identity or translation mType = v.getType(); return; } - double scale = 1.0 / - (v.data[kScaleX] * ((double) v.data[kScaleY] * v.data[kPerspective2] - - (double) v.data[kTranslateY] * v.data[kPerspective1]) + - v.data[kSkewX] * ((double) v.data[kTranslateY] * v.data[kPerspective0] - - (double) v.data[kSkewY] * v.data[kPerspective2]) + - v.data[kTranslateX] * ((double) v.data[kSkewY] * v.data[kPerspective1] - - (double) v.data[kScaleY] * v.data[kPerspective0])); + double scale = 1.0 / (v.data[kScaleX] * ((double)v.data[kScaleY] * v.data[kPerspective2] - + (double)v.data[kTranslateY] * v.data[kPerspective1]) + + v.data[kSkewX] * ((double)v.data[kTranslateY] * v.data[kPerspective0] - + (double)v.data[kSkewY] * v.data[kPerspective2]) + + v.data[kTranslateX] * ((double)v.data[kSkewY] * v.data[kPerspective1] - + (double)v.data[kScaleY] * v.data[kPerspective0])); data[kScaleX] = (v.data[kScaleY] * v.data[kPerspective2] - - v.data[kTranslateY] * v.data[kPerspective1]) * scale; - data[kSkewX] = (v.data[kTranslateX] * v.data[kPerspective1] - - v.data[kSkewX] * v.data[kPerspective2]) * scale; - data[kTranslateX] = (v.data[kSkewX] * v.data[kTranslateY] - - v.data[kTranslateX] * v.data[kScaleY]) * scale; - - data[kSkewY] = (v.data[kTranslateY] * v.data[kPerspective0] - - v.data[kSkewY] * v.data[kPerspective2]) * scale; + v.data[kTranslateY] * v.data[kPerspective1]) * + scale; + data[kSkewX] = + (v.data[kTranslateX] * v.data[kPerspective1] - v.data[kSkewX] * v.data[kPerspective2]) * + scale; + data[kTranslateX] = + (v.data[kSkewX] * v.data[kTranslateY] - v.data[kTranslateX] * v.data[kScaleY]) * scale; + + data[kSkewY] = + (v.data[kTranslateY] * v.data[kPerspective0] - v.data[kSkewY] * v.data[kPerspective2]) * + scale; data[kScaleY] = (v.data[kScaleX] * v.data[kPerspective2] - - v.data[kTranslateX] * v.data[kPerspective0]) * scale; - data[kTranslateY] = (v.data[kTranslateX] * v.data[kSkewY] - - v.data[kScaleX] * v.data[kTranslateY]) * scale; - - data[kPerspective0] = (v.data[kSkewY] * v.data[kPerspective1] - - v.data[kScaleY] * v.data[kPerspective0]) * scale; - data[kPerspective1] = (v.data[kSkewX] * v.data[kPerspective0] - - v.data[kScaleX] * v.data[kPerspective1]) * scale; - data[kPerspective2] = (v.data[kScaleX] * v.data[kScaleY] - - v.data[kSkewX] * v.data[kSkewY]) * scale; + v.data[kTranslateX] * v.data[kPerspective0]) * + scale; + data[kTranslateY] = + (v.data[kTranslateX] * v.data[kSkewY] - v.data[kScaleX] * v.data[kTranslateY]) * scale; + + data[kPerspective0] = + (v.data[kSkewY] * v.data[kPerspective1] - v.data[kScaleY] * v.data[kPerspective0]) * + scale; + data[kPerspective1] = + (v.data[kSkewX] * v.data[kPerspective0] - v.data[kScaleX] * v.data[kPerspective1]) * + scale; + data[kPerspective2] = + (v.data[kScaleX] * v.data[kScaleY] - v.data[kSkewX] * v.data[kSkewY]) * scale; mType = kTypeUnknown; } @@ -298,13 +303,13 @@ void Matrix4::loadScale(float sx, float sy, float sz) { void Matrix4::loadSkew(float sx, float sy) { loadIdentity(); - data[kScaleX] = 1.0f; - data[kSkewX] = sx; - data[kTranslateX] = 0.0f; + data[kScaleX] = 1.0f; + data[kSkewX] = sx; + data[kTranslateX] = 0.0f; - data[kSkewY] = sy; - data[kScaleY] = 1.0f; - data[kTranslateY] = 0.0f; + data[kSkewY] = sy; + data[kScaleY] = 1.0f; + data[kTranslateY] = 0.0f; data[kPerspective0] = 0.0f; data[kPerspective1] = 0.0f; @@ -320,23 +325,23 @@ void Matrix4::loadRotate(float angle) { loadIdentity(); - data[kScaleX] = c; - data[kSkewX] = -s; + data[kScaleX] = c; + data[kSkewX] = -s; - data[kSkewY] = s; - data[kScaleY] = c; + data[kSkewY] = s; + data[kScaleY] = c; mType = kTypeUnknown; } void Matrix4::loadRotate(float angle, float x, float y, float z) { - data[kPerspective0] = 0.0f; - data[kPerspective1] = 0.0f; - data[11] = 0.0f; - data[kTranslateX] = 0.0f; - data[kTranslateY] = 0.0f; - data[kTranslateZ] = 0.0f; - data[kPerspective2] = 1.0f; + data[kPerspective0] = 0.0f; + data[kPerspective1] = 0.0f; + data[11] = 0.0f; + data[kTranslateX] = 0.0f; + data[kTranslateY] = 0.0f; + data[kTranslateZ] = 0.0f; + data[kPerspective2] = 1.0f; angle *= float(M_PI / 180.0f); float c = cosf(angle); @@ -356,27 +361,27 @@ void Matrix4::loadRotate(float angle, float x, float y, float z) { const float ys = y * s; const float zs = z * s; - data[kScaleX] = x * x * nc + c; - data[kSkewX] = xy * nc - zs; - data[8] = zx * nc + ys; - data[kSkewY] = xy * nc + zs; - data[kScaleY] = y * y * nc + c; - data[9] = yz * nc - xs; - data[2] = zx * nc - ys; - data[6] = yz * nc + xs; - data[kScaleZ] = z * z * nc + c; + data[kScaleX] = x * x * nc + c; + data[kSkewX] = xy * nc - zs; + data[8] = zx * nc + ys; + data[kSkewY] = xy * nc + zs; + data[kScaleY] = y * y * nc + c; + data[9] = yz * nc - xs; + data[2] = zx * nc - ys; + data[6] = yz * nc + xs; + data[kScaleZ] = z * z * nc + c; mType = kTypeUnknown; } void Matrix4::loadMultiply(const Matrix4& u, const Matrix4& v) { - for (int i = 0 ; i < 4 ; i++) { + for (int i = 0; i < 4; i++) { float x = 0; float y = 0; float z = 0; float w = 0; - for (int j = 0 ; j < 4 ; j++) { + for (int j = 0; j < 4; j++) { const float e = v.get(i, j); x += u.get(j, 0) * e; y += u.get(j, 1) * e; @@ -412,7 +417,7 @@ float Matrix4::mapZ(const Vector3& orig) const { } void Matrix4::mapPoint3d(Vector3& vec) const { - //TODO: optimize simple case + // TODO: optimize simple case const Vector3 orig(vec); vec.x = orig.x * data[kScaleX] + orig.y * data[kSkewX] + orig.z * data[8] + data[kTranslateX]; vec.y = orig.x * data[kSkewY] + orig.y * data[kScaleY] + orig.z * data[9] + data[kTranslateY]; @@ -469,16 +474,11 @@ void Matrix4::mapRect(Rect& r) const { return; } - float vertices[] = { - r.left, r.top, - r.right, r.top, - r.right, r.bottom, - r.left, r.bottom - }; + float vertices[] = {r.left, r.top, r.right, r.top, r.right, r.bottom, r.left, r.bottom}; float x, y, z; - for (int i = 0; i < 8; i+= 2) { + for (int i = 0; i < 8; i += 2) { float px = vertices[i]; float py = vertices[i + 1]; @@ -498,10 +498,14 @@ void Matrix4::mapRect(Rect& r) const { x = vertices[i]; y = vertices[i + 1]; - if (x < r.left) r.left = x; - else if (x > r.right) r.right = x; - if (y < r.top) r.top = y; - else if (y > r.bottom) r.bottom = y; + if (x < r.left) + r.left = x; + else if (x > r.right) + r.right = x; + if (y < r.top) + r.top = y; + else if (y > r.bottom) + r.bottom = y; } } @@ -522,5 +526,5 @@ void Matrix4::dump(const char* label) const { ALOGD("]"); } -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/Matrix.h b/libs/hwui/Matrix.h index ba9cbbe02ec1..f0a3a959617d 100644 --- a/libs/hwui/Matrix.h +++ b/libs/hwui/Matrix.h @@ -18,27 +18,26 @@ #include "Rect.h" +#include <SkMatrix.h> #include <cutils/compiler.h> #include <iomanip> #include <ostream> -#include <SkMatrix.h> namespace android { namespace uirenderer { #define SK_MATRIX_STRING "[%.2f %.2f %.2f] [%.2f %.2f %.2f] [%.2f %.2f %.2f]" -#define SK_MATRIX_ARGS(m) \ - (m)->get(0), (m)->get(1), (m)->get(2), \ - (m)->get(3), (m)->get(4), (m)->get(5), \ - (m)->get(6), (m)->get(7), (m)->get(8) +#define SK_MATRIX_ARGS(m) \ + (m)->get(0), (m)->get(1), (m)->get(2), (m)->get(3), (m)->get(4), (m)->get(5), (m)->get(6), \ + (m)->get(7), (m)->get(8) -#define MATRIX_4_STRING "[%.2f %.2f %.2f %.2f] [%.2f %.2f %.2f %.2f]" \ +#define MATRIX_4_STRING \ + "[%.2f %.2f %.2f %.2f] [%.2f %.2f %.2f %.2f]" \ " [%.2f %.2f %.2f %.2f] [%.2f %.2f %.2f %.2f]" -#define MATRIX_4_ARGS(m) \ - (m)->data[0], (m)->data[4], (m)->data[8], (m)->data[12], \ - (m)->data[1], (m)->data[5], (m)->data[9], (m)->data[13], \ - (m)->data[2], (m)->data[6], (m)->data[10], (m)->data[14], \ - (m)->data[3], (m)->data[7], (m)->data[11], (m)->data[15] \ +#define MATRIX_4_ARGS(m) \ + (m)->data[0], (m)->data[4], (m)->data[8], (m)->data[12], (m)->data[1], (m)->data[5], \ + (m)->data[9], (m)->data[13], (m)->data[2], (m)->data[6], (m)->data[10], (m)->data[14], \ + (m)->data[3], (m)->data[7], (m)->data[11], (m)->data[15] /////////////////////////////////////////////////////////////////////////////// // Classes @@ -77,21 +76,15 @@ public: static const int sGeometryMask = 0xf; - Matrix4() { - loadIdentity(); - } + Matrix4() { loadIdentity(); } - explicit Matrix4(const float* v) { - load(v); - } + explicit Matrix4(const float* v) { load(v); } Matrix4(const SkMatrix& v) { // NOLINT, implicit load(v); } - float operator[](int index) const { - return data[index]; - } + float operator[](int index) const { return data[index]; } float& operator[](int index) { mType = kTypeUnknown; @@ -107,9 +100,7 @@ public: return !memcmp(&a.data[0], &b.data[0], 16 * sizeof(float)); } - friend bool operator!=(const Matrix4& a, const Matrix4& b) { - return !(a == b); - } + friend bool operator!=(const Matrix4& a, const Matrix4& b) { return !(a == b); } void loadIdentity(); @@ -126,9 +117,7 @@ public: void loadMultiply(const Matrix4& u, const Matrix4& v); void loadOrtho(float left, float right, float bottom, float top, float near, float far); - void loadOrtho(int width, int height) { - loadOrtho(0, width, height, 0, -1, 1); - } + void loadOrtho(int width, int height) { loadOrtho(0, width, height, 0, -1, 1); } uint8_t getType() const; @@ -208,8 +197,8 @@ public: float mapZ(const Vector3& orig) const; void mapPoint3d(Vector3& vec) const; - void mapPoint(float& x, float& y) const; // 2d only - void mapRect(Rect& r) const; // 2d only + void mapPoint(float& x, float& y) const; // 2d only + void mapRect(Rect& r) const; // 2d only float getTranslateX() const; float getTranslateY() const; @@ -241,17 +230,13 @@ public: private: mutable uint8_t mType; - inline float get(int i, int j) const { - return data[i * 4 + j]; - } + inline float get(int i, int j) const { return data[i * 4 + j]; } - inline void set(int i, int j, float v) { - data[i * 4 + j] = v; - } + inline void set(int i, int j, float v) { data[i * 4 + j] = v; } uint8_t getGeometryType() const; -}; // class Matrix4 +}; // class Matrix4 /////////////////////////////////////////////////////////////////////////////// // Types @@ -259,6 +244,5 @@ private: typedef Matrix4 mat4; -}; // namespace uirenderer -}; // namespace android - +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/NinePatchUtils.h b/libs/hwui/NinePatchUtils.h index e989a4680a60..db9509fff378 100644 --- a/libs/hwui/NinePatchUtils.h +++ b/libs/hwui/NinePatchUtils.h @@ -20,7 +20,7 @@ namespace android { namespace NinePatchUtils { static inline void SetLatticeDivs(SkCanvas::Lattice* lattice, const Res_png_9patch& chunk, - int width, int height) { + int width, int height) { lattice->fXCount = chunk.numXDivs; lattice->fYCount = chunk.numYDivs; lattice->fXDivs = chunk.getXDivs(); @@ -53,10 +53,13 @@ static inline int NumDistinctRects(const SkCanvas::Lattice& lattice) { return xRects * yRects; } -static inline void SetLatticeFlags(SkCanvas::Lattice* lattice, SkCanvas::Lattice::Flags* flags, - int numFlags, const Res_png_9patch& chunk) { - lattice->fFlags = flags; - sk_bzero(flags, numFlags * sizeof(SkCanvas::Lattice::Flags)); +static inline void SetLatticeFlags(SkCanvas::Lattice* lattice, + SkCanvas::Lattice::RectType* flags, int numFlags, const Res_png_9patch& chunk, + SkColor* colors) { + lattice->fRectTypes = flags; + lattice->fColors = colors; + sk_bzero(flags, numFlags * sizeof(SkCanvas::Lattice::RectType)); + sk_bzero(colors, numFlags * sizeof(SkColor)); bool needPadRow = lattice->fYCount > 0 && 0 == lattice->fYDivs[0]; bool needPadCol = lattice->fXCount > 0 && 0 == lattice->fXDivs[0]; @@ -65,6 +68,7 @@ static inline void SetLatticeFlags(SkCanvas::Lattice* lattice, SkCanvas::Lattice if (needPadRow) { // Skip flags for the degenerate first row of rects. flags += lattice->fXCount + 1; + colors += lattice->fXCount + 1; yCount--; } @@ -75,22 +79,30 @@ static inline void SetLatticeFlags(SkCanvas::Lattice* lattice, SkCanvas::Lattice if (0 == x && needPadCol) { // First rect of each column is degenerate, skip the flag. flags++; + colors++; continue; } - if (0 == chunk.getColors()[i++]) { - *flags = SkCanvas::Lattice::kTransparent_Flags; + uint32_t currentColor = chunk.getColors()[i++]; + if (Res_png_9patch::TRANSPARENT_COLOR == currentColor) { + *flags = SkCanvas::Lattice::kTransparent; + setFlags = true; + } else if (Res_png_9patch::NO_COLOR != currentColor) { + *flags = SkCanvas::Lattice::kFixedColor; + *colors = currentColor; setFlags = true; } flags++; + colors++; } } if (!setFlags) { - lattice->fFlags = nullptr; + lattice->fRectTypes = nullptr; + lattice->fColors = nullptr; } } -}; // namespace NinePatchUtils -}; // namespace android +}; // namespace NinePatchUtils +}; // namespace android diff --git a/libs/hwui/OpDumper.cpp b/libs/hwui/OpDumper.cpp index f4b7ee0fe430..5d2ccc77e82f 100644 --- a/libs/hwui/OpDumper.cpp +++ b/libs/hwui/OpDumper.cpp @@ -34,14 +34,13 @@ void OpDumper::dump(const RecordedOp& op, std::ostream& output, int level) { op.localMatrix.mapRect(localBounds); output << sOpNameLut[op.opId] << " " << localBounds; - if (op.localClip - && (!op.localClip->rect.contains(localBounds) || op.localClip->intersectWithRoot)) { - output << std::fixed << std::setprecision(0) - << " clip=" << op.localClip->rect - << " mode=" << (int)op.localClip->mode; + if (op.localClip && + (!op.localClip->rect.contains(localBounds) || op.localClip->intersectWithRoot)) { + output << std::fixed << std::setprecision(0) << " clip=" << op.localClip->rect + << " mode=" << (int)op.localClip->mode; if (op.localClip->intersectWithRoot) { - output << " iwr"; + output << " iwr"; } } } @@ -50,5 +49,5 @@ const char* OpDumper::opName(const RecordedOp& op) { return sOpNameLut[op.opId]; } -} // namespace uirenderer -} // namespace android +} // namespace uirenderer +} // namespace android diff --git a/libs/hwui/OpDumper.h b/libs/hwui/OpDumper.h index a82289c7cca2..edbe381fcc72 100644 --- a/libs/hwui/OpDumper.h +++ b/libs/hwui/OpDumper.h @@ -29,5 +29,5 @@ public: static const char* opName(const RecordedOp& op); }; -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/OpenGLReadback.cpp b/libs/hwui/OpenGLReadback.cpp index 2687410897ac..11432d629650 100644 --- a/libs/hwui/OpenGLReadback.cpp +++ b/libs/hwui/OpenGLReadback.cpp @@ -17,30 +17,29 @@ #include "OpenGLReadback.h" #include "Caches.h" -#include "Image.h" -#include "GlopBuilder.h" #include "GlLayer.h" +#include "GlopBuilder.h" +#include "Image.h" #include "renderstate/RenderState.h" #include "renderthread/EglManager.h" #include "utils/GLUtils.h" #include <GLES2/gl2.h> +#include <gui/Surface.h> #include <ui/Fence.h> #include <ui/GraphicBuffer.h> -#include <gui/Surface.h> namespace android { namespace uirenderer { CopyResult OpenGLReadback::copySurfaceInto(Surface& surface, const Rect& srcRect, - SkBitmap* bitmap) { + SkBitmap* bitmap) { ATRACE_CALL(); // Setup the source sp<GraphicBuffer> sourceBuffer; sp<Fence> sourceFence; Matrix4 texTransform; - status_t err = surface.getLastQueuedBuffer(&sourceBuffer, &sourceFence, - texTransform.data); + status_t err = surface.getLastQueuedBuffer(&sourceBuffer, &sourceFence, texTransform.data); texTransform.invalidateType(); if (err != NO_ERROR) { ALOGW("Failed to get last queued buffer, error = %d", err); @@ -64,7 +63,8 @@ CopyResult OpenGLReadback::copySurfaceInto(Surface& surface, const Rect& srcRect } CopyResult OpenGLReadback::copyGraphicBufferInto(GraphicBuffer* graphicBuffer, - Matrix4& texTransform, const Rect& srcRect, SkBitmap* bitmap) { + Matrix4& texTransform, const Rect& srcRect, + SkBitmap* bitmap) { mRenderThread.eglManager().initialize(); // TODO: Can't use Image helper since it forces GL_TEXTURE_2D usage via // GL_OES_EGL_image, which doesn't work since we need samplerExternalOES @@ -72,11 +72,11 @@ CopyResult OpenGLReadback::copyGraphicBufferInto(GraphicBuffer* graphicBuffer, // Create the EGLImage object that maps the GraphicBuffer EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); - EGLClientBuffer clientBuffer = (EGLClientBuffer) graphicBuffer->getNativeBuffer(); - EGLint attrs[] = { EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE }; + EGLClientBuffer clientBuffer = (EGLClientBuffer)graphicBuffer->getNativeBuffer(); + EGLint attrs[] = {EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE}; - EGLImageKHR sourceImage = eglCreateImageKHR(display, EGL_NO_CONTEXT, - EGL_NATIVE_BUFFER_ANDROID, clientBuffer, attrs); + EGLImageKHR sourceImage = eglCreateImageKHR(display, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, + clientBuffer, attrs); if (sourceImage == EGL_NO_IMAGE_KHR) { ALOGW("eglCreateImageKHR failed (%#x)", eglGetError()); @@ -85,8 +85,8 @@ CopyResult OpenGLReadback::copyGraphicBufferInto(GraphicBuffer* graphicBuffer, uint32_t width = graphicBuffer->getWidth(); uint32_t height = graphicBuffer->getHeight(); - CopyResult copyResult = copyImageInto(sourceImage, texTransform, width, height, - srcRect, bitmap); + CopyResult copyResult = + copyImageInto(sourceImage, texTransform, width, height, srcRect, bitmap); // All we're flushing & finishing is the deletion of the texture since // copyImageInto already did a major flush & finish as an implicit @@ -105,10 +105,7 @@ CopyResult OpenGLReadback::copyGraphicBufferInto(GraphicBuffer* graphicBuffer, S } static float sFlipVInit[16] = { - 1, 0, 0, 0, - 0, -1, 0, 0, - 0, 0, 1, 0, - 0, 1, 0, 1, + 1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, }; static const Matrix4 sFlipV(sFlipVInit); @@ -116,19 +113,19 @@ static const Matrix4 sFlipV(sFlipVInit); //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// -inline CopyResult copyTextureInto(Caches& caches, RenderState& renderState, - Texture& sourceTexture, const Matrix4& texTransform, const Rect& srcRect, - SkBitmap* bitmap) { +inline CopyResult copyTextureInto(Caches& caches, RenderState& renderState, Texture& sourceTexture, + const Matrix4& texTransform, const Rect& srcRect, + SkBitmap* bitmap) { int destWidth = bitmap->width(); int destHeight = bitmap->height(); - if (destWidth > caches.maxTextureSize - || destHeight > caches.maxTextureSize) { - ALOGW("Can't copy surface into bitmap, %dx%d exceeds max texture size %d", - destWidth, destHeight, caches.maxTextureSize); + if (destWidth > caches.maxTextureSize || destHeight > caches.maxTextureSize) { + ALOGW("Can't copy surface into bitmap, %dx%d exceeds max texture size %d", destWidth, + destHeight, caches.maxTextureSize); return CopyResult::DestinationInvalid; } - if (bitmap->colorType() == kRGBA_F16_SkColorType && !caches.extensions().hasFloatTextures()) { + if (bitmap->colorType() == kRGBA_F16_SkColorType && + !caches.extensions().hasRenderableFloatTextures()) { ALOGW("Can't copy surface into bitmap, RGBA_F16 config is not supported"); return CopyResult::DestinationInvalid; } @@ -188,10 +185,8 @@ inline CopyResult copyTextureInto(Caches& caches, RenderState& renderState, glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, destWidth, destHeight, - 0, format, type, nullptr); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, - GL_TEXTURE_2D, texture, 0); + glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, destWidth, destHeight, 0, format, type, nullptr); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0); { bool requiresFilter; @@ -208,15 +203,15 @@ inline CopyResult copyTextureInto(Caches& caches, RenderState& renderState, // GLES coordinates. croppedTexTransform.multiply(sFlipV); croppedTexTransform.translate(srcRect.left / sourceTexture.width(), - srcRect.top / sourceTexture.height(), 0); + srcRect.top / sourceTexture.height(), 0); croppedTexTransform.scale(srcRect.getWidth() / sourceTexture.width(), - srcRect.getHeight() / sourceTexture.height(), 1); + srcRect.getHeight() / sourceTexture.height(), 1); croppedTexTransform.multiply(sFlipV); - requiresFilter = srcRect.getWidth() != (float) destWidth - || srcRect.getHeight() != (float) destHeight; + requiresFilter = srcRect.getWidth() != (float)destWidth || + srcRect.getHeight() != (float)destHeight; } else { - requiresFilter = sourceTexture.width() != (uint32_t) destWidth - || sourceTexture.height() != (uint32_t) destHeight; + requiresFilter = sourceTexture.width() != (uint32_t)destWidth || + sourceTexture.height() != (uint32_t)destHeight; } Glop glop; GlopBuilder(renderState, caches, &glop) @@ -231,8 +226,7 @@ inline CopyResult copyTextureInto(Caches& caches, RenderState& renderState, renderState.render(glop, ortho, false); // TODO: We should convert to linear space when the target is RGBA16F - glReadPixels(0, 0, bitmap->width(), bitmap->height(), format, - type, bitmap->getPixels()); + glReadPixels(0, 0, bitmap->width(), bitmap->height(), format, type, bitmap->getPixels()); bitmap->notifyPixelsChanged(); } @@ -245,10 +239,9 @@ inline CopyResult copyTextureInto(Caches& caches, RenderState& renderState, return CopyResult::Success; } -CopyResult OpenGLReadbackImpl::copyImageInto(EGLImageKHR eglImage, - const Matrix4& imgTransform, int imgWidth, int imgHeight, const Rect& srcRect, - SkBitmap* bitmap) { - +CopyResult OpenGLReadbackImpl::copyImageInto(EGLImageKHR eglImage, const Matrix4& imgTransform, + int imgWidth, int imgHeight, const Rect& srcRect, + SkBitmap* bitmap) { // If this is a 90 or 270 degree rotation we need to swap width/height // This is a fuzzy way of checking that. if (imgTransform[Matrix4::kSkewX] >= 0.5f || imgTransform[Matrix4::kSkewX] <= -0.5f) { @@ -270,26 +263,25 @@ CopyResult OpenGLReadbackImpl::copyImageInto(EGLImageKHR eglImage, Texture sourceTexture(caches); sourceTexture.wrap(sourceTexId, imgWidth, imgHeight, 0, 0 /* total lie */, - GL_TEXTURE_EXTERNAL_OES); + GL_TEXTURE_EXTERNAL_OES); - CopyResult copyResult = copyTextureInto(caches, mRenderThread.renderState(), - sourceTexture, imgTransform, srcRect, bitmap); + CopyResult copyResult = copyTextureInto(caches, mRenderThread.renderState(), sourceTexture, + imgTransform, srcRect, bitmap); sourceTexture.deleteTexture(); return copyResult; } -bool OpenGLReadbackImpl::copyLayerInto(renderthread::RenderThread& renderThread, - GlLayer& layer, SkBitmap* bitmap) { +bool OpenGLReadbackImpl::copyLayerInto(renderthread::RenderThread& renderThread, GlLayer& layer, + SkBitmap* bitmap) { if (!layer.isRenderable()) { // layer has never been updated by DeferredLayerUpdater, abort copy return false; } - return CopyResult::Success == copyTextureInto(Caches::getInstance(), - renderThread.renderState(), layer.getTexture(), layer.getTexTransform(), - Rect(), bitmap); + return CopyResult::Success == copyTextureInto(Caches::getInstance(), renderThread.renderState(), + layer.getTexture(), layer.getTexTransform(), + Rect(), bitmap); } - -} // namespace uirenderer -} // namespace android +} // namespace uirenderer +} // namespace android diff --git a/libs/hwui/OpenGLReadback.h b/libs/hwui/OpenGLReadback.h index 403f2e34d515..ca40738b4901 100644 --- a/libs/hwui/OpenGLReadback.h +++ b/libs/hwui/OpenGLReadback.h @@ -30,19 +30,21 @@ class GlLayer; class OpenGLReadback : public Readback { public: virtual CopyResult copySurfaceInto(Surface& surface, const Rect& srcRect, - SkBitmap* bitmap) override; + SkBitmap* bitmap) override; virtual CopyResult copyGraphicBufferInto(GraphicBuffer* graphicBuffer, - SkBitmap* bitmap) override; + SkBitmap* bitmap) override; protected: explicit OpenGLReadback(renderthread::RenderThread& thread) : Readback(thread) {} virtual ~OpenGLReadback() {} virtual CopyResult copyImageInto(EGLImageKHR eglImage, const Matrix4& imgTransform, - int imgWidth, int imgHeight, const Rect& srcRect, SkBitmap* bitmap) = 0; + int imgWidth, int imgHeight, const Rect& srcRect, + SkBitmap* bitmap) = 0; + private: CopyResult copyGraphicBufferInto(GraphicBuffer* graphicBuffer, Matrix4& texTransform, - const Rect& srcRect, SkBitmap* bitmap); + const Rect& srcRect, SkBitmap* bitmap); }; class OpenGLReadbackImpl : public OpenGLReadback { @@ -53,12 +55,13 @@ public: * Copies the layer's contents into the provided bitmap. */ static bool copyLayerInto(renderthread::RenderThread& renderThread, GlLayer& layer, - SkBitmap* bitmap); + SkBitmap* bitmap); protected: virtual CopyResult copyImageInto(EGLImageKHR eglImage, const Matrix4& imgTransform, - int imgWidth, int imgHeight, const Rect& srcRect, SkBitmap* bitmap) override; + int imgWidth, int imgHeight, const Rect& srcRect, + SkBitmap* bitmap) override; }; -} // namespace uirenderer -} // namespace android +} // namespace uirenderer +} // namespace android diff --git a/libs/hwui/Outline.h b/libs/hwui/Outline.h index 922ff7caecb8..4ddacc8a922d 100644 --- a/libs/hwui/Outline.h +++ b/libs/hwui/Outline.h @@ -26,27 +26,14 @@ namespace uirenderer { class Outline { public: - enum class Type { - None = 0, - Empty = 1, - ConvexPath = 2, - RoundRect = 3 - }; - - Outline() - : mShouldClip(false) - , mType(Type::None) - , mRadius(0) - , mAlpha(0.0f) {} + enum class Type { None = 0, Empty = 1, ConvexPath = 2, RoundRect = 3 }; + + Outline() : mShouldClip(false), mType(Type::None), mRadius(0), mAlpha(0.0f) {} void setRoundRect(int left, int top, int right, int bottom, float radius, float alpha) { mAlpha = alpha; - if (mType == Type::RoundRect - && left == mBounds.left - && right == mBounds.right - && top == mBounds.top - && bottom == mBounds.bottom - && radius == mRadius) { + if (mType == Type::RoundRect && left == mBounds.left && right == mBounds.right && + top == mBounds.top && bottom == mBounds.bottom && radius == mRadius) { // nothing to change, don't do any work return; } @@ -55,11 +42,17 @@ public: mBounds.set(left, top, right, bottom); mRadius = radius; + + // Reuse memory if previous outline was the same shape (rect or round rect). + if ( mPath.countVerbs() > 10) { + mPath.reset(); + } else { + mPath.rewind(); + } + // update mPath to reflect new outline - mPath.reset(); if (MathUtils::isPositive(radius)) { - mPath.addRoundRect(SkRect::MakeLTRB(left, top, right, bottom), - radius, radius); + mPath.addRoundRect(SkRect::MakeLTRB(left, top, right, bottom), radius, radius); } else { mPath.addRect(left, top, right, bottom); } @@ -88,21 +81,13 @@ public: mAlpha = 0.0f; } - bool isEmpty() const { - return mType == Type::Empty; - } + bool isEmpty() const { return mType == Type::Empty; } - float getAlpha() const { - return mAlpha; - } + float getAlpha() const { return mAlpha; } - void setShouldClip(bool clip) { - mShouldClip = clip; - } + void setShouldClip(bool clip) { mShouldClip = clip; } - bool getShouldClip() const { - return mShouldClip; - } + bool getShouldClip() const { return mShouldClip; } bool willClip() const { // only round rect outlines can be used for clipping @@ -129,17 +114,11 @@ public: return &mPath; } - Type getType() const { - return mType; - } + Type getType() const { return mType; } - const Rect& getBounds() const { - return mBounds; - } + const Rect& getBounds() const { return mBounds; } - float getRadius() const { - return mRadius; - } + float getRadius() const { return mRadius; } private: bool mShouldClip; diff --git a/libs/hwui/Patch.cpp b/libs/hwui/Patch.cpp index b471e7850a99..c243dad01f04 100644 --- a/libs/hwui/Patch.cpp +++ b/libs/hwui/Patch.cpp @@ -21,8 +21,8 @@ #include "UvMapper.h" #include "utils/MathUtils.h" -#include <algorithm> #include <utils/Log.h> +#include <algorithm> namespace android { namespace uirenderer { @@ -35,10 +35,9 @@ uint32_t Patch::getSize() const { return verticesCount * sizeof(TextureVertex); } -Patch::Patch(const float bitmapWidth, const float bitmapHeight, - float width, float height, const UvMapper& mapper, const Res_png_9patch* patch) +Patch::Patch(const float bitmapWidth, const float bitmapHeight, float width, float height, + const UvMapper& mapper, const Res_png_9patch* patch) : mColors(patch->getColors()) { - int8_t emptyQuads = 0; const int8_t numColors = patch->numColors; if (uint8_t(numColors) < sizeof(uint32_t) * 4) { @@ -121,8 +120,8 @@ Patch::Patch(const float bitmapWidth, const float bitmapHeight, v1 += vOffset / bitmapHeight; if (stepY > 0.0f) { - generateRow(xDivs, xCount, vertex, y1, y2, v1, v2, stretchX, rescaleX, - width, bitmapWidth, quadCount); + generateRow(xDivs, xCount, vertex, y1, y2, v1, v2, stretchX, rescaleX, width, + bitmapWidth, quadCount); } y1 = y2; @@ -133,8 +132,8 @@ Patch::Patch(const float bitmapWidth, const float bitmapHeight, if (previousStepY != bitmapHeight) { y2 = height; - generateRow(xDivs, xCount, vertex, y1, y2, v1, 1.0f, stretchX, rescaleX, - width, bitmapWidth, quadCount); + generateRow(xDivs, xCount, vertex, y1, y2, v1, 1.0f, stretchX, rescaleX, width, bitmapWidth, + quadCount); } if (verticesCount != maxVertices) { @@ -144,9 +143,9 @@ Patch::Patch(const float bitmapWidth, const float bitmapHeight, } } -void Patch::generateRow(const int32_t* xDivs, uint32_t xCount, TextureVertex*& vertex, - float y1, float y2, float v1, float v2, float stretchX, float rescaleX, - float width, float bitmapWidth, uint32_t& quadCount) { +void Patch::generateRow(const int32_t* xDivs, uint32_t xCount, TextureVertex*& vertex, float y1, + float y2, float v1, float v2, float stretchX, float rescaleX, float width, + float bitmapWidth, uint32_t& quadCount) { float previousStepX = 0.0f; float x1 = 0.0f; @@ -184,8 +183,8 @@ void Patch::generateRow(const int32_t* xDivs, uint32_t xCount, TextureVertex*& v } } -void Patch::generateQuad(TextureVertex*& vertex, float x1, float y1, float x2, float y2, - float u1, float v1, float u2, float v2, uint32_t& quadCount) { +void Patch::generateQuad(TextureVertex*& vertex, float x1, float y1, float x2, float y2, float u1, + float v1, float u2, float v2, uint32_t& quadCount) { const uint32_t oldQuadCount = quadCount; quadCount++; @@ -226,5 +225,5 @@ void Patch::generateQuad(TextureVertex*& vertex, float x1, float y1, float x2, f #endif } -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/Patch.h b/libs/hwui/Patch.h index f04416ccabf9..a659ed227bec 100644 --- a/libs/hwui/Patch.h +++ b/libs/hwui/Patch.h @@ -39,9 +39,8 @@ struct TextureVertex; class Patch { public: - Patch(const float bitmapWidth, const float bitmapHeight, - float width, float height, - const UvMapper& mapper, const Res_png_9patch* patch); + Patch(const float bitmapWidth, const float bitmapHeight, float width, float height, + const UvMapper& mapper, const Res_png_9patch* patch); /** * Returns the size of this patch's mesh in bytes. @@ -58,17 +57,17 @@ public: GLintptr textureOffset = 0; private: - void generateRow(const int32_t* xDivs, uint32_t xCount, TextureVertex*& vertex, - float y1, float y2, float v1, float v2, float stretchX, float rescaleX, - float width, float bitmapWidth, uint32_t& quadCount); - void generateQuad(TextureVertex*& vertex, float x1, float y1, float x2, float y2, - float u1, float v1, float u2, float v2, uint32_t& quadCount); + void generateRow(const int32_t* xDivs, uint32_t xCount, TextureVertex*& vertex, float y1, + float y2, float v1, float v2, float stretchX, float rescaleX, float width, + float bitmapWidth, uint32_t& quadCount); + void generateQuad(TextureVertex*& vertex, float x1, float y1, float x2, float y2, float u1, + float v1, float u2, float v2, uint32_t& quadCount); const uint32_t* mColors; UvMapper mUvMapper; -}; // struct Patch +}; // struct Patch -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android -#endif // ANDROID_HWUI_PATCH_H +#endif // ANDROID_HWUI_PATCH_H diff --git a/libs/hwui/PatchCache.cpp b/libs/hwui/PatchCache.cpp index 78c7eb9ad809..673d73c5475d 100644 --- a/libs/hwui/PatchCache.cpp +++ b/libs/hwui/PatchCache.cpp @@ -56,7 +56,7 @@ hash_t PatchCache::PatchDescription::hash() const { } int PatchCache::PatchDescription::compare(const PatchCache::PatchDescription& lhs, - const PatchCache::PatchDescription& rhs) { + const PatchCache::PatchDescription& rhs) { return memcmp(&lhs, &rhs, sizeof(PatchDescription)); } @@ -115,7 +115,7 @@ void PatchCache::removeDeferred(Res_png_9patch* patch) { void PatchCache::clearGarbage() { Vector<patch_pair_t> patchesToRemove; - { // scope for the mutex + { // scope for the mutex Mutex::Autolock _l(mLock); size_t count = mGarbage.size(); for (size_t i = 0; i < count; i++) { @@ -123,7 +123,7 @@ void PatchCache::clearGarbage() { remove(patchesToRemove, patch); // A Res_png_9patch is actually an array of byte that's larger // than sizeof(Res_png_9patch). It must be freed as an array. - delete[] (int8_t*) patch; + delete[](int8_t*) patch; } mGarbage.clear(); } @@ -153,8 +153,8 @@ void PatchCache::clearGarbage() { } void PatchCache::createVertexBuffer() { - mRenderState.meshState().genOrUpdateMeshBuffer(&mMeshBuffer, - mMaxSize, nullptr, GL_DYNAMIC_DRAW); + mRenderState.meshState().genOrUpdateMeshBuffer(&mMeshBuffer, mMaxSize, nullptr, + GL_DYNAMIC_DRAW); mSize = 0; mFreeBlocks = new BufferBlock(0, mMaxSize); } @@ -198,11 +198,11 @@ void PatchCache::setupMesh(Patch* newMesh) { } // Copy the 9patch mesh in the VBO - newMesh->positionOffset = (GLintptr) (block->offset); + newMesh->positionOffset = (GLintptr)(block->offset); newMesh->textureOffset = newMesh->positionOffset + kMeshTextureOffset; mRenderState.meshState().updateMeshBufferSubData(mMeshBuffer, newMesh->positionOffset, size, - newMesh->vertices.get()); + newMesh->vertices.get()); // Remove the block since we've used it entirely if (block->size == size) { @@ -223,15 +223,15 @@ void PatchCache::setupMesh(Patch* newMesh) { static const UvMapper sIdentity; -const Patch* PatchCache::get( const uint32_t bitmapWidth, const uint32_t bitmapHeight, - const float pixelWidth, const float pixelHeight, const Res_png_9patch* patch) { - +const Patch* PatchCache::get(const uint32_t bitmapWidth, const uint32_t bitmapHeight, + const float pixelWidth, const float pixelHeight, + const Res_png_9patch* patch) { const PatchDescription description(bitmapWidth, bitmapHeight, pixelWidth, pixelHeight, patch); const Patch* mesh = mCache.get(description); if (!mesh) { - Patch* newMesh = new Patch(bitmapWidth, bitmapHeight, - pixelWidth, pixelHeight, sIdentity, patch); + Patch* newMesh = + new Patch(bitmapWidth, bitmapHeight, pixelWidth, pixelHeight, sIdentity, patch); if (newMesh->vertices) { setupMesh(newMesh); @@ -260,5 +260,5 @@ void PatchCache::dumpFreeBlocks(const char* prefix) { } #endif -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/PatchCache.h b/libs/hwui/PatchCache.h index aa746c7dfa15..273c3f56f2cd 100644 --- a/libs/hwui/PatchCache.h +++ b/libs/hwui/PatchCache.h @@ -36,9 +36,9 @@ class Patch; // Debug #if DEBUG_PATCHES - #define PATCH_LOGD(...) ALOGD(__VA_ARGS__) +#define PATCH_LOGD(...) ALOGD(__VA_ARGS__) #else - #define PATCH_LOGD(...) +#define PATCH_LOGD(...) #endif /////////////////////////////////////////////////////////////////////////////// @@ -54,20 +54,14 @@ public: ~PatchCache(); const Patch* get(const uint32_t bitmapWidth, const uint32_t bitmapHeight, - const float pixelWidth, const float pixelHeight, const Res_png_9patch* patch); + const float pixelWidth, const float pixelHeight, const Res_png_9patch* patch); void clear(); - uint32_t getSize() const { - return mSize; - } + uint32_t getSize() const { return mSize; } - uint32_t getMaxSize() const { - return mMaxSize; - } + uint32_t getMaxSize() const { return mMaxSize; } - GLuint getMeshBuffer() const { - return mMeshBuffer; - } + GLuint getMeshBuffer() const { return mMeshBuffer; } /** * Removes the entries associated with the specified 9-patch. This is meant @@ -81,18 +75,23 @@ public: */ void clearGarbage(); - private: struct PatchDescription { - PatchDescription(): mPatch(nullptr), mBitmapWidth(0), mBitmapHeight(0), - mPixelWidth(0), mPixelHeight(0) { - } + PatchDescription() + : mPatch(nullptr) + , mBitmapWidth(0) + , mBitmapHeight(0) + , mPixelWidth(0) + , mPixelHeight(0) {} PatchDescription(const uint32_t bitmapWidth, const uint32_t bitmapHeight, - const float pixelWidth, const float pixelHeight, const Res_png_9patch* patch): - mPatch(patch), mBitmapWidth(bitmapWidth), mBitmapHeight(bitmapHeight), - mPixelWidth(pixelWidth), mPixelHeight(pixelHeight) { - } + const float pixelWidth, const float pixelHeight, + const Res_png_9patch* patch) + : mPatch(patch) + , mBitmapWidth(bitmapWidth) + , mBitmapHeight(bitmapHeight) + , mPixelWidth(pixelWidth) + , mPixelHeight(pixelHeight) {} hash_t hash() const; @@ -100,27 +99,20 @@ private: static int compare(const PatchDescription& lhs, const PatchDescription& rhs); - bool operator==(const PatchDescription& other) const { - return compare(*this, other) == 0; - } + bool operator==(const PatchDescription& other) const { return compare(*this, other) == 0; } - bool operator!=(const PatchDescription& other) const { - return compare(*this, other) != 0; - } + bool operator!=(const PatchDescription& other) const { return compare(*this, other) != 0; } friend inline int strictly_order_type(const PatchDescription& lhs, - const PatchDescription& rhs) { + const PatchDescription& rhs) { return PatchDescription::compare(lhs, rhs) < 0; } - friend inline int compare_type(const PatchDescription& lhs, - const PatchDescription& rhs) { + friend inline int compare_type(const PatchDescription& lhs, const PatchDescription& rhs) { return PatchDescription::compare(lhs, rhs); } - friend inline hash_t hash_type(const PatchDescription& entry) { - return entry.hash(); - } + friend inline hash_t hash_type(const PatchDescription& entry) { return entry.hash(); } private: const Res_png_9patch* mPatch; @@ -129,7 +121,7 @@ private: float mPixelWidth; float mPixelHeight; - }; // struct PatchDescription + }; // struct PatchDescription /** * A buffer block represents an empty range in the mesh buffer @@ -139,14 +131,13 @@ private: * to track available regions of memory in the VBO. */ struct BufferBlock { - BufferBlock(uint32_t offset, uint32_t size): offset(offset), size(size), next(nullptr) { - } + BufferBlock(uint32_t offset, uint32_t size) : offset(offset), size(size), next(nullptr) {} uint32_t offset; uint32_t size; BufferBlock* next; - }; // struct BufferBlock + }; // struct BufferBlock typedef Pair<const PatchDescription*, Patch*> patch_pair_t; @@ -174,7 +165,7 @@ private: // Garbage tracking, required to handle GC events on the VM side Vector<Res_png_9patch*> mGarbage; mutable Mutex mLock; -}; // class PatchCache +}; // class PatchCache -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/PathCache.cpp b/libs/hwui/PathCache.cpp index 8d4ae1b6622a..e67c5bddd354 100644 --- a/libs/hwui/PathCache.cpp +++ b/libs/hwui/PathCache.cpp @@ -46,13 +46,14 @@ static bool compareWidthHeight(const T& lhs, const T& rhs) { } static bool compareRoundRects(const PathDescription::Shape::RoundRect& lhs, - const PathDescription::Shape::RoundRect& rhs) { + const PathDescription::Shape::RoundRect& rhs) { return compareWidthHeight(lhs, rhs) && lhs.mRx == rhs.mRx && lhs.mRy == rhs.mRy; } -static bool compareArcs(const PathDescription::Shape::Arc& lhs, const PathDescription::Shape::Arc& rhs) { +static bool compareArcs(const PathDescription::Shape::Arc& lhs, + const PathDescription::Shape::Arc& rhs) { return compareWidthHeight(lhs, rhs) && lhs.mStartAngle == rhs.mStartAngle && - lhs.mSweepAngle == rhs.mSweepAngle && lhs.mUseCenter == rhs.mUseCenter; + lhs.mSweepAngle == rhs.mSweepAngle && lhs.mUseCenter == rhs.mUseCenter; } /////////////////////////////////////////////////////////////////////////////// @@ -91,7 +92,7 @@ hash_t PathDescription::hash() const { hash = JenkinsHashMix(hash, android::hash_type(miter)); hash = JenkinsHashMix(hash, android::hash_type(strokeWidth)); hash = JenkinsHashMix(hash, android::hash_type(pathEffect)); - hash = JenkinsHashMixBytes(hash, (uint8_t*) &shape, sizeof(Shape)); + hash = JenkinsHashMixBytes(hash, (uint8_t*)&shape, sizeof(Shape)); return JenkinsHashWhiten(hash); } @@ -126,7 +127,7 @@ bool PathDescription::operator==(const PathDescription& rhs) const { /////////////////////////////////////////////////////////////////////////////// static void computePathBounds(const SkPath* path, const SkPaint* paint, PathTexture* texture, - uint32_t& width, uint32_t& height) { + uint32_t& width, uint32_t& height) { const SkRect& bounds = path->getBounds(); const float pathWidth = std::max(bounds.width(), 1.0f); const float pathHeight = std::max(bounds.height(), 1.0f); @@ -134,7 +135,7 @@ static void computePathBounds(const SkPath* path, const SkPaint* paint, PathText texture->left = floorf(bounds.fLeft); texture->top = floorf(bounds.fTop); - texture->offset = (int) floorf(std::max(paint->getStrokeWidth(), 1.0f) * 1.5f + 0.5f); + texture->offset = (int)floorf(std::max(paint->getStrokeWidth(), 1.0f) * 1.5f + 0.5f); width = uint32_t(pathWidth + texture->offset * 2.0 + 0.5); height = uint32_t(pathHeight + texture->offset * 2.0 + 0.5); @@ -152,12 +153,12 @@ static void initPaint(SkPaint& paint) { } static sk_sp<Bitmap> drawPath(const SkPath* path, const SkPaint* paint, PathTexture* texture, - uint32_t maxTextureSize) { + uint32_t maxTextureSize) { uint32_t width, height; computePathBounds(path, paint, texture, width, height); if (width > maxTextureSize || height > maxTextureSize) { - ALOGW("Shape too large to be rendered into a texture (%dx%d, max=%dx%d)", - width, height, maxTextureSize, maxTextureSize); + ALOGW("Shape too large to be rendered into a texture (%dx%d, max=%dx%d)", width, height, + maxTextureSize, maxTextureSize); return nullptr; } @@ -230,13 +231,13 @@ void PathCache::removeTexture(PathTexture* texture) { // to the cache and the size wasn't increased if (size > mSize) { ALOGE("Removing path texture of size %d will leave " - "the cache in an inconsistent state", size); + "the cache in an inconsistent state", + size); } mSize -= size; } - PATH_LOGD("PathCache::delete name, size, mSize = %d, %d, %d", - texture->id, size, mSize); + PATH_LOGD("PathCache::delete name, size, mSize = %d, %d, %d", texture->id, size, mSize); if (mDebugEnabled) { ALOGD("Shape deleted, size = %d", size); } @@ -258,14 +259,16 @@ void PathCache::purgeCache(uint32_t width, uint32_t height) { void PathCache::trim() { while (mSize > mMaxSize || mCache.size() > PATH_CACHE_COUNT_LIMIT) { - LOG_ALWAYS_FATAL_IF(!mCache.size(), "Inconsistent mSize! Ran out of items to remove!" - " mSize = %u, mMaxSize = %u", mSize, mMaxSize); + LOG_ALWAYS_FATAL_IF(!mCache.size(), + "Inconsistent mSize! Ran out of items to remove!" + " mSize = %u, mMaxSize = %u", + mSize, mMaxSize); mCache.removeOldest(); } } -PathTexture* PathCache::addTexture(const PathDescription& entry, const SkPath *path, - const SkPaint* paint) { +PathTexture* PathCache::addTexture(const PathDescription& entry, const SkPath* path, + const SkPaint* paint) { ATRACE_NAME("Generate Path Texture"); PathTexture* texture = new PathTexture(Caches::getInstance(), path->getGenerationID()); @@ -280,8 +283,8 @@ PathTexture* PathCache::addTexture(const PathDescription& entry, const SkPath *p return texture; } -void PathCache::generateTexture(const PathDescription& entry, Bitmap& bitmap, - PathTexture* texture, bool addToCache) { +void PathCache::generateTexture(const PathDescription& entry, Bitmap& bitmap, PathTexture* texture, + bool addToCache) { generateTexture(bitmap, texture); // Note here that we upload to a texture even if it's bigger than mMaxSize. @@ -289,8 +292,7 @@ void PathCache::generateTexture(const PathDescription& entry, Bitmap& bitmap, // immediately on trim, or on any other Path entering the cache. uint32_t size = texture->width() * texture->height(); mSize += size; - PATH_LOGD("PathCache::get/create: name, size, mSize = %d, %d, %d", - texture->id, size, mSize); + PATH_LOGD("PathCache::get/create: name, size, mSize = %d, %d, %d", texture->id, size, mSize); if (mDebugEnabled) { ALOGD("Shape created, size = %d", size); } @@ -313,9 +315,8 @@ void PathCache::generateTexture(Bitmap& bitmap, Texture* texture) { // Path precaching /////////////////////////////////////////////////////////////////////////////// -PathCache::PathProcessor::PathProcessor(Caches& caches): - TaskProcessor<sk_sp<Bitmap> >(&caches.tasks), mMaxTextureSize(caches.maxTextureSize) { -} +PathCache::PathProcessor::PathProcessor(Caches& caches) + : TaskProcessor<sk_sp<Bitmap> >(&caches.tasks), mMaxTextureSize(caches.maxTextureSize) {} void PathCache::PathProcessor::onProcess(const sp<Task<sk_sp<Bitmap> > >& task) { PathTask* t = static_cast<PathTask*>(task.get()); @@ -336,7 +337,7 @@ void PathCache::removeDeferred(const SkPath* path) { void PathCache::clearGarbage() { Vector<PathDescription> pathsToRemove; - { // scope for the mutex + { // scope for the mutex Mutex::Autolock l(mLock); for (const uint32_t generationID : mGarbage) { LruCache<PathDescription, PathTexture*>::Iterator iter(mCache); @@ -433,8 +434,8 @@ void PathCache::precache(const SkPath* path, const SkPaint* paint) { // Rounded rects /////////////////////////////////////////////////////////////////////////////// -PathTexture* PathCache::getRoundRect(float width, float height, - float rx, float ry, const SkPaint* paint) { +PathTexture* PathCache::getRoundRect(float width, float height, float rx, float ry, + const SkPaint* paint) { PathDescription entry(ShapeType::RoundRect, paint); entry.shape.roundRect.mWidth = width; entry.shape.roundRect.mHeight = height; @@ -525,8 +526,8 @@ PathTexture* PathCache::getRect(float width, float height, const SkPaint* paint) // Arcs /////////////////////////////////////////////////////////////////////////////// -PathTexture* PathCache::getArc(float width, float height, - float startAngle, float sweepAngle, bool useCenter, const SkPaint* paint) { +PathTexture* PathCache::getArc(float width, float height, float startAngle, float sweepAngle, + bool useCenter, const SkPaint* paint) { PathDescription entry(ShapeType::Arc, paint); entry.shape.arc.mWidth = width; entry.shape.arc.mHeight = height; @@ -554,5 +555,5 @@ PathTexture* PathCache::getArc(float width, float height, return texture; } -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/PathCache.h b/libs/hwui/PathCache.h index 7bd190df951b..28c8bbb8ca6e 100644 --- a/libs/hwui/PathCache.h +++ b/libs/hwui/PathCache.h @@ -47,9 +47,9 @@ class Caches; // Debug #if DEBUG_PATHS - #define PATH_LOGD(...) ALOGD(__VA_ARGS__) +#define PATH_LOGD(...) ALOGD(__VA_ARGS__) #else - #define PATH_LOGD(...) +#define PATH_LOGD(...) #endif /////////////////////////////////////////////////////////////////////////////// @@ -57,11 +57,10 @@ class Caches; /////////////////////////////////////////////////////////////////////////////// struct PathTexture; -class PathTask: public Task<sk_sp<Bitmap>> { +class PathTask : public Task<sk_sp<Bitmap>> { public: - PathTask(const SkPath* path, const SkPaint* paint, PathTexture* texture): - path(*path), paint(*paint), texture(texture) { - } + PathTask(const SkPath* path, const SkPaint* paint, PathTexture* texture) + : path(*path), paint(*paint), texture(texture) {} // copied, since input path not guaranteed to survive for duration of task const SkPath path; @@ -74,15 +73,10 @@ public: /** * Alpha texture used to represent a path. */ -struct PathTexture: public Texture { - PathTexture(Caches& caches, int generation) - : Texture(caches) { - this->generation = generation; - } +struct PathTexture : public Texture { + PathTexture(Caches& caches, int generation) : Texture(caches) { this->generation = generation; } - ~PathTexture() { - clearTask(); - } + ~PathTexture() { clearTask(); } /** * Left coordinate of the path bounds. @@ -97,13 +91,9 @@ struct PathTexture: public Texture { */ float offset = 0; - sp<PathTask> task() const { - return mTask; - } + sp<PathTask> task() const { return mTask; } - void setTask(const sp<PathTask>& task) { - mTask = task; - } + void setTask(const sp<PathTask>& task) { mTask = task; } void clearTask() { if (mTask != nullptr) { @@ -113,17 +103,9 @@ struct PathTexture: public Texture { private: sp<PathTask> mTask; -}; // struct PathTexture - -enum class ShapeType { - None, - Rect, - RoundRect, - Circle, - Oval, - Arc, - Path -}; +}; // struct PathTexture + +enum class ShapeType { None, Rect, RoundRect, Circle, Oval, Arc, Path }; struct PathDescription { HASHABLE_TYPE(PathDescription); @@ -173,7 +155,7 @@ struct PathDescription { * Any texture added to the cache causing the cache to grow beyond the maximum * allowed size will also cause the oldest texture to be kicked out. */ -class PathCache: public OnEntryRemoved<PathDescription, PathTexture*> { +class PathCache : public OnEntryRemoved<PathDescription, PathTexture*> { public: PathCache(); ~PathCache(); @@ -203,9 +185,9 @@ public: PathTexture* getOval(float width, float height, const SkPaint* paint); PathTexture* getRect(float width, float height, const SkPaint* paint); PathTexture* getArc(float width, float height, float startAngle, float sweepAngle, - bool useCenter, const SkPaint* paint); + bool useCenter, const SkPaint* paint); PathTexture* get(const SkPath* path, const SkPaint* paint); - void remove(const SkPath* path, const SkPaint* paint); + void remove(const SkPath* path, const SkPaint* paint); /** * Removes the specified path. This is meant to be called from threads @@ -234,19 +216,16 @@ public: void precache(const SkPath* path, const SkPaint* paint); private: - PathTexture* addTexture(const PathDescription& entry, - const SkPath *path, const SkPaint* paint); + PathTexture* addTexture(const PathDescription& entry, const SkPath* path, const SkPaint* paint); /** * Generates the texture from a bitmap into the specified texture structure. */ void generateTexture(Bitmap& bitmap, Texture* texture); void generateTexture(const PathDescription& entry, Bitmap& bitmap, PathTexture* texture, - bool addToCache = true); + bool addToCache = true); - PathTexture* get(const PathDescription& entry) { - return mCache.get(entry); - } + PathTexture* get(const PathDescription& entry) { return mCache.get(entry); } /** * Ensures there is enough space in the cache for a texture of the specified @@ -258,13 +237,12 @@ private: void init(); - - class PathProcessor: public TaskProcessor<sk_sp<Bitmap> > { + class PathProcessor : public TaskProcessor<sk_sp<Bitmap>> { public: explicit PathProcessor(Caches& caches); - ~PathProcessor() { } + ~PathProcessor() {} - virtual void onProcess(const sp<Task<sk_sp<Bitmap> > >& task) override; + virtual void onProcess(const sp<Task<sk_sp<Bitmap>>>& task) override; private: uint32_t mMaxTextureSize; @@ -281,9 +259,9 @@ private: std::vector<uint32_t> mGarbage; mutable Mutex mLock; -}; // class PathCache +}; // class PathCache -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android -#endif // ANDROID_HWUI_PATH_CACHE_H +#endif // ANDROID_HWUI_PATH_CACHE_H diff --git a/libs/hwui/PathParser.cpp b/libs/hwui/PathParser.cpp index 2179f146115a..a48fdfc41c0c 100644 --- a/libs/hwui/PathParser.cpp +++ b/libs/hwui/PathParser.cpp @@ -19,9 +19,9 @@ #include "jni.h" #include <errno.h> +#include <stdlib.h> #include <utils/Log.h> #include <sstream> -#include <stdlib.h> #include <string> #include <vector> @@ -36,8 +36,8 @@ static size_t nextStart(const char* s, size_t length, size_t startIndex) { // used for floating point numbers' scientific notation. // Therefore, when searching for next command, we should ignore 'e' // and 'E'. - if ((((c - 'A') * (c - 'Z') <= 0) || ((c - 'a') * (c - 'z') <= 0)) - && c != 'e' && c != 'E') { + if ((((c - 'A') * (c - 'Z') <= 0) || ((c - 'a') * (c - 'z') <= 0)) && c != 'e' && + c != 'E') { return index; } index++; @@ -52,7 +52,8 @@ static size_t nextStart(const char* s, size_t length, size_t startIndex) { * @param result the result of the extraction, including the position of the * the starting position of next number, whether it is ending with a '-'. */ -static void extract(int* outEndPosition, bool* outEndWithNegOrDot, const char* s, int start, int end) { +static void extract(int* outEndPosition, bool* outEndWithNegOrDot, const char* s, int start, + int end) { // Now looking for ' ', ',', '.' or '-' from the start. int currentIndex = start; bool foundSeparator = false; @@ -64,30 +65,30 @@ static void extract(int* outEndPosition, bool* outEndWithNegOrDot, const char* s isExponential = false; char currentChar = s[currentIndex]; switch (currentChar) { - case ' ': - case ',': - foundSeparator = true; - break; - case '-': - // The negative sign following a 'e' or 'E' is not a separator. - if (currentIndex != start && !isPrevExponential) { - foundSeparator = true; - *outEndWithNegOrDot = true; - } - break; - case '.': - if (!secondDot) { - secondDot = true; - } else { - // This is the second dot, and it is considered as a separator. + case ' ': + case ',': foundSeparator = true; - *outEndWithNegOrDot = true; - } - break; - case 'e': - case 'E': - isExponential = true; - break; + break; + case '-': + // The negative sign following a 'e' or 'E' is not a separator. + if (currentIndex != start && !isPrevExponential) { + foundSeparator = true; + *outEndWithNegOrDot = true; + } + break; + case '.': + if (!secondDot) { + secondDot = true; + } else { + // This is the second dot, and it is considered as a separator. + foundSeparator = true; + *outEndWithNegOrDot = true; + } + break; + case 'e': + case 'E': + isExponential = true; + break; } if (foundSeparator) { break; @@ -98,7 +99,8 @@ static void extract(int* outEndPosition, bool* outEndWithNegOrDot, const char* s *outEndPosition = currentIndex; } -static float parseFloat(PathParser::ParseResult* result, const char* startPtr, size_t expectedLength) { +static float parseFloat(PathParser::ParseResult* result, const char* startPtr, + size_t expectedLength) { char* endPtr = NULL; float currentValue = strtof(startPtr, &endPtr); if ((currentValue == HUGE_VALF || currentValue == -HUGE_VALF) && errno == ERANGE) { @@ -122,8 +124,7 @@ static float parseFloat(PathParser::ParseResult* result, const char* startPtr, s * @return true on success */ static void getFloats(std::vector<float>* outPoints, PathParser::ParseResult* result, - const char* pathStr, int start, int end) { - + const char* pathStr, int start, int end) { if (pathStr[start] == 'z' || pathStr[start] == 'Z') { return; } @@ -138,8 +139,7 @@ static void getFloats(std::vector<float>* outPoints, PathParser::ParseResult* re extract(&endPosition, &endWithNegOrDot, pathStr, startPosition, end); if (startPosition < endPosition) { - float currentValue = parseFloat(result, &pathStr[startPosition], - end - startPosition); + float currentValue = parseFloat(result, &pathStr[startPosition], end - startPosition); if (result->failureOccurred) { return; } @@ -158,12 +158,12 @@ static void getFloats(std::vector<float>* outPoints, PathParser::ParseResult* re bool PathParser::isVerbValid(char verb) { verb = tolower(verb); - return verb == 'a' || verb == 'c' || verb == 'h' || verb == 'l' || verb == 'm' || verb == 'q' - || verb == 's' || verb == 't' || verb == 'v' || verb == 'z'; + return verb == 'a' || verb == 'c' || verb == 'h' || verb == 'l' || verb == 'm' || verb == 'q' || + verb == 's' || verb == 't' || verb == 'v' || verb == 'z'; } void PathParser::getPathDataFromAsciiString(PathData* data, ParseResult* result, - const char* pathStr, size_t strLen) { + const char* pathStr, size_t strLen) { if (pathStr == NULL) { result->failureOccurred = true; result->failureMessage = "Path string cannot be NULL."; @@ -188,8 +188,8 @@ void PathParser::getPathDataFromAsciiString(PathData* data, ParseResult* result, getFloats(&points, result, pathStr, start, end); if (!isVerbValid(pathStr[start])) { result->failureOccurred = true; - result->failureMessage = "Invalid pathData. Failure occurred at position " - + std::to_string(start) + " of path: " + pathStr; + result->failureMessage = "Invalid pathData. Failure occurred at position " + + std::to_string(start) + " of path: " + pathStr; } // If either verb or points is not valid, return immediately. if (result->failureOccurred) { @@ -205,8 +205,8 @@ void PathParser::getPathDataFromAsciiString(PathData* data, ParseResult* result, if ((end - start) == 1 && start < strLen) { if (!isVerbValid(pathStr[start])) { result->failureOccurred = true; - result->failureMessage = "Invalid pathData. Failure occurred at position " - + std::to_string(start) + " of path: " + pathStr; + result->failureMessage = "Invalid pathData. Failure occurred at position " + + std::to_string(start) + " of path: " + pathStr; return; } data->verbs.push_back(pathStr[start]); @@ -235,7 +235,8 @@ void PathParser::dump(const PathData& data) { ALOGD("points are : %s", os.str().c_str()); } -void PathParser::parseAsciiStringForSkPath(SkPath* skPath, ParseResult* result, const char* pathStr, size_t strLen) { +void PathParser::parseAsciiStringForSkPath(SkPath* skPath, ParseResult* result, const char* pathStr, + size_t strLen) { PathData pathData; getPathDataFromAsciiString(&pathData, result, pathStr, strLen); if (result->failureOccurred) { @@ -252,5 +253,5 @@ void PathParser::parseAsciiStringForSkPath(SkPath* skPath, ParseResult* result, return; } -}; // namespace uirenderer -}; //namespace android +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/PathParser.h b/libs/hwui/PathParser.h index 5578e8d42e2f..e4ec7be5ee8e 100644 --- a/libs/hwui/PathParser.h +++ b/libs/hwui/PathParser.h @@ -20,16 +20,15 @@ #include "VectorDrawable.h" #include "utils/VectorDrawableUtils.h" -#include <jni.h> #include <android/log.h> #include <cutils/compiler.h> +#include <jni.h> #include <string> namespace android { namespace uirenderer { - class PathParser { public: struct ANDROID_API ParseResult { @@ -40,13 +39,13 @@ public: * Parse the string literal and create a Skia Path. Return true on success. */ ANDROID_API static void parseAsciiStringForSkPath(SkPath* outPath, ParseResult* result, - const char* pathStr, size_t strLength); + const char* pathStr, size_t strLength); ANDROID_API static void getPathDataFromAsciiString(PathData* outData, ParseResult* result, - const char* pathStr, size_t strLength); + const char* pathStr, size_t strLength); static void dump(const PathData& data); static bool isVerbValid(char verb); }; -}; // namespace uirenderer -}; // namespace android -#endif //ANDROID_HWUI_PATHPARSER_H +}; // namespace uirenderer +}; // namespace android +#endif // ANDROID_HWUI_PATHPARSER_H diff --git a/libs/hwui/PathTessellator.cpp b/libs/hwui/PathTessellator.cpp index 64b2c4564d7b..973b1d14b77e 100644 --- a/libs/hwui/PathTessellator.cpp +++ b/libs/hwui/PathTessellator.cpp @@ -18,13 +18,12 @@ #define VERTEX_DEBUG 0 #if VERTEX_DEBUG -#define DEBUG_DUMP_ALPHA_BUFFER() \ - for (unsigned int i = 0; i < vertexBuffer.getSize(); i++) { \ - ALOGD("point %d at %f %f, alpha %f", \ - i, buffer[i].x, buffer[i].y, buffer[i].alpha); \ +#define DEBUG_DUMP_ALPHA_BUFFER() \ + for (unsigned int i = 0; i < vertexBuffer.getSize(); i++) { \ + ALOGD("point %d at %f %f, alpha %f", i, buffer[i].x, buffer[i].y, buffer[i].alpha); \ } -#define DEBUG_DUMP_BUFFER() \ - for (unsigned int i = 0; i < vertexBuffer.getSize(); i++) { \ +#define DEBUG_DUMP_BUFFER() \ + for (unsigned int i = 0; i < vertexBuffer.getSize(); i++) { \ ALOGD("point %d at %f %f", i, buffer[i].x, buffer[i].y); \ } #else @@ -41,13 +40,13 @@ #include <algorithm> -#include <SkPath.h> +#include <SkGeometry.h> // WARNING: Internal Skia Header #include <SkPaint.h> +#include <SkPath.h> #include <SkPoint.h> -#include <SkGeometry.h> // WARNING: Internal Skia Header -#include <stdlib.h> #include <stdint.h> +#include <stdlib.h> #include <sys/types.h> #include <utils/Log.h> @@ -64,8 +63,8 @@ namespace uirenderer { /** * Extracts the x and y scale from the transform as positive values, and clamps them */ -void PathTessellator::extractTessellationScales(const Matrix4& transform, - float* scaleX, float* scaleY) { +void PathTessellator::extractTessellationScales(const Matrix4& transform, float* scaleX, + float* scaleY) { if (CC_LIKELY(transform.isPureTranslate())) { *scaleX = 1.0f; *scaleY = 1.0f; @@ -98,9 +97,12 @@ inline static Vector2 totalOffsetFromNormals(const Vector2& normalA, const Vecto */ struct PaintInfo { public: - PaintInfo(const SkPaint* paint, const mat4& transform) : - style(paint->getStyle()), cap(paint->getStrokeCap()), isAA(paint->isAntiAlias()), - halfStrokeWidth(paint->getStrokeWidth() * 0.5f), maxAlpha(1.0f) { + PaintInfo(const SkPaint* paint, const mat4& transform) + : style(paint->getStyle()) + , cap(paint->getStrokeCap()) + , isAA(paint->isAntiAlias()) + , halfStrokeWidth(paint->getStrokeWidth() * 0.5f) + , maxAlpha(1.0f) { // compute inverse scales if (CC_LIKELY(transform.isPureTranslate())) { inverseScaleX = 1.0f; @@ -113,7 +115,7 @@ public: } if (isAA && halfStrokeWidth != 0 && inverseScaleX == inverseScaleY && - 2 * halfStrokeWidth < inverseScaleX) { + 2 * halfStrokeWidth < inverseScaleX) { // AA, with non-hairline stroke, width < 1 pixel. Scale alpha and treat as hairline. maxAlpha *= (2 * halfStrokeWidth) / inverseScaleX; halfStrokeWidth = 0.0f; @@ -171,17 +173,17 @@ public: if (halfStrokeWidth == 0) { // hairline, outset by (0.5f + fudge factor) in post-scaling space bounds->outset(fabs(inverseScaleX) * (0.5f + Vertex::GeometryFudgeFactor()), - fabs(inverseScaleY) * (0.5f + Vertex::GeometryFudgeFactor())); + fabs(inverseScaleY) * (0.5f + Vertex::GeometryFudgeFactor())); } else { // non hairline, outset by half stroke width pre-scaled, and fudge factor post scaled bounds->outset(halfStrokeWidth + fabs(inverseScaleX) * Vertex::GeometryFudgeFactor(), - halfStrokeWidth + fabs(inverseScaleY) * Vertex::GeometryFudgeFactor()); + halfStrokeWidth + fabs(inverseScaleY) * Vertex::GeometryFudgeFactor()); } } }; void getFillVerticesFromPerimeter(const std::vector<Vertex>& perimeter, - VertexBuffer& vertexBuffer) { + VertexBuffer& vertexBuffer) { Vertex* buffer = vertexBuffer.alloc<Vertex>(perimeter.size()); int currentIndex = 0; @@ -206,7 +208,8 @@ void getFillVerticesFromPerimeter(const std::vector<Vertex>& perimeter, * (for a total of perimeter.size() * 2 + 2 vertices) */ void getStrokeVerticesFromPerimeter(const PaintInfo& paintInfo, - const std::vector<Vertex>& perimeter, VertexBuffer& vertexBuffer) { + const std::vector<Vertex>& perimeter, + VertexBuffer& vertexBuffer) { Vertex* buffer = vertexBuffer.alloc<Vertex>(perimeter.size() * 2 + 2); int currentIndex = 0; @@ -222,13 +225,11 @@ void getStrokeVerticesFromPerimeter(const PaintInfo& paintInfo, Vector2 totalOffset = totalOffsetFromNormals(lastNormal, nextNormal); paintInfo.scaleOffsetForStrokeWidth(totalOffset); - Vertex::set(&buffer[currentIndex++], - current->x + totalOffset.x, - current->y + totalOffset.y); + Vertex::set(&buffer[currentIndex++], current->x + totalOffset.x, + current->y + totalOffset.y); - Vertex::set(&buffer[currentIndex++], - current->x - totalOffset.x, - current->y - totalOffset.y); + Vertex::set(&buffer[currentIndex++], current->x - totalOffset.x, + current->y - totalOffset.y); current = next; lastNormal = nextNormal; @@ -242,7 +243,8 @@ void getStrokeVerticesFromPerimeter(const PaintInfo& paintInfo, } static inline void storeBeginEnd(const PaintInfo& paintInfo, const Vertex& center, - const Vector2& normal, Vertex* buffer, int& currentIndex, bool begin) { + const Vector2& normal, Vertex* buffer, int& currentIndex, + bool begin) { Vector2 strokeOffset = normal; paintInfo.scaleOffsetForStrokeWidth(strokeOffset); @@ -264,7 +266,8 @@ static inline void storeBeginEnd(const PaintInfo& paintInfo, const Vertex& cente * 2 - can zig-zag across 'extra' vertices at either end, to create round caps */ void getStrokeVerticesFromUnclosedVertices(const PaintInfo& paintInfo, - const std::vector<Vertex>& vertices, VertexBuffer& vertexBuffer) { + const std::vector<Vertex>& vertices, + VertexBuffer& vertexBuffer) { const int extra = paintInfo.capExtraDivisions(); const int allocSize = (vertices.size() + extra) * 2; Vertex* buffer = vertexBuffer.alloc<Vertex>(allocSize); @@ -272,12 +275,9 @@ void getStrokeVerticesFromUnclosedVertices(const PaintInfo& paintInfo, const int lastIndex = vertices.size() - 1; if (extra > 0) { // tessellate both round caps - float beginTheta = atan2( - - (vertices[0].x - vertices[1].x), - vertices[0].y - vertices[1].y); - float endTheta = atan2( - - (vertices[lastIndex].x - vertices[lastIndex - 1].x), - vertices[lastIndex].y - vertices[lastIndex - 1].y); + float beginTheta = atan2(-(vertices[0].x - vertices[1].x), vertices[0].y - vertices[1].y); + float endTheta = atan2(-(vertices[lastIndex].x - vertices[lastIndex - 1].x), + vertices[lastIndex].y - vertices[lastIndex - 1].y); const float dTheta = PI / (extra + 1); int capOffset; @@ -291,16 +291,15 @@ void getStrokeVerticesFromUnclosedVertices(const PaintInfo& paintInfo, beginTheta += dTheta; Vector2 beginRadialOffset = {cosf(beginTheta), sinf(beginTheta)}; paintInfo.scaleOffsetForStrokeWidth(beginRadialOffset); - Vertex::set(&buffer[capOffset], - vertices[0].x + beginRadialOffset.x, - vertices[0].y + beginRadialOffset.y); + Vertex::set(&buffer[capOffset], vertices[0].x + beginRadialOffset.x, + vertices[0].y + beginRadialOffset.y); endTheta += dTheta; Vector2 endRadialOffset = {cosf(endTheta), sinf(endTheta)}; paintInfo.scaleOffsetForStrokeWidth(endRadialOffset); Vertex::set(&buffer[allocSize - 1 - capOffset], - vertices[lastIndex].x + endRadialOffset.x, - vertices[lastIndex].y + endRadialOffset.y); + vertices[lastIndex].x + endRadialOffset.x, + vertices[lastIndex].y + endRadialOffset.y); } } @@ -317,7 +316,7 @@ void getStrokeVerticesFromUnclosedVertices(const PaintInfo& paintInfo, Vector2 nextNormal = {next->y - current->y, current->x - next->x}; nextNormal.normalize(); - Vector2 strokeOffset = totalOffsetFromNormals(lastNormal, nextNormal); + Vector2 strokeOffset = totalOffsetFromNormals(lastNormal, nextNormal); paintInfo.scaleOffsetForStrokeWidth(strokeOffset); Vector2 center = {current->x, current->y}; @@ -344,8 +343,8 @@ void getStrokeVerticesFromUnclosedVertices(const PaintInfo& paintInfo, * 3 - zig zag back and forth inside the shape to fill it (using perimeter.size() vertices) */ void getFillVerticesFromPerimeterAA(const PaintInfo& paintInfo, - const std::vector<Vertex>& perimeter, VertexBuffer& vertexBuffer, - float maxAlpha = 1.0f) { + const std::vector<Vertex>& perimeter, + VertexBuffer& vertexBuffer, float maxAlpha = 1.0f) { AlphaVertex* buffer = vertexBuffer.alloc<AlphaVertex>(perimeter.size() * 3 + 2); // generate alpha points - fill Alpha vertex gaps in between each point with @@ -362,16 +361,13 @@ void getFillVerticesFromPerimeterAA(const PaintInfo& paintInfo, // AA point offset from original point is that point's normal, such that each side is offset // by .5 pixels - Vector2 totalOffset = paintInfo.deriveAAOffset(totalOffsetFromNormals(lastNormal, nextNormal)); + Vector2 totalOffset = + paintInfo.deriveAAOffset(totalOffsetFromNormals(lastNormal, nextNormal)); - AlphaVertex::set(&buffer[currentIndex++], - current->x + totalOffset.x, - current->y + totalOffset.y, - 0.0f); - AlphaVertex::set(&buffer[currentIndex++], - current->x - totalOffset.x, - current->y - totalOffset.y, - maxAlpha); + AlphaVertex::set(&buffer[currentIndex++], current->x + totalOffset.x, + current->y + totalOffset.y, 0.0f); + AlphaVertex::set(&buffer[currentIndex++], current->x - totalOffset.x, + current->y - totalOffset.y, maxAlpha); current = next; lastNormal = nextNormal; @@ -404,12 +400,11 @@ void getFillVerticesFromPerimeterAA(const PaintInfo& paintInfo, * getStrokeVerticesFromUnclosedVerticesAA() below. */ inline static void storeCapAA(const PaintInfo& paintInfo, const std::vector<Vertex>& vertices, - AlphaVertex* buffer, bool isFirst, Vector2 normal, int offset) { + AlphaVertex* buffer, bool isFirst, Vector2 normal, int offset) { const int extra = paintInfo.capExtraDivisions(); const int extraOffset = (extra + 1) / 2; - const int capIndex = isFirst - ? 2 * offset + 6 + 2 * (extra + extraOffset) - : offset + 2 + 2 * extraOffset; + const int capIndex = + isFirst ? 2 * offset + 6 + 2 * (extra + extraOffset) : offset + 2 + 2 * extraOffset; if (isFirst) normal *= -1; // TODO: this normal should be scaled by radialScale if extra != 0, see totalOffsetFromNormals() @@ -437,26 +432,18 @@ inline static void storeCapAA(const PaintInfo& paintInfo, const std::vector<Vert referencePoint += rotated; } - AlphaVertex::set(&buffer[capIndex + 0], - referencePoint.x + outerOffset.x + capAAOffset.x, - referencePoint.y + outerOffset.y + capAAOffset.y, - 0.0f); - AlphaVertex::set(&buffer[capIndex + 1], - referencePoint.x + innerOffset.x - capAAOffset.x, - referencePoint.y + innerOffset.y - capAAOffset.y, - paintInfo.maxAlpha); + AlphaVertex::set(&buffer[capIndex + 0], referencePoint.x + outerOffset.x + capAAOffset.x, + referencePoint.y + outerOffset.y + capAAOffset.y, 0.0f); + AlphaVertex::set(&buffer[capIndex + 1], referencePoint.x + innerOffset.x - capAAOffset.x, + referencePoint.y + innerOffset.y - capAAOffset.y, paintInfo.maxAlpha); bool isRound = paintInfo.cap == SkPaint::kRound_Cap; const int postCapIndex = (isRound && isFirst) ? (2 * extraOffset - 2) : capIndex + (2 * extra); - AlphaVertex::set(&buffer[postCapIndex + 2], - referencePoint.x - outerOffset.x + capAAOffset.x, - referencePoint.y - outerOffset.y + capAAOffset.y, - 0.0f); - AlphaVertex::set(&buffer[postCapIndex + 3], - referencePoint.x - innerOffset.x - capAAOffset.x, - referencePoint.y - innerOffset.y - capAAOffset.y, - paintInfo.maxAlpha); + AlphaVertex::set(&buffer[postCapIndex + 2], referencePoint.x - outerOffset.x + capAAOffset.x, + referencePoint.y - outerOffset.y + capAAOffset.y, 0.0f); + AlphaVertex::set(&buffer[postCapIndex + 3], referencePoint.x - innerOffset.x - capAAOffset.x, + referencePoint.y - innerOffset.y - capAAOffset.y, paintInfo.maxAlpha); if (isRound) { const float dTheta = PI / (extra + 1); @@ -475,20 +462,18 @@ inline static void storeCapAA(const PaintInfo& paintInfo, const std::vector<Vert AAOffset = paintInfo.deriveAAOffset(radialOffset); paintInfo.scaleOffsetForStrokeWidth(radialOffset); AlphaVertex::set(&buffer[capPerimIndex++], - referencePoint.x + radialOffset.x + AAOffset.x, - referencePoint.y + radialOffset.y + AAOffset.y, - 0.0f); + referencePoint.x + radialOffset.x + AAOffset.x, + referencePoint.y + radialOffset.y + AAOffset.y, 0.0f); AlphaVertex::set(&buffer[capPerimIndex++], - referencePoint.x + radialOffset.x - AAOffset.x, - referencePoint.y + radialOffset.y - AAOffset.y, - paintInfo.maxAlpha); + referencePoint.x + radialOffset.x - AAOffset.x, + referencePoint.y + radialOffset.y - AAOffset.y, paintInfo.maxAlpha); if (isFirst && i == extra - extraOffset) { - //copy most recent two points to first two points + // copy most recent two points to first two points buffer[0] = buffer[capPerimIndex - 2]; buffer[1] = buffer[capPerimIndex - 1]; - capPerimIndex = 2; // start writing the rest of the round cap at index 2 + capPerimIndex = 2; // start writing the rest of the round cap at index 2 } } @@ -513,7 +498,7 @@ inline static void storeCapAA(const PaintInfo& paintInfo, const std::vector<Vert if (isFirst) { buffer[0] = buffer[postCapIndex + 2]; buffer[1] = buffer[postCapIndex + 3]; - buffer[postCapIndex + 4] = buffer[1]; // degenerate tris (the only two!) + buffer[postCapIndex + 4] = buffer[1]; // degenerate tris (the only two!) buffer[postCapIndex + 5] = buffer[postCapIndex + 1]; } else { buffer[6 * vertices.size()] = buffer[postCapIndex + 1]; @@ -574,8 +559,8 @@ or, for rounded caps: = 2 + 6 * pts + 6 * roundDivs */ void getStrokeVerticesFromUnclosedVerticesAA(const PaintInfo& paintInfo, - const std::vector<Vertex>& vertices, VertexBuffer& vertexBuffer) { - + const std::vector<Vertex>& vertices, + VertexBuffer& vertexBuffer) { const int extra = paintInfo.capExtraDivisions(); const int allocSize = 6 * vertices.size() + 2 + 6 * extra; @@ -609,32 +594,20 @@ void getStrokeVerticesFromUnclosedVerticesAA(const PaintInfo& paintInfo, Vector2 outerOffset = innerOffset + AAOffset; innerOffset -= AAOffset; - AlphaVertex::set(&buffer[currentAAOuterIndex++], - current->x + outerOffset.x, - current->y + outerOffset.y, - 0.0f); - AlphaVertex::set(&buffer[currentAAOuterIndex++], - current->x + innerOffset.x, - current->y + innerOffset.y, - paintInfo.maxAlpha); - - AlphaVertex::set(&buffer[currentStrokeIndex++], - current->x + innerOffset.x, - current->y + innerOffset.y, - paintInfo.maxAlpha); - AlphaVertex::set(&buffer[currentStrokeIndex++], - current->x - innerOffset.x, - current->y - innerOffset.y, - paintInfo.maxAlpha); - - AlphaVertex::set(&buffer[currentAAInnerIndex--], - current->x - innerOffset.x, - current->y - innerOffset.y, - paintInfo.maxAlpha); - AlphaVertex::set(&buffer[currentAAInnerIndex--], - current->x - outerOffset.x, - current->y - outerOffset.y, - 0.0f); + AlphaVertex::set(&buffer[currentAAOuterIndex++], current->x + outerOffset.x, + current->y + outerOffset.y, 0.0f); + AlphaVertex::set(&buffer[currentAAOuterIndex++], current->x + innerOffset.x, + current->y + innerOffset.y, paintInfo.maxAlpha); + + AlphaVertex::set(&buffer[currentStrokeIndex++], current->x + innerOffset.x, + current->y + innerOffset.y, paintInfo.maxAlpha); + AlphaVertex::set(&buffer[currentStrokeIndex++], current->x - innerOffset.x, + current->y - innerOffset.y, paintInfo.maxAlpha); + + AlphaVertex::set(&buffer[currentAAInnerIndex--], current->x - innerOffset.x, + current->y - innerOffset.y, paintInfo.maxAlpha); + AlphaVertex::set(&buffer[currentAAInnerIndex--], current->x - outerOffset.x, + current->y - outerOffset.y, 0.0f); current = next; lastNormal = nextNormal; @@ -646,9 +619,9 @@ void getStrokeVerticesFromUnclosedVerticesAA(const PaintInfo& paintInfo, DEBUG_DUMP_ALPHA_BUFFER(); } - void getStrokeVerticesFromPerimeterAA(const PaintInfo& paintInfo, - const std::vector<Vertex>& perimeter, VertexBuffer& vertexBuffer) { + const std::vector<Vertex>& perimeter, + VertexBuffer& vertexBuffer) { AlphaVertex* buffer = vertexBuffer.alloc<AlphaVertex>(6 * perimeter.size() + 8); int offset = 2 * perimeter.size() + 3; @@ -673,32 +646,20 @@ void getStrokeVerticesFromPerimeterAA(const PaintInfo& paintInfo, Vector2 outerOffset = innerOffset + AAOffset; innerOffset -= AAOffset; - AlphaVertex::set(&buffer[currentAAOuterIndex++], - current->x + outerOffset.x, - current->y + outerOffset.y, - 0.0f); - AlphaVertex::set(&buffer[currentAAOuterIndex++], - current->x + innerOffset.x, - current->y + innerOffset.y, - paintInfo.maxAlpha); - - AlphaVertex::set(&buffer[currentStrokeIndex++], - current->x + innerOffset.x, - current->y + innerOffset.y, - paintInfo.maxAlpha); - AlphaVertex::set(&buffer[currentStrokeIndex++], - current->x - innerOffset.x, - current->y - innerOffset.y, - paintInfo.maxAlpha); - - AlphaVertex::set(&buffer[currentAAInnerIndex++], - current->x - innerOffset.x, - current->y - innerOffset.y, - paintInfo.maxAlpha); - AlphaVertex::set(&buffer[currentAAInnerIndex++], - current->x - outerOffset.x, - current->y - outerOffset.y, - 0.0f); + AlphaVertex::set(&buffer[currentAAOuterIndex++], current->x + outerOffset.x, + current->y + outerOffset.y, 0.0f); + AlphaVertex::set(&buffer[currentAAOuterIndex++], current->x + innerOffset.x, + current->y + innerOffset.y, paintInfo.maxAlpha); + + AlphaVertex::set(&buffer[currentStrokeIndex++], current->x + innerOffset.x, + current->y + innerOffset.y, paintInfo.maxAlpha); + AlphaVertex::set(&buffer[currentStrokeIndex++], current->x - innerOffset.x, + current->y - innerOffset.y, paintInfo.maxAlpha); + + AlphaVertex::set(&buffer[currentAAInnerIndex++], current->x - innerOffset.x, + current->y - innerOffset.y, paintInfo.maxAlpha); + AlphaVertex::set(&buffer[currentAAInnerIndex++], current->x - outerOffset.x, + current->y - outerOffset.y, 0.0f); current = next; lastNormal = nextNormal; @@ -720,8 +681,8 @@ void getStrokeVerticesFromPerimeterAA(const PaintInfo& paintInfo, DEBUG_DUMP_ALPHA_BUFFER(); } -void PathTessellator::tessellatePath(const SkPath &path, const SkPaint* paint, - const mat4& transform, VertexBuffer& vertexBuffer) { +void PathTessellator::tessellatePath(const SkPath& path, const SkPaint* paint, + const mat4& transform, VertexBuffer& vertexBuffer) { ATRACE_CALL(); const PaintInfo paintInfo(paint, transform); @@ -742,9 +703,9 @@ void PathTessellator::tessellatePath(const SkPath &path, const SkPaint* paint, // force close if we're filling the path, since fill path expects closed perimeter. bool forceClose = paintInfo.style != SkPaint::kStroke_Style; PathApproximationInfo approximationInfo(threshInvScaleX, threshInvScaleY, - OUTLINE_REFINE_THRESHOLD); - bool wasClosed = approximatePathOutlineVertices(path, forceClose, - approximationInfo, tempVertices); + OUTLINE_REFINE_THRESHOLD); + bool wasClosed = + approximatePathOutlineVertices(path, forceClose, approximationInfo, tempVertices); if (!tempVertices.size()) { // path was empty, return without allocating vertex buffer @@ -753,8 +714,7 @@ void PathTessellator::tessellatePath(const SkPath &path, const SkPaint* paint, #if VERTEX_DEBUG for (unsigned int i = 0; i < tempVertices.size(); i++) { - ALOGD("orig path: point at %f %f", - tempVertices[i].x, tempVertices[i].y); + ALOGD("orig path: point at %f %f", tempVertices[i].x, tempVertices[i].y); } #endif @@ -790,8 +750,8 @@ void PathTessellator::tessellatePath(const SkPath &path, const SkPaint* paint, } template <class TYPE> -static void instanceVertices(VertexBuffer& srcBuffer, VertexBuffer& dstBuffer, - const float* points, int count, Rect& bounds) { +static void instanceVertices(VertexBuffer& srcBuffer, VertexBuffer& dstBuffer, const float* points, + int count, Rect& bounds) { bounds.set(points[0], points[1], points[0], points[1]); int numPoints = count / 2; @@ -806,7 +766,7 @@ static void instanceVertices(VertexBuffer& srcBuffer, VertexBuffer& dstBuffer, } void PathTessellator::tessellatePoints(const float* points, int count, const SkPaint* paint, - const mat4& transform, VertexBuffer& vertexBuffer) { + const mat4& transform, VertexBuffer& vertexBuffer) { const PaintInfo paintInfo(paint, transform); // determine point shape @@ -823,7 +783,7 @@ void PathTessellator::tessellatePoints(const float* points, int count, const SkP // calculate outline std::vector<Vertex> outlineVertices; PathApproximationInfo approximationInfo(paintInfo.inverseScaleX, paintInfo.inverseScaleY, - OUTLINE_REFINE_THRESHOLD); + OUTLINE_REFINE_THRESHOLD); approximatePathOutlineVertices(path, true, approximationInfo, outlineVertices); if (!outlineVertices.size()) return; @@ -847,7 +807,7 @@ void PathTessellator::tessellatePoints(const float* points, int count, const SkP } void PathTessellator::tessellateLines(const float* points, int count, const SkPaint* paint, - const mat4& transform, VertexBuffer& vertexBuffer) { + const mat4& transform, VertexBuffer& vertexBuffer) { ATRACE_CALL(); const PaintInfo paintInfo(paint, transform); @@ -900,7 +860,7 @@ void PathTessellator::tessellateLines(const float* points, int count, const SkPa /////////////////////////////////////////////////////////////////////////////// bool PathTessellator::approximatePathOutlineVertices(const SkPath& path, float threshold, - std::vector<Vertex>& outputVertices) { + std::vector<Vertex>& outputVertices) { PathApproximationInfo approximationInfo(1.0f, 1.0f, threshold); return approximatePathOutlineVertices(path, true, approximationInfo, outputVertices); } @@ -932,6 +892,7 @@ public: } } } + private: bool initialized = false; double lastX = 0; @@ -940,7 +901,8 @@ private: }; bool PathTessellator::approximatePathOutlineVertices(const SkPath& path, bool forceClose, - const PathApproximationInfo& approximationInfo, std::vector<Vertex>& outputVertices) { + const PathApproximationInfo& approximationInfo, + std::vector<Vertex>& outputVertices) { ATRACE_CALL(); // TODO: to support joins other than sharp miter, join vertices should be labelled in the @@ -950,7 +912,7 @@ bool PathTessellator::approximatePathOutlineVertices(const SkPath& path, bool fo SkPath::Verb v; ClockwiseEnforcer clockwiseEnforcer; while (SkPath::kDone_Verb != (v = iter.next(pts))) { - switch (v) { + switch (v) { case SkPath::kMove_Verb: outputVertices.push_back(Vertex{pts[0].x(), pts[0].y()}); ALOGV("Move to pos %f %f", pts[0].x(), pts[0].y()); @@ -967,22 +929,17 @@ bool PathTessellator::approximatePathOutlineVertices(const SkPath& path, bool fo break; case SkPath::kQuad_Verb: ALOGV("kQuad_Verb"); - recursiveQuadraticBezierVertices( - pts[0].x(), pts[0].y(), - pts[2].x(), pts[2].y(), - pts[1].x(), pts[1].y(), - approximationInfo, outputVertices); + recursiveQuadraticBezierVertices(pts[0].x(), pts[0].y(), pts[2].x(), pts[2].y(), + pts[1].x(), pts[1].y(), approximationInfo, + outputVertices); clockwiseEnforcer.addPoint(pts[1]); clockwiseEnforcer.addPoint(pts[2]); break; case SkPath::kCubic_Verb: ALOGV("kCubic_Verb"); - recursiveCubicBezierVertices( - pts[0].x(), pts[0].y(), - pts[1].x(), pts[1].y(), - pts[3].x(), pts[3].y(), - pts[2].x(), pts[2].y(), - approximationInfo, outputVertices); + recursiveCubicBezierVertices(pts[0].x(), pts[0].y(), pts[1].x(), pts[1].y(), + pts[3].x(), pts[3].y(), pts[2].x(), pts[2].y(), + approximationInfo, outputVertices); clockwiseEnforcer.addPoint(pts[1]); clockwiseEnforcer.addPoint(pts[2]); clockwiseEnforcer.addPoint(pts[3]); @@ -990,37 +947,33 @@ bool PathTessellator::approximatePathOutlineVertices(const SkPath& path, bool fo case SkPath::kConic_Verb: { ALOGV("kConic_Verb"); SkAutoConicToQuads converter; - const SkPoint* quads = converter.computeQuads(pts, iter.conicWeight(), - approximationInfo.thresholdForConicQuads); + const SkPoint* quads = converter.computeQuads( + pts, iter.conicWeight(), approximationInfo.thresholdForConicQuads); for (int i = 0; i < converter.countQuads(); ++i) { const int offset = 2 * i; - recursiveQuadraticBezierVertices( - quads[offset].x(), quads[offset].y(), - quads[offset+2].x(), quads[offset+2].y(), - quads[offset+1].x(), quads[offset+1].y(), - approximationInfo, outputVertices); + recursiveQuadraticBezierVertices(quads[offset].x(), quads[offset].y(), + quads[offset + 2].x(), quads[offset + 2].y(), + quads[offset + 1].x(), quads[offset + 1].y(), + approximationInfo, outputVertices); } clockwiseEnforcer.addPoint(pts[1]); clockwiseEnforcer.addPoint(pts[2]); break; } default: - static_assert(SkPath::kMove_Verb == 0 - && SkPath::kLine_Verb == 1 - && SkPath::kQuad_Verb == 2 - && SkPath::kConic_Verb == 3 - && SkPath::kCubic_Verb == 4 - && SkPath::kClose_Verb == 5 - && SkPath::kDone_Verb == 6, - "Path enum changed, new types may have been added"); + static_assert(SkPath::kMove_Verb == 0 && SkPath::kLine_Verb == 1 && + SkPath::kQuad_Verb == 2 && SkPath::kConic_Verb == 3 && + SkPath::kCubic_Verb == 4 && SkPath::kClose_Verb == 5 && + SkPath::kDone_Verb == 6, + "Path enum changed, new types may have been added"); break; - } + } } bool wasClosed = false; int size = outputVertices.size(); if (size >= 2 && outputVertices[0].x == outputVertices[size - 1].x && - outputVertices[0].y == outputVertices[size - 1].y) { + outputVertices[0].y == outputVertices[size - 1].y) { outputVertices.pop_back(); wasClosed = true; } @@ -1045,19 +998,17 @@ static inline float getThreshold(const PathApproximationInfo& info, float dx, fl return info.thresholdSquared * scale; } -void PathTessellator::recursiveCubicBezierVertices( - float p1x, float p1y, float c1x, float c1y, - float p2x, float p2y, float c2x, float c2y, - const PathApproximationInfo& approximationInfo, - std::vector<Vertex>& outputVertices, int depth) { +void PathTessellator::recursiveCubicBezierVertices(float p1x, float p1y, float c1x, float c1y, + float p2x, float p2y, float c2x, float c2y, + const PathApproximationInfo& approximationInfo, + std::vector<Vertex>& outputVertices, int depth) { float dx = p2x - p1x; float dy = p2y - p1y; float d1 = fabs((c1x - p2x) * dy - (c1y - p2y) * dx); float d2 = fabs((c2x - p2x) * dy - (c2y - p2y) * dx); float d = d1 + d2; - if (depth >= MAX_DEPTH - || d * d <= getThreshold(approximationInfo, dx, dy)) { + if (depth >= MAX_DEPTH || d * d <= getThreshold(approximationInfo, dx, dy)) { // below thresh, draw line by adding endpoint outputVertices.push_back(Vertex{p2x, p2y}); } else { @@ -1078,30 +1029,23 @@ void PathTessellator::recursiveCubicBezierVertices( float mx = (p1c1c2x + p2c1c2x) * 0.5f; float my = (p1c1c2y + p2c1c2y) * 0.5f; - recursiveCubicBezierVertices( - p1x, p1y, p1c1x, p1c1y, - mx, my, p1c1c2x, p1c1c2y, - approximationInfo, outputVertices, depth + 1); - recursiveCubicBezierVertices( - mx, my, p2c1c2x, p2c1c2y, - p2x, p2y, p2c2x, p2c2y, - approximationInfo, outputVertices, depth + 1); + recursiveCubicBezierVertices(p1x, p1y, p1c1x, p1c1y, mx, my, p1c1c2x, p1c1c2y, + approximationInfo, outputVertices, depth + 1); + recursiveCubicBezierVertices(mx, my, p2c1c2x, p2c1c2y, p2x, p2y, p2c2x, p2c2y, + approximationInfo, outputVertices, depth + 1); } } void PathTessellator::recursiveQuadraticBezierVertices( - float ax, float ay, - float bx, float by, - float cx, float cy, - const PathApproximationInfo& approximationInfo, - std::vector<Vertex>& outputVertices, int depth) { + float ax, float ay, float bx, float by, float cx, float cy, + const PathApproximationInfo& approximationInfo, std::vector<Vertex>& outputVertices, + int depth) { float dx = bx - ax; float dy = by - ay; // d is the cross product of vector (B-A) and (C-B). float d = (cx - bx) * dy - (cy - by) * dx; - if (depth >= MAX_DEPTH - || d * d <= getThreshold(approximationInfo, dx, dy)) { + if (depth >= MAX_DEPTH || d * d <= getThreshold(approximationInfo, dx, dy)) { // below thresh, draw line by adding endpoint outputVertices.push_back(Vertex{bx, by}); } else { @@ -1114,12 +1058,12 @@ void PathTessellator::recursiveQuadraticBezierVertices( float mx = (acx + bcx) * 0.5f; float my = (acy + bcy) * 0.5f; - recursiveQuadraticBezierVertices(ax, ay, mx, my, acx, acy, - approximationInfo, outputVertices, depth + 1); - recursiveQuadraticBezierVertices(mx, my, bx, by, bcx, bcy, - approximationInfo, outputVertices, depth + 1); + recursiveQuadraticBezierVertices(ax, ay, mx, my, acx, acy, approximationInfo, + outputVertices, depth + 1); + recursiveQuadraticBezierVertices(mx, my, bx, by, bcx, bcy, approximationInfo, + outputVertices, depth + 1); } } -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/PathTessellator.h b/libs/hwui/PathTessellator.h index cddfb049212c..ed268324b341 100644 --- a/libs/hwui/PathTessellator.h +++ b/libs/hwui/PathTessellator.h @@ -39,11 +39,10 @@ namespace uirenderer { */ struct PathApproximationInfo { PathApproximationInfo(float invScaleX, float invScaleY, float pixelThreshold) - : thresholdSquared(pixelThreshold * pixelThreshold) - , sqrInvScaleX(invScaleX * invScaleX) - , sqrInvScaleY(invScaleY * invScaleY) - , thresholdForConicQuads(pixelThreshold * std::min(invScaleX, invScaleY) / 2.0f) { - }; + : thresholdSquared(pixelThreshold * pixelThreshold) + , sqrInvScaleX(invScaleX * invScaleX) + , sqrInvScaleY(invScaleY * invScaleY) + , thresholdForConicQuads(pixelThreshold * std::min(invScaleX, invScaleY) / 2.0f){}; const float thresholdSquared; const float sqrInvScaleX; @@ -64,7 +63,8 @@ public: static void extractTessellationScales(const Matrix4& transform, float* scaleX, float* scaleY); /** - * Populates a VertexBuffer with a tessellated approximation of the input convex path, as a single + * Populates a VertexBuffer with a tessellated approximation of the input convex path, as a + * single * triangle strip. Note: joins are not currently supported. * * @param path The path to be approximated @@ -74,8 +74,8 @@ public: * vertex approximation, and correct AA ramp offsetting. * @param vertexBuffer The output buffer */ - static void tessellatePath(const SkPath& path, const SkPaint* paint, - const mat4& transform, VertexBuffer& vertexBuffer); + static void tessellatePath(const SkPath& path, const SkPaint* paint, const mat4& transform, + VertexBuffer& vertexBuffer); /** * Populates a VertexBuffer with a tessellated approximation of points as a single triangle @@ -84,12 +84,13 @@ public: * @param points The center vertices of the points to be drawn * @param count The number of floats making up the point vertices * @param paint The paint the points will be drawn with indicating AA, stroke width & cap - * @param transform The transform the points will be drawn with, used to drive stretch-aware path + * @param transform The transform the points will be drawn with, used to drive stretch-aware + * path * vertex approximation, and correct AA ramp offsetting * @param vertexBuffer The output buffer */ static void tessellatePoints(const float* points, int count, const SkPaint* paint, - const mat4& transform, VertexBuffer& vertexBuffer); + const mat4& transform, VertexBuffer& vertexBuffer); /** * Populates a VertexBuffer with a tessellated approximation of lines as a single triangle @@ -98,12 +99,13 @@ public: * @param points Pairs of endpoints defining the lines to be drawn * @param count The number of floats making up the line vertices * @param paint The paint the lines will be drawn with indicating AA, stroke width & cap - * @param transform The transform the points will be drawn with, used to drive stretch-aware path + * @param transform The transform the points will be drawn with, used to drive stretch-aware + * path * vertex approximation, and correct AA ramp offsetting * @param vertexBuffer The output buffer */ static void tessellateLines(const float* points, int count, const SkPaint* paint, - const mat4& transform, VertexBuffer& vertexBuffer); + const mat4& transform, VertexBuffer& vertexBuffer); /** * Approximates a convex outline into a clockwise Vector of 2d vertices. @@ -112,38 +114,35 @@ public: * @param threshold The threshold of acceptable error (in pixels) when approximating * @param outputVertices An empty Vector which will be populated with the output */ - static bool approximatePathOutlineVertices(const SkPath &path, float threshold, - std::vector<Vertex> &outputVertices); + static bool approximatePathOutlineVertices(const SkPath& path, float threshold, + std::vector<Vertex>& outputVertices); private: - static bool approximatePathOutlineVertices(const SkPath &path, bool forceClose, - const PathApproximationInfo& approximationInfo, std::vector<Vertex> &outputVertices); + static bool approximatePathOutlineVertices(const SkPath& path, bool forceClose, + const PathApproximationInfo& approximationInfo, + std::vector<Vertex>& outputVertices); -/* - endpoints a & b, - control c - */ - static void recursiveQuadraticBezierVertices( - float ax, float ay, - float bx, float by, - float cx, float cy, - const PathApproximationInfo& approximationInfo, - std::vector<Vertex> &outputVertices, int depth = 0); - -/* - endpoints p1, p2 - control c1, c2 - */ - static void recursiveCubicBezierVertices( - float p1x, float p1y, - float c1x, float c1y, - float p2x, float p2y, - float c2x, float c2y, - const PathApproximationInfo& approximationInfo, - std::vector<Vertex> &outputVertices, int depth = 0); + /* + endpoints a & b, + control c + */ + static void recursiveQuadraticBezierVertices(float ax, float ay, float bx, float by, float cx, + float cy, + const PathApproximationInfo& approximationInfo, + std::vector<Vertex>& outputVertices, + int depth = 0); + + /* + endpoints p1, p2 + control c1, c2 + */ + static void recursiveCubicBezierVertices(float p1x, float p1y, float c1x, float c1y, float p2x, + float p2y, float c2x, float c2y, + const PathApproximationInfo& approximationInfo, + std::vector<Vertex>& outputVertices, int depth = 0); }; -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android -#endif // ANDROID_HWUI_PATH_TESSELLATOR_H +#endif // ANDROID_HWUI_PATH_TESSELLATOR_H diff --git a/libs/hwui/PixelBuffer.cpp b/libs/hwui/PixelBuffer.cpp index 2a96b6914afc..910a9889db1f 100644 --- a/libs/hwui/PixelBuffer.cpp +++ b/libs/hwui/PixelBuffer.cpp @@ -31,7 +31,7 @@ namespace uirenderer { // CPU pixel buffer /////////////////////////////////////////////////////////////////////////////// -class CpuPixelBuffer: public PixelBuffer { +class CpuPixelBuffer : public PixelBuffer { public: CpuPixelBuffer(GLenum format, uint32_t width, uint32_t height); @@ -48,8 +48,7 @@ private: CpuPixelBuffer::CpuPixelBuffer(GLenum format, uint32_t width, uint32_t height) : PixelBuffer(format, width, height) - , mBuffer(new uint8_t[width * height * formatSize(format)]) { -} + , mBuffer(new uint8_t[width * height * formatSize(format)]) {} uint8_t* CpuPixelBuffer::map(AccessMode mode) { if (mAccessMode == kAccessMode_None) { @@ -63,15 +62,15 @@ void CpuPixelBuffer::unmap() { } void CpuPixelBuffer::upload(uint32_t x, uint32_t y, uint32_t width, uint32_t height, int offset) { - glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height, - mFormat, GL_UNSIGNED_BYTE, &mBuffer[offset]); + glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height, mFormat, GL_UNSIGNED_BYTE, + &mBuffer[offset]); } /////////////////////////////////////////////////////////////////////////////// // GPU pixel buffer /////////////////////////////////////////////////////////////////////////////// -class GpuPixelBuffer: public PixelBuffer { +class GpuPixelBuffer : public PixelBuffer { public: GpuPixelBuffer(GLenum format, uint32_t width, uint32_t height); ~GpuPixelBuffer(); @@ -89,11 +88,10 @@ private: Caches& mCaches; }; -GpuPixelBuffer::GpuPixelBuffer(GLenum format, - uint32_t width, uint32_t height) +GpuPixelBuffer::GpuPixelBuffer(GLenum format, uint32_t width, uint32_t height) : PixelBuffer(format, width, height) , mMappedPointer(nullptr) - , mCaches(Caches::getInstance()){ + , mCaches(Caches::getInstance()) { glGenBuffers(1, &mBuffer); mCaches.pixelBufferState().bind(mBuffer); @@ -108,7 +106,7 @@ GpuPixelBuffer::~GpuPixelBuffer() { uint8_t* GpuPixelBuffer::map(AccessMode mode) { if (mAccessMode == kAccessMode_None) { mCaches.pixelBufferState().bind(mBuffer); - mMappedPointer = (uint8_t*) glMapBufferRange(GL_PIXEL_UNPACK_BUFFER, 0, getSize(), mode); + mMappedPointer = (uint8_t*)glMapBufferRange(GL_PIXEL_UNPACK_BUFFER, 0, getSize(), mode); if (CC_UNLIKELY(!mMappedPointer)) { GLUtils::dumpGLErrors(); LOG_ALWAYS_FATAL("Failed to map PBO"); @@ -138,8 +136,8 @@ void GpuPixelBuffer::upload(uint32_t x, uint32_t y, uint32_t width, uint32_t hei // If the buffer is not mapped, unmap() will not bind it mCaches.pixelBufferState().bind(mBuffer); unmap(); - glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height, mFormat, - GL_UNSIGNED_BYTE, reinterpret_cast<void*>(offset)); + glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height, mFormat, GL_UNSIGNED_BYTE, + reinterpret_cast<void*>(offset)); mCaches.pixelBufferState().unbind(); } @@ -147,13 +145,12 @@ void GpuPixelBuffer::upload(uint32_t x, uint32_t y, uint32_t width, uint32_t hei // Factory /////////////////////////////////////////////////////////////////////////////// -PixelBuffer* PixelBuffer::create(GLenum format, - uint32_t width, uint32_t height, BufferType type) { +PixelBuffer* PixelBuffer::create(GLenum format, uint32_t width, uint32_t height, BufferType type) { if (type == kBufferType_Auto && Caches::getInstance().gpuPixelBuffersEnabled) { return new GpuPixelBuffer(format, width, height); } return new CpuPixelBuffer(format, width, height); } -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/PixelBuffer.h b/libs/hwui/PixelBuffer.h index 77d5e413cb36..e7e341b90ad3 100644 --- a/libs/hwui/PixelBuffer.h +++ b/libs/hwui/PixelBuffer.h @@ -45,10 +45,7 @@ namespace uirenderer { */ class PixelBuffer { public: - enum BufferType { - kBufferType_Auto, - kBufferType_CPU - }; + enum BufferType { kBufferType_Auto, kBufferType_CPU }; enum AccessMode { kAccessMode_None = 0, @@ -66,17 +63,14 @@ public: * a CPU or GPU buffer. */ static PixelBuffer* create(GLenum format, uint32_t width, uint32_t height, - BufferType type = kBufferType_Auto); + BufferType type = kBufferType_Auto); - virtual ~PixelBuffer() { - } + virtual ~PixelBuffer() {} /** * Returns the format of this render buffer. */ - GLenum getFormat() const { - return mFormat; - } + GLenum getFormat() const { return mFormat; } /** * Maps this before with the specified access mode. This method @@ -95,9 +89,7 @@ public: * Returns the current access mode for this buffer. If the buffer * is not mapped, this method returns kAccessMode_None. */ - AccessMode getAccessMode() const { - return mAccessMode; - } + AccessMode getAccessMode() const { return mAccessMode; } /** * Upload the specified rectangle of this pixel buffer as a @@ -121,23 +113,17 @@ public: /** * Returns the width of the render buffer in pixels. */ - uint32_t getWidth() const { - return mWidth; - } + uint32_t getWidth() const { return mWidth; } /** * Returns the height of the render buffer in pixels. */ - uint32_t getHeight() const { - return mHeight; - } + uint32_t getHeight() const { return mHeight; } /** * Returns the size of this pixel buffer in bytes. */ - uint32_t getSize() const { - return mWidth * mHeight * formatSize(mFormat); - } + uint32_t getSize() const { return mWidth * mHeight * formatSize(mFormat); } /** * Returns the offset of a pixel in this pixel buffer, in bytes. @@ -178,7 +164,7 @@ public: return 3; } - ALOGE("unsupported format: %d",format); + ALOGE("unsupported format: %d", format); return 0; } @@ -187,9 +173,8 @@ protected: * Creates a new render buffer in the specified format and dimensions. * The format must be GL_ALPHA or GL_RGBA. */ - PixelBuffer(GLenum format, uint32_t width, uint32_t height): - mFormat(format), mWidth(width), mHeight(height), mAccessMode(kAccessMode_None) { - } + PixelBuffer(GLenum format, uint32_t width, uint32_t height) + : mFormat(format), mWidth(width), mHeight(height), mAccessMode(kAccessMode_None) {} /** * Unmaps this buffer, if needed. After the buffer is unmapped, @@ -205,9 +190,9 @@ protected: AccessMode mAccessMode; -}; // class PixelBuffer +}; // class PixelBuffer -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android -#endif // ANDROID_HWUI_PIXEL_BUFFER_H +#endif // ANDROID_HWUI_PIXEL_BUFFER_H diff --git a/libs/hwui/ProfileData.cpp b/libs/hwui/ProfileData.cpp index a295c5debc67..b392ecdde18f 100644 --- a/libs/hwui/ProfileData.cpp +++ b/libs/hwui/ProfileData.cpp @@ -22,11 +22,8 @@ namespace android { namespace uirenderer { static const char* JANK_TYPE_NAMES[] = { - "Missed Vsync", - "High input latency", - "Slow UI thread", - "Slow bitmap uploads", - "Slow issue draw commands", + "Missed Vsync", "High input latency", "Slow UI thread", + "Slow bitmap uploads", "Slow issue draw commands", }; // The bucketing algorithm controls so to speak @@ -54,10 +51,8 @@ static uint32_t frameCountIndexForFrameTime(nsecs_t frameTime) { // index = threshold + (amountAboveThreshold / 2) // However if index is <= this will do nothing. It will underflow, do // a right shift by 0 (no-op), then overflow back to the original value - index = ((index - kBucket4msIntervals) >> (index > kBucket4msIntervals)) - + kBucket4msIntervals; - index = ((index - kBucket2msIntervals) >> (index > kBucket2msIntervals)) - + kBucket2msIntervals; + index = ((index - kBucket4msIntervals) >> (index > kBucket4msIntervals)) + kBucket4msIntervals; + index = ((index - kBucket2msIntervals) >> (index > kBucket2msIntervals)) + kBucket2msIntervals; // If index was < minThreshold at the start of all this it's going to // be a pretty garbage value right now. However, mask is 0 so we'll end // up with the desired result of 0. @@ -101,8 +96,7 @@ void ProfileData::mergeWith(const ProfileData& other) { mJankFrameCount += other.mJankFrameCount; mTotalFrameCount >>= divider; mTotalFrameCount += other.mTotalFrameCount; - if (mStatStartTime > other.mStatStartTime - || mStatStartTime == 0) { + if (mStatStartTime > other.mStatStartTime || mStatStartTime == 0) { mStatStartTime = other.mStatStartTime; } } @@ -111,7 +105,7 @@ void ProfileData::dump(int fd) const { dprintf(fd, "\nStats since: %" PRIu64 "ns", mStatStartTime); dprintf(fd, "\nTotal frames rendered: %u", mTotalFrameCount); dprintf(fd, "\nJanky frames: %u (%.2f%%)", mJankFrameCount, - (float) mJankFrameCount / (float) mTotalFrameCount * 100.0f); + (float)mJankFrameCount / (float)mTotalFrameCount * 100.0f); dprintf(fd, "\n50th percentile: %ums", findPercentile(50)); dprintf(fd, "\n90th percentile: %ums", findPercentile(90)); dprintf(fd, "\n95th percentile: %ums", findPercentile(95)); diff --git a/libs/hwui/ProfileData.h b/libs/hwui/ProfileData.h index d53ee29511f0..1e688ab6fa68 100644 --- a/libs/hwui/ProfileData.h +++ b/libs/hwui/ProfileData.h @@ -70,8 +70,8 @@ public: void histogramForEach(const std::function<void(HistogramEntry)>& callback) const; constexpr static int HistogramSize() { - return std::tuple_size<decltype(ProfileData::mFrameCounts)>::value - + std::tuple_size<decltype(ProfileData::mSlowFrameCounts)>::value; + return std::tuple_size<decltype(ProfileData::mFrameCounts)>::value + + std::tuple_size<decltype(ProfileData::mSlowFrameCounts)>::value; } // Visible for testing @@ -82,7 +82,7 @@ private: // Open our guts up to unit tests friend class MockProfileData; - std::array <uint32_t, NUM_BUCKETS> mJankTypeCounts; + std::array<uint32_t, NUM_BUCKETS> mJankTypeCounts; // See comments on kBucket* constants for what this holds std::array<uint32_t, 57> mFrameCounts; // Holds a histogram of frame times in 50ms increments from 150ms to 5s @@ -106,4 +106,3 @@ public: } /* namespace uirenderer */ } /* namespace android */ - diff --git a/libs/hwui/ProfileDataContainer.cpp b/libs/hwui/ProfileDataContainer.cpp index 9db0ef1d6e9a..38e0f0aa8d83 100644 --- a/libs/hwui/ProfileDataContainer.cpp +++ b/libs/hwui/ProfileDataContainer.cpp @@ -16,8 +16,10 @@ #include "ProfileDataContainer.h" -#include <log/log.h> +#include <errno.h> + #include <cutils/ashmem.h> +#include <log/log.h> #include <errno.h> #include <sys/mman.h> @@ -51,21 +53,20 @@ void ProfileDataContainer::switchStorageToAshmem(int ashmemfd) { int regionSize = ashmem_get_size_region(ashmemfd); if (regionSize < 0) { int err = errno; - ALOGW("Failed to get ashmem region size from fd %d, err %d %s", ashmemfd, err, strerror(err)); + ALOGW("Failed to get ashmem region size from fd %d, err %d %s", ashmemfd, err, + strerror(err)); return; } if (regionSize < static_cast<int>(sizeof(ProfileData))) { - ALOGW("Ashmem region is too small! Received %d, required %u", - regionSize, static_cast<unsigned int>(sizeof(ProfileData))); + ALOGW("Ashmem region is too small! Received %d, required %u", regionSize, + static_cast<unsigned int>(sizeof(ProfileData))); return; } ProfileData* newData = reinterpret_cast<ProfileData*>( - mmap(NULL, sizeof(ProfileData), PROT_READ | PROT_WRITE, - MAP_SHARED, ashmemfd, 0)); + mmap(NULL, sizeof(ProfileData), PROT_READ | PROT_WRITE, MAP_SHARED, ashmemfd, 0)); if (newData == MAP_FAILED) { int err = errno; - ALOGW("Failed to move profile data to ashmem fd %d, error = %d", - ashmemfd, err); + ALOGW("Failed to move profile data to ashmem fd %d, error = %d", ashmemfd, err); return; } diff --git a/libs/hwui/ProfileDataContainer.h b/libs/hwui/ProfileDataContainer.h index d2de24188c7c..a39869491ede 100644 --- a/libs/hwui/ProfileDataContainer.h +++ b/libs/hwui/ProfileDataContainer.h @@ -24,6 +24,7 @@ namespace uirenderer { class ProfileDataContainer { PREVENT_COPY_AND_ASSIGN(ProfileDataContainer); + public: explicit ProfileDataContainer() {} diff --git a/libs/hwui/ProfileRenderer.cpp b/libs/hwui/ProfileRenderer.cpp index 0ad484ce0687..8a00ffa54c58 100644 --- a/libs/hwui/ProfileRenderer.cpp +++ b/libs/hwui/ProfileRenderer.cpp @@ -20,7 +20,7 @@ namespace android { namespace uirenderer { void ProfileRenderer::drawRect(float left, float top, float right, float bottom, - const SkPaint& paint) { + const SkPaint& paint) { mRenderer.drawRect(left, top, right, bottom, &paint); } diff --git a/libs/hwui/ProfileRenderer.h b/libs/hwui/ProfileRenderer.h index b9e586f592e8..5c8bb2529fb4 100644 --- a/libs/hwui/ProfileRenderer.h +++ b/libs/hwui/ProfileRenderer.h @@ -23,9 +23,7 @@ namespace uirenderer { class ProfileRenderer : public IProfileRenderer { public: - ProfileRenderer(BakedOpRenderer& renderer) - : mRenderer(renderer) { - } + ProfileRenderer(BakedOpRenderer& renderer) : mRenderer(renderer) {} void drawRect(float left, float top, float right, float bottom, const SkPaint& paint) override; void drawRects(const float* rects, int count, const SkPaint& paint) override; diff --git a/libs/hwui/Program.cpp b/libs/hwui/Program.cpp index e43b80d440e7..052798b9cea9 100644 --- a/libs/hwui/Program.cpp +++ b/libs/hwui/Program.cpp @@ -151,7 +151,7 @@ GLuint Program::buildShader(const char* source, GLenum type) { } void Program::set(const mat4& projectionMatrix, const mat4& modelViewMatrix, - const mat4& transformMatrix, bool offset) { + const mat4& transformMatrix, bool offset) { if (projectionMatrix != mProjection || offset != mOffset) { if (CC_LIKELY(!offset)) { glUniformMatrix4fv(projection, 1, GL_FALSE, &projectionMatrix.data[0]); @@ -195,5 +195,5 @@ void Program::remove() { mUse = false; } -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/Program.h b/libs/hwui/Program.h index 2becfcb709c3..dcc2bd410ebd 100644 --- a/libs/hwui/Program.h +++ b/libs/hwui/Program.h @@ -39,25 +39,25 @@ namespace uirenderer { // Debug #if DEBUG_PROGRAMS - #define PROGRAM_LOGD(...) ALOGD(__VA_ARGS__) +#define PROGRAM_LOGD(...) ALOGD(__VA_ARGS__) #else - #define PROGRAM_LOGD(...) +#define PROGRAM_LOGD(...) #endif #define COLOR_COMPONENT_THRESHOLD 1.0f #define COLOR_COMPONENT_INV_THRESHOLD 0.0f -#define PROGRAM_KEY_TEXTURE 0x01 -#define PROGRAM_KEY_A8_TEXTURE 0x02 -#define PROGRAM_KEY_BITMAP 0x04 -#define PROGRAM_KEY_GRADIENT 0x08 -#define PROGRAM_KEY_BITMAP_FIRST 0x10 -#define PROGRAM_KEY_COLOR_MATRIX 0x20 -#define PROGRAM_KEY_COLOR_BLEND 0x40 -#define PROGRAM_KEY_BITMAP_NPOT 0x80 -#define PROGRAM_KEY_BITMAP_EXTERNAL 0x100 - -#define PROGRAM_KEY_BITMAP_WRAPS_MASK 0x600 +#define PROGRAM_KEY_TEXTURE 0x01 +#define PROGRAM_KEY_A8_TEXTURE 0x02 +#define PROGRAM_KEY_BITMAP 0x04 +#define PROGRAM_KEY_GRADIENT 0x08 +#define PROGRAM_KEY_BITMAP_FIRST 0x10 +#define PROGRAM_KEY_COLOR_MATRIX 0x20 +#define PROGRAM_KEY_COLOR_BLEND 0x40 +#define PROGRAM_KEY_BITMAP_NPOT 0x80 +#define PROGRAM_KEY_BITMAP_EXTERNAL 0x100 + +#define PROGRAM_KEY_BITMAP_WRAPS_MASK 0x600 #define PROGRAM_KEY_BITMAP_WRAPT_MASK 0x1800 #define PROGRAM_KEY_SWAP_SRC_DST_SHIFT 13 @@ -71,7 +71,7 @@ namespace uirenderer { #define PROGRAM_BITMAP_WRAPS_SHIFT 9 #define PROGRAM_BITMAP_WRAPT_SHIFT 11 -#define PROGRAM_GRADIENT_TYPE_SHIFT 33 // 2 bits for gradient type +#define PROGRAM_GRADIENT_TYPE_SHIFT 33 // 2 bits for gradient type #define PROGRAM_MODULATE_SHIFT 35 #define PROGRAM_HAS_VERTEX_ALPHA_SHIFT 36 @@ -91,7 +91,7 @@ namespace uirenderer { #define PROGRAM_HAS_LINEAR_TEXTURE 45 #define PROGRAM_HAS_COLOR_SPACE_CONVERSION 46 -#define PROGRAM_TRANSFER_FUNCTION 47 // 2 bits for transfer function +#define PROGRAM_TRANSFER_FUNCTION 47 // 2 bits for transfer function #define PROGRAM_HAS_TRANSLUCENT_CONVERSION 49 /////////////////////////////////////////////////////////////////////////////// @@ -110,21 +110,11 @@ typedef uint64_t programid; * A ProgramDescription must be used in conjunction with a ProgramCache. */ struct ProgramDescription { - enum class ColorFilterMode : int8_t { - None = 0, - Matrix, - Blend - }; - - enum Gradient : int8_t { - kGradientLinear = 0, - kGradientCircular, - kGradientSweep - }; - - ProgramDescription() { - reset(); - } + enum class ColorFilterMode : int8_t { None = 0, Matrix, Blend }; + + enum Gradient : int8_t { kGradientLinear = 0, kGradientCircular, kGradientSweep }; + + ProgramDescription() { reset(); } // Texturing bool hasTexture; @@ -243,7 +233,7 @@ struct ProgramDescription { */ bool setAlpha8ColorModulate(const float r, const float g, const float b, const float a) { modulate = a < COLOR_COMPONENT_THRESHOLD || r > COLOR_COMPONENT_INV_THRESHOLD || - g > COLOR_COMPONENT_INV_THRESHOLD || b > COLOR_COMPONENT_INV_THRESHOLD; + g > COLOR_COMPONENT_INV_THRESHOLD || b > COLOR_COMPONENT_INV_THRESHOLD; return modulate; } @@ -277,12 +267,12 @@ struct ProgramDescription { break; case ColorFilterMode::Blend: key |= PROGRAM_KEY_COLOR_BLEND; - key |= ((int) colorMode & PROGRAM_MAX_XFERMODE) << PROGRAM_XFERMODE_COLOR_OP_SHIFT; + key |= ((int)colorMode & PROGRAM_MAX_XFERMODE) << PROGRAM_XFERMODE_COLOR_OP_SHIFT; break; case ColorFilterMode::None: break; } - key |= ((int) framebufferMode & PROGRAM_MAX_XFERMODE) << PROGRAM_XFERMODE_FRAMEBUFFER_SHIFT; + key |= ((int)framebufferMode & PROGRAM_MAX_XFERMODE) << PROGRAM_XFERMODE_FRAMEBUFFER_SHIFT; key |= programid(swapSrcDst) << PROGRAM_KEY_SWAP_SRC_DST_SHIFT; key |= programid(modulate) << PROGRAM_MODULATE_SHIFT; key |= programid(hasVertexAlpha) << PROGRAM_HAS_VERTEX_ALPHA_SHIFT; @@ -307,8 +297,7 @@ struct ProgramDescription { void log(const char* message) const { #if DEBUG_PROGRAMS programid k = key(); - PROGRAM_LOGD("%s (key = 0x%.8x%.8x)", message, uint32_t(k >> 32), - uint32_t(k & 0xffffffff)); + PROGRAM_LOGD("%s (key = 0x%.8x%.8x)", message, uint32_t(k >> 32), uint32_t(k & 0xffffffff)); #endif } @@ -325,7 +314,7 @@ private: return 0; } -}; // struct ProgramDescription +}; // struct ProgramDescription /** * A program holds a vertex and a fragment shader. It offers several utility @@ -333,10 +322,7 @@ private: */ class Program { public: - enum ShaderBindings { - kBindingPosition, - kBindingTexCoords - }; + enum ShaderBindings { kBindingPosition, kBindingTexCoords }; /** * Creates a new program with the specified vertex and fragment @@ -370,23 +356,19 @@ public: * Indicates whether this program is currently in use with * the GL context. */ - inline bool isInUse() const { - return mUse; - } + inline bool isInUse() const { return mUse; } /** * Indicates whether this program was correctly compiled and linked. */ - inline bool isInitialized() const { - return mInitialized; - } + inline bool isInitialized() const { return mInitialized; } /** * Binds the program with the specified projection, modelView and * transform matrices. */ - void set(const mat4& projectionMatrix, const mat4& modelViewMatrix, - const mat4& transformMatrix, bool offset = false); + void set(const mat4& projectionMatrix, const mat4& modelViewMatrix, const mat4& transformMatrix, + bool offset = false); /** * Sets the color associated with this shader. @@ -456,9 +438,9 @@ private: mat4 mProjection; bool mOffset; -}; // class Program +}; // class Program -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android -#endif // ANDROID_HWUI_PROGRAM_H +#endif // ANDROID_HWUI_PROGRAM_H diff --git a/libs/hwui/ProgramCache.cpp b/libs/hwui/ProgramCache.cpp index b767046f1a4f..1164ebfdf1e5 100644 --- a/libs/hwui/ProgramCache.cpp +++ b/libs/hwui/ProgramCache.cpp @@ -41,19 +41,14 @@ namespace uirenderer { const char* gVS_Header_Start = "#version 100\n" "attribute vec4 position;\n"; -const char* gVS_Header_Attributes_TexCoords = - "attribute vec2 texCoords;\n"; -const char* gVS_Header_Attributes_Colors = - "attribute vec4 colors;\n"; -const char* gVS_Header_Attributes_VertexAlphaParameters = - "attribute float vtxAlpha;\n"; -const char* gVS_Header_Uniforms_TextureTransform = - "uniform mat4 mainTextureTransform;\n"; +const char* gVS_Header_Attributes_TexCoords = "attribute vec2 texCoords;\n"; +const char* gVS_Header_Attributes_Colors = "attribute vec4 colors;\n"; +const char* gVS_Header_Attributes_VertexAlphaParameters = "attribute float vtxAlpha;\n"; +const char* gVS_Header_Uniforms_TextureTransform = "uniform mat4 mainTextureTransform;\n"; const char* gVS_Header_Uniforms = - "uniform mat4 projection;\n" \ + "uniform mat4 projection;\n" "uniform mat4 transform;\n"; -const char* gVS_Header_Uniforms_HasGradient = - "uniform mat4 screenSpace;\n"; +const char* gVS_Header_Uniforms_HasGradient = "uniform mat4 screenSpace;\n"; const char* gVS_Header_Uniforms_HasBitmap = "uniform mat4 textureTransform;\n" "uniform mediump vec2 textureDimension;\n"; @@ -61,35 +56,24 @@ const char* gVS_Header_Uniforms_HasRoundRectClip = "uniform mat4 roundRectInvTransform;\n" "uniform mediump vec4 roundRectInnerRectLTWH;\n" "uniform mediump float roundRectRadius;\n"; -const char* gVS_Header_Varyings_HasTexture = - "varying vec2 outTexCoords;\n"; -const char* gVS_Header_Varyings_HasColors = - "varying vec4 outColors;\n"; -const char* gVS_Header_Varyings_HasVertexAlpha = - "varying float alpha;\n"; -const char* gVS_Header_Varyings_HasBitmap = - "varying highp vec2 outBitmapTexCoords;\n"; +const char* gVS_Header_Varyings_HasTexture = "varying vec2 outTexCoords;\n"; +const char* gVS_Header_Varyings_HasColors = "varying vec4 outColors;\n"; +const char* gVS_Header_Varyings_HasVertexAlpha = "varying float alpha;\n"; +const char* gVS_Header_Varyings_HasBitmap = "varying highp vec2 outBitmapTexCoords;\n"; const char* gVS_Header_Varyings_HasGradient[6] = { // Linear - "varying highp vec2 linear;\n", - "varying float linear;\n", + "varying highp vec2 linear;\n", "varying float linear;\n", // Circular - "varying highp vec2 circular;\n", - "varying highp vec2 circular;\n", + "varying highp vec2 circular;\n", "varying highp vec2 circular;\n", // Sweep - "varying highp vec2 sweep;\n", - "varying highp vec2 sweep;\n", + "varying highp vec2 sweep;\n", "varying highp vec2 sweep;\n", }; -const char* gVS_Header_Varyings_HasRoundRectClip = - "varying mediump vec2 roundRectPos;\n"; -const char* gVS_Main = - "\nvoid main(void) {\n"; -const char* gVS_Main_OutTexCoords = - " outTexCoords = texCoords;\n"; -const char* gVS_Main_OutColors = - " outColors = colors;\n"; +const char* gVS_Header_Varyings_HasRoundRectClip = "varying mediump vec2 roundRectPos;\n"; +const char* gVS_Main = "\nvoid main(void) {\n"; +const char* gVS_Main_OutTexCoords = " outTexCoords = texCoords;\n"; +const char* gVS_Main_OutColors = " outColors = colors;\n"; const char* gVS_Main_OutTransformedTexCoords = " outTexCoords = (mainTextureTransform * vec4(texCoords, 0.0, 1.0)).xy;\n"; const char* gVS_Main_OutGradient[6] = { @@ -102,53 +86,42 @@ const char* gVS_Main_OutGradient[6] = { " circular = (screenSpace * position).xy;\n", // Sweep - " sweep = (screenSpace * position).xy;\n", - " sweep = (screenSpace * position).xy;\n" -}; + " sweep = (screenSpace * position).xy;\n", " sweep = (screenSpace * position).xy;\n"}; const char* gVS_Main_OutBitmapTexCoords = " outBitmapTexCoords = (textureTransform * position).xy * textureDimension;\n"; const char* gVS_Main_Position = " vec4 transformedPosition = projection * transform * position;\n" " gl_Position = transformedPosition;\n"; -const char* gVS_Main_VertexAlpha = - " alpha = vtxAlpha;\n"; +const char* gVS_Main_VertexAlpha = " alpha = vtxAlpha;\n"; const char* gVS_Main_HasRoundRectClip = - " roundRectPos = ((roundRectInvTransform * transformedPosition).xy / roundRectRadius) - roundRectInnerRectLTWH.xy;\n"; -const char* gVS_Footer = - "}\n\n"; + " roundRectPos = ((roundRectInvTransform * transformedPosition).xy / roundRectRadius) - " + "roundRectInnerRectLTWH.xy;\n"; +const char* gVS_Footer = "}\n\n"; /////////////////////////////////////////////////////////////////////////////// // Fragment shaders snippets /////////////////////////////////////////////////////////////////////////////// -const char* gFS_Header_Start = - "#version 100\n"; +const char* gFS_Header_Start = "#version 100\n"; const char* gFS_Header_Extension_FramebufferFetch = "#extension GL_NV_shader_framebuffer_fetch : enable\n\n"; const char* gFS_Header_Extension_ExternalTexture = "#extension GL_OES_EGL_image_external : require\n\n"; -const char* gFS_Header = - "precision mediump float;\n\n"; -const char* gFS_Uniforms_Color = - "uniform vec4 color;\n"; -const char* gFS_Uniforms_TextureSampler = - "uniform sampler2D baseSampler;\n"; -const char* gFS_Uniforms_ExternalTextureSampler = - "uniform samplerExternalOES baseSampler;\n"; +const char* gFS_Header = "precision mediump float;\n\n"; +const char* gFS_Uniforms_Color = "uniform vec4 color;\n"; +const char* gFS_Uniforms_TextureSampler = "uniform sampler2D baseSampler;\n"; +const char* gFS_Uniforms_ExternalTextureSampler = "uniform samplerExternalOES baseSampler;\n"; const char* gFS_Uniforms_GradientSampler[2] = { "uniform vec2 screenSize;\n" "uniform sampler2D gradientSampler;\n", "uniform vec2 screenSize;\n" "uniform vec4 startColor;\n" - "uniform vec4 endColor;\n" -}; -const char* gFS_Uniforms_BitmapSampler = - "uniform sampler2D bitmapSampler;\n"; -const char* gFS_Uniforms_BitmapExternalSampler = - "uniform samplerExternalOES bitmapSampler;\n"; + "uniform vec4 endColor;\n"}; +const char* gFS_Uniforms_BitmapSampler = "uniform sampler2D bitmapSampler;\n"; +const char* gFS_Uniforms_BitmapExternalSampler = "uniform samplerExternalOES bitmapSampler;\n"; const char* gFS_Uniforms_ColorOp[3] = { // None "", @@ -156,8 +129,7 @@ const char* gFS_Uniforms_ColorOp[3] = { "uniform mat4 colorMatrix;\n" "uniform vec4 colorMatrixVector;\n", // PorterDuff - "uniform vec4 colorBlend;\n" -}; + "uniform vec4 colorBlend;\n"}; const char* gFS_Uniforms_HasRoundRectClip = "uniform mediump vec4 roundRectInnerRectLTWH;\n" @@ -172,11 +144,8 @@ const char* gFS_Uniforms_TransferFunction[4] = { // In this order: g, a, b, c, d, e, f // See ColorSpace::TransferParameters // We'll use hardware sRGB conversion as much as possible - "", - "uniform float transferFunction[7];\n", - "uniform float transferFunction[5];\n", - "uniform float transferFunctionGamma;\n" -}; + "", "uniform float transferFunction[7];\n", "uniform float transferFunction[5];\n", + "uniform float transferFunctionGamma;\n"}; const char* gFS_OETF[2] = { R"__SHADER__( @@ -189,8 +158,7 @@ const char* gFS_OETF[2] = { vec4 OETF(const vec4 linear) { return vec4(sign(linear.rgb) * OETF_sRGB(abs(linear.rgb)), linear.a); } - )__SHADER__" -}; + )__SHADER__"}; const char* gFS_ColorConvert[3] = { // Just OETF @@ -274,8 +242,7 @@ const char* gFS_TransferFunction[4] = { pow(x.g, transferFunctionGamma), pow(x.b, transferFunctionGamma)); } - )__SHADER__" -}; + )__SHADER__"}; // Dithering must be done in the quantization space // When we are writing to an sRGB framebuffer, we must do the following: @@ -327,16 +294,12 @@ const char* gFS_Main = "\nvoid main(void) {\n" " vec4 fragColor;\n"; -const char* gFS_Main_AddDither = - " fragColor = dither(fragColor);\n"; +const char* gFS_Main_AddDither = " fragColor = dither(fragColor);\n"; // General case -const char* gFS_Main_FetchColor = - " fragColor = color;\n"; -const char* gFS_Main_ModulateColor = - " fragColor *= color.a;\n"; -const char* gFS_Main_ApplyVertexAlphaLinearInterp = - " fragColor *= alpha;\n"; +const char* gFS_Main_FetchColor = " fragColor = color;\n"; +const char* gFS_Main_ModulateColor = " fragColor *= color.a;\n"; +const char* gFS_Main_ApplyVertexAlphaLinearInterp = " fragColor *= alpha;\n"; const char* gFS_Main_ApplyVertexAlphaShadowInterp = // map alpha through shadow alpha sampler " fragColor *= texture2D(baseSampler, vec2(alpha, 0.5)).a;\n"; @@ -344,8 +307,7 @@ const char* gFS_Main_FetchTexture[2] = { // Don't modulate " fragColor = colorConvert(texture2D(baseSampler, outTexCoords));\n", // Modulate - " fragColor = color * colorConvert(texture2D(baseSampler, outTexCoords));\n" -}; + " fragColor = color * colorConvert(texture2D(baseSampler, outTexCoords));\n"}; const char* gFS_Main_FetchA8Texture[4] = { // Don't modulate " fragColor = texture2D(baseSampler, outTexCoords);\n", @@ -370,53 +332,46 @@ const char* gFS_Main_FetchGradient[6] = { " vec4 gradientColor = texture2D(gradientSampler, vec2(index - floor(index), 0.5));\n", " highp float index = atan(sweep.y, sweep.x) * 0.15915494309; // inv(2 * PI)\n" - " vec4 gradientColor = mix(startColor, endColor, clamp(index - floor(index), 0.0, 1.0));\n" -}; + " vec4 gradientColor = mix(startColor, endColor, clamp(index - floor(index), 0.0, " + "1.0));\n"}; const char* gFS_Main_FetchBitmap = " vec4 bitmapColor = colorConvert(texture2D(bitmapSampler, outBitmapTexCoords));\n"; const char* gFS_Main_FetchBitmapNpot = - " vec4 bitmapColor = colorConvert(texture2D(bitmapSampler, wrap(outBitmapTexCoords)));\n"; -const char* gFS_Main_BlendShadersBG = - " fragColor = blendShaders(gradientColor, bitmapColor)"; -const char* gFS_Main_BlendShadersGB = - " fragColor = blendShaders(bitmapColor, gradientColor)"; + " vec4 bitmapColor = colorConvert(texture2D(bitmapSampler, " + "wrap(outBitmapTexCoords)));\n"; +const char* gFS_Main_BlendShadersBG = " fragColor = blendShaders(gradientColor, bitmapColor)"; +const char* gFS_Main_BlendShadersGB = " fragColor = blendShaders(bitmapColor, gradientColor)"; const char* gFS_Main_BlendShaders_Modulate[6] = { // Don't modulate - ";\n", - ";\n", + ";\n", ";\n", // Modulate - " * color.a;\n", - " * color.a;\n", + " * color.a;\n", " * color.a;\n", // Modulate with alpha 8 texture " * texture2D(baseSampler, outTexCoords).a;\n", " * gamma(texture2D(baseSampler, outTexCoords).a, color.rgb);\n", }; const char* gFS_Main_GradientShader_Modulate[6] = { // Don't modulate - " fragColor = gradientColor;\n", - " fragColor = gradientColor;\n", + " fragColor = gradientColor;\n", " fragColor = gradientColor;\n", // Modulate - " fragColor = gradientColor * color.a;\n", - " fragColor = gradientColor * color.a;\n", + " fragColor = gradientColor * color.a;\n", " fragColor = gradientColor * color.a;\n", // Modulate with alpha 8 texture " fragColor = gradientColor * texture2D(baseSampler, outTexCoords).a;\n", - " fragColor = gradientColor * gamma(texture2D(baseSampler, outTexCoords).a, gradientColor.rgb);\n", - }; + " fragColor = gradientColor * gamma(texture2D(baseSampler, outTexCoords).a, " + "gradientColor.rgb);\n", +}; const char* gFS_Main_BitmapShader_Modulate[6] = { // Don't modulate - " fragColor = bitmapColor;\n", - " fragColor = bitmapColor;\n", + " fragColor = bitmapColor;\n", " fragColor = bitmapColor;\n", // Modulate - " fragColor = bitmapColor * color.a;\n", - " fragColor = bitmapColor * color.a;\n", + " fragColor = bitmapColor * color.a;\n", " fragColor = bitmapColor * color.a;\n", // Modulate with alpha 8 texture " fragColor = bitmapColor * texture2D(baseSampler, outTexCoords).a;\n", - " fragColor = bitmapColor * gamma(texture2D(baseSampler, outTexCoords).a, bitmapColor.rgb);\n", - }; -const char* gFS_Main_FragColor = - " gl_FragColor = fragColor;\n"; -const char* gFS_Main_FragColor_HasColors = - " gl_FragColor *= outColors;\n"; + " fragColor = bitmapColor * gamma(texture2D(baseSampler, outTexCoords).a, " + "bitmapColor.rgb);\n", +}; +const char* gFS_Main_FragColor = " gl_FragColor = fragColor;\n"; +const char* gFS_Main_FragColor_HasColors = " gl_FragColor *= outColors;\n"; const char* gFS_Main_FragColor_Blend = " gl_FragColor = blendFramebuffer(fragColor, gl_LastFragColor);\n"; const char* gFS_Main_FragColor_Blend_Swap = @@ -425,13 +380,12 @@ const char* gFS_Main_ApplyColorOp[3] = { // None "", // Matrix - " fragColor.rgb /= (fragColor.a + 0.0019);\n" // un-premultiply + " fragColor.rgb /= (fragColor.a + 0.0019);\n" // un-premultiply " fragColor *= colorMatrix;\n" " fragColor += colorMatrixVector;\n" - " fragColor.rgb *= (fragColor.a + 0.0019);\n", // re-premultiply + " fragColor.rgb *= (fragColor.a + 0.0019);\n", // re-premultiply // PorterDuff - " fragColor = blendColors(colorBlend, fragColor);\n" -}; + " fragColor = blendColors(colorBlend, fragColor);\n"}; // Note: LTWH (left top width height) -> xyzw // roundRectPos is now divided by roundRectRadius in vertex shader @@ -443,13 +397,12 @@ const char* gFS_Main_FragColor_HasRoundRectClip = // since distance is divided by radius, it's in [0;1] so precision is not an issue // this also lets us clamp(0.0, 1.0) instead of max() which is cheaper on GPUs " mediump vec2 dist = clamp(max(fragToLT, fragFromRB), 0.0, 1.0);\n" - " mediump float linearDist = clamp(roundRectRadius - (length(dist) * roundRectRadius), 0.0, 1.0);\n" + " mediump float linearDist = clamp(roundRectRadius - (length(dist) * roundRectRadius), " + "0.0, 1.0);\n" " gl_FragColor *= linearDist;\n"; -const char* gFS_Main_DebugHighlight = - " gl_FragColor.rgb = vec3(0.0, gl_FragColor.a, 0.0);\n"; -const char* gFS_Footer = - "}\n\n"; +const char* gFS_Main_DebugHighlight = " gl_FragColor.rgb = vec3(0.0, gl_FragColor.a, 0.0);\n"; +const char* gFS_Footer = "}\n\n"; /////////////////////////////////////////////////////////////////////////////// // PorterDuff snippets @@ -480,7 +433,7 @@ const char* gBlendOps[18] = { "return vec4(dst.rgb * src.a + (1.0 - dst.a) * src.rgb, src.a);\n", // Xor "return vec4(src.rgb * (1.0 - dst.a) + (1.0 - src.a) * dst.rgb, " - "src.a + dst.a - 2.0 * src.a * dst.a);\n", + "src.a + dst.a - 2.0 * src.a * dst.a);\n", // Plus "return min(src + dst, 1.0);\n", // Modulate @@ -489,16 +442,17 @@ const char* gBlendOps[18] = { "return src + dst - src * dst;\n", // Overlay "return clamp(vec4(mix(" - "2.0 * src.rgb * dst.rgb + src.rgb * (1.0 - dst.a) + dst.rgb * (1.0 - src.a), " - "src.a * dst.a - 2.0 * (dst.a - dst.rgb) * (src.a - src.rgb) + src.rgb * (1.0 - dst.a) + dst.rgb * (1.0 - src.a), " - "step(dst.a, 2.0 * dst.rgb)), " - "src.a + dst.a - src.a * dst.a), 0.0, 1.0);\n", + "2.0 * src.rgb * dst.rgb + src.rgb * (1.0 - dst.a) + dst.rgb * (1.0 - src.a), " + "src.a * dst.a - 2.0 * (dst.a - dst.rgb) * (src.a - src.rgb) + src.rgb * (1.0 - dst.a) + " + "dst.rgb * (1.0 - src.a), " + "step(dst.a, 2.0 * dst.rgb)), " + "src.a + dst.a - src.a * dst.a), 0.0, 1.0);\n", // Darken "return vec4(src.rgb * (1.0 - dst.a) + (1.0 - src.a) * dst.rgb + " - "min(src.rgb * dst.a, dst.rgb * src.a), src.a + dst.a - src.a * dst.a);\n", + "min(src.rgb * dst.a, dst.rgb * src.a), src.a + dst.a - src.a * dst.a);\n", // Lighten "return vec4(src.rgb * (1.0 - dst.a) + (1.0 - src.a) * dst.rgb + " - "max(src.rgb * dst.a, dst.rgb * src.a), src.a + dst.a - src.a * dst.a);\n", + "max(src.rgb * dst.a, dst.rgb * src.a), src.a + dst.a - src.a * dst.a);\n", }; /////////////////////////////////////////////////////////////////////////////// @@ -507,8 +461,7 @@ const char* gBlendOps[18] = { ProgramCache::ProgramCache(const Extensions& extensions) : mHasES3(extensions.getMajorGlVersion() >= 3) - , mHasLinearBlending(extensions.hasLinearBlending()) { -} + , mHasLinearBlending(extensions.hasLinearBlending()) {} ProgramCache::~ProgramCache() { clear(); @@ -605,7 +558,8 @@ String8 ProgramCache::generateVertexShader(const ProgramDescription& description } // Begin the shader - shader.append(gVS_Main); { + shader.append(gVS_Main); + { if (description.hasTextureTransform) { shader.append(gVS_Main_OutTransformedTexCoords); } else if (description.hasTexture || description.hasExternalTexture) { @@ -637,8 +591,8 @@ String8 ProgramCache::generateVertexShader(const ProgramDescription& description return shader; } -static bool shaderOp(const ProgramDescription& description, String8& shader, - const int modulateOp, const char** snippets) { +static bool shaderOp(const ProgramDescription& description, String8& shader, const int modulateOp, + const char** snippets) { int op = description.hasAlpha8Texture ? MODULATE_OP_MODULATE_A8 : modulateOp; op = op * 2 + description.hasGammaCorrection; shader.append(snippets[op]); @@ -652,8 +606,8 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti if (blendFramebuffer) { shader.append(gFS_Header_Extension_FramebufferFetch); } - if (description.hasExternalTexture - || (description.hasBitmap && description.isShaderBitmapExternal)) { + if (description.hasExternalTexture || + (description.hasBitmap && description.isShaderBitmapExternal)) { shader.append(gFS_Header_Extension_ExternalTexture); } @@ -682,7 +636,7 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti // Uniforms int modulateOp = MODULATE_OP_NO_MODULATE; const bool singleColor = !description.hasTexture && !description.hasExternalTexture && - !description.hasGradient && !description.hasBitmap; + !description.hasGradient && !description.hasBitmap; if (description.modulate || singleColor) { shader.append(gFS_Uniforms_Color); @@ -701,7 +655,8 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti } if (description.hasGammaCorrection) { - shader.appendFormat(gFS_Gamma_Preamble, Properties::textGamma, 1.0f / Properties::textGamma); + shader.appendFormat(gFS_Gamma_Preamble, Properties::textGamma, + 1.0f / Properties::textGamma); } if (description.hasBitmap) { @@ -731,17 +686,19 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti if (description.useShaderBasedWrap) { generateTextureWrap(shader, description.bitmapWrapS, description.bitmapWrapT); } - if (description.hasGradient || description.hasLinearTexture - || description.hasColorSpaceConversion) { + if (description.hasGradient || description.hasLinearTexture || + description.hasColorSpaceConversion) { shader.append(gFS_sRGB_TransferFunctions); } if (description.hasBitmap || ((description.hasTexture || description.hasExternalTexture) && - !description.hasAlpha8Texture)) { + !description.hasAlpha8Texture)) { shader.append(gFS_TransferFunction[static_cast<int>(description.transferFunction)]); - shader.append(gFS_OETF[(description.hasLinearTexture || description.hasColorSpaceConversion) - && !mHasLinearBlending]); + shader.append( + gFS_OETF[(description.hasLinearTexture || description.hasColorSpaceConversion) && + !mHasLinearBlending]); shader.append(gFS_ColorConvert[description.hasColorSpaceConversion - ? 1 + description.hasTranslucentConversion : 0]); + ? 1 + description.hasTranslucentConversion + : 0]); } if (description.hasGradient) { shader.append(gFS_GradientFunctions); @@ -749,13 +706,14 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti } // Begin the shader - shader.append(gFS_Main); { + shader.append(gFS_Main); + { // Stores the result in fragColor directly if (description.hasTexture || description.hasExternalTexture) { if (description.hasAlpha8Texture) { if (!description.hasGradient && !description.hasBitmap) { - shader.append( - gFS_Main_FetchA8Texture[modulateOp * 2 + description.hasGammaCorrection]); + shader.append(gFS_Main_FetchA8Texture[modulateOp * 2 + + description.hasGammaCorrection]); } } else { shader.append(gFS_Main_FetchTexture[modulateOp]); @@ -783,15 +741,15 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti } else { shader.append(gFS_Main_BlendShadersGB); } - applyModulate = shaderOp(description, shader, modulateOp, - gFS_Main_BlendShaders_Modulate); + applyModulate = + shaderOp(description, shader, modulateOp, gFS_Main_BlendShaders_Modulate); } else { if (description.hasGradient) { - applyModulate = shaderOp(description, shader, modulateOp, - gFS_Main_GradientShader_Modulate); + applyModulate = + shaderOp(description, shader, modulateOp, gFS_Main_GradientShader_Modulate); } else if (description.hasBitmap) { - applyModulate = shaderOp(description, shader, modulateOp, - gFS_Main_BitmapShader_Modulate); + applyModulate = + shaderOp(description, shader, modulateOp, gFS_Main_BitmapShader_Modulate); } } @@ -818,8 +776,8 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti if (!blendFramebuffer) { shader.append(gFS_Main_FragColor); } else { - shader.append(!description.swapSrcDst ? - gFS_Main_FragColor_Blend : gFS_Main_FragColor_Blend_Swap); + shader.append(!description.swapSrcDst ? gFS_Main_FragColor_Blend + : gFS_Main_FragColor_Blend_Swap); } if (description.hasColors) { shader.append(gFS_Main_FragColor_HasColors); @@ -835,8 +793,8 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti shader.append(gFS_Footer); #if DEBUG_PROGRAMS - PROGRAM_LOGD("*** Generated fragment shader:\n\n"); - printLongString(shader); + PROGRAM_LOGD("*** Generated fragment shader:\n\n"); + printLongString(shader); #endif return shader; @@ -903,5 +861,5 @@ void ProgramCache::printLongString(const String8& shader) const { } } -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/ProgramCache.h b/libs/hwui/ProgramCache.h index ee76f22f35d4..488a4994ba95 100644 --- a/libs/hwui/ProgramCache.h +++ b/libs/hwui/ProgramCache.h @@ -60,9 +60,9 @@ private: const bool mHasES3; const bool mHasLinearBlending; -}; // class ProgramCache +}; // class ProgramCache -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android -#endif // ANDROID_HWUI_PROGRAM_CACHE_H +#endif // ANDROID_HWUI_PROGRAM_CACHE_H diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp index acc75393ebcf..6cd283a9063c 100644 --- a/libs/hwui/Properties.cpp +++ b/libs/hwui/Properties.cpp @@ -51,15 +51,22 @@ int Properties::overrideSpotShadowStrength = -1; ProfileType Properties::sProfileType = ProfileType::None; bool Properties::sDisableProfileBars = false; RenderPipelineType Properties::sRenderPipelineType = RenderPipelineType::NotInitialized; +bool Properties::enableHighContrastText = false; bool Properties::waitForGpuCompletion = false; bool Properties::forceDrawFrame = false; bool Properties::filterOutTestOverhead = false; bool Properties::disableVsync = false; +bool Properties::skpCaptureEnabled = false; +bool Properties::enableRTAnimations = true; + +bool Properties::runningInEmulator = false; static int property_get_int(const char* key, int defaultValue) { - char buf[PROPERTY_VALUE_MAX] = {'\0',}; + char buf[PROPERTY_VALUE_MAX] = { + '\0', + }; if (property_get(key, buf, "") > 0) { return atoi(buf); @@ -119,7 +126,7 @@ bool Properties::load() { showDirtyRegions = property_get_bool(PROPERTY_DEBUG_SHOW_DIRTY_REGIONS, false); - debugLevel = (DebugLevel) property_get_int(PROPERTY_DEBUG, kDebugDisabled); + debugLevel = (DebugLevel)property_get_int(PROPERTY_DEBUG, kDebugDisabled); skipEmptyFrames = property_get_bool(PROPERTY_SKIP_EMPTY_DAMAGE, true); useBufferAge = property_get_bool(PROPERTY_USE_BUFFER_AGE, true); @@ -127,9 +134,13 @@ bool Properties::load() { filterOutTestOverhead = property_get_bool(PROPERTY_FILTER_TEST_OVERHEAD, false); - return (prevDebugLayersUpdates != debugLayersUpdates) - || (prevDebugOverdraw != debugOverdraw) - || (prevDebugStencilClip != debugStencilClip); + skpCaptureEnabled = property_get_bool("ro.debuggable", false) && + property_get_bool(PROPERTY_CAPTURE_SKP_ENABLED, false); + + runningInEmulator = property_get_bool(PROPERTY_QEMU_KERNEL, false); + + return (prevDebugLayersUpdates != debugLayersUpdates) || (prevDebugOverdraw != debugOverdraw) || + (prevDebugStencilClip != debugStencilClip); } void Properties::overrideProperty(const char* name, const char* value) { @@ -172,35 +183,40 @@ ProfileType Properties::getProfileType() { } RenderPipelineType Properties::getRenderPipelineType() { - if (RenderPipelineType::NotInitialized != sRenderPipelineType) { + if (sRenderPipelineType != RenderPipelineType::NotInitialized) { return sRenderPipelineType; } char prop[PROPERTY_VALUE_MAX]; - property_get(PROPERTY_RENDERER, prop, "opengl"); - if (!strcmp(prop, "skiagl") ) { + property_get(PROPERTY_RENDERER, prop, "skiagl"); + if (!strcmp(prop, "skiagl")) { ALOGD("Skia GL Pipeline"); sRenderPipelineType = RenderPipelineType::SkiaGL; - } else if (!strcmp(prop, "skiavk") ) { + } else if (!strcmp(prop, "skiavk")) { ALOGD("Skia Vulkan Pipeline"); sRenderPipelineType = RenderPipelineType::SkiaVulkan; - } else { //"opengl" + } else { //"opengl" ALOGD("HWUI GL Pipeline"); sRenderPipelineType = RenderPipelineType::OpenGL; } return sRenderPipelineType; } -#ifdef HWUI_GLES_WRAP_ENABLED void Properties::overrideRenderPipelineType(RenderPipelineType type) { +#if !defined(HWUI_GLES_WRAP_ENABLED) + // If we're doing actual rendering then we can't change the renderer after it's been set. + // Unit tests can freely change this as often as it wants, though, as there's no actual + // GL rendering happening + if (sRenderPipelineType != RenderPipelineType::NotInitialized) { + return; + } +#endif sRenderPipelineType = type; } -#endif bool Properties::isSkiaEnabled() { auto renderType = getRenderPipelineType(); - return RenderPipelineType::SkiaGL == renderType - || RenderPipelineType::SkiaVulkan == renderType; + return RenderPipelineType::SkiaGL == renderType || RenderPipelineType::SkiaVulkan == renderType; } -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h index 47ae9e912127..179b97bf6d9f 100644 --- a/libs/hwui/Properties.h +++ b/libs/hwui/Properties.h @@ -17,6 +17,7 @@ #ifndef ANDROID_HWUI_PROPERTIES_H #define ANDROID_HWUI_PROPERTIES_H +#include <cutils/compiler.h> #include <cutils/properties.h> /** @@ -164,38 +165,42 @@ enum DebugLevel { */ #define PROPERTY_RENDERER "debug.hwui.renderer" +/** + * Allows to collect a recording of Skia drawing commands. + */ +#define PROPERTY_CAPTURE_SKP_ENABLED "debug.hwui.capture_skp_enabled" + +/** + * Defines how many frames in a sequence to capture. + */ +#define PROPERTY_CAPTURE_SKP_FRAMES "debug.hwui.capture_skp_frames" + +/** + * File name and location, where a SKP recording will be saved. + */ +#define PROPERTY_CAPTURE_SKP_FILENAME "debug.hwui.skp_filename" + +/** + * Property for whether this is running in the emulator. + */ +#define PROPERTY_QEMU_KERNEL "ro.kernel.qemu" + /////////////////////////////////////////////////////////////////////////////// // Misc /////////////////////////////////////////////////////////////////////////////// // Converts a number of mega-bytes into bytes -#define MB(s) ((s) * 1024 * 1024) +#define MB(s) ((s)*1024 * 1024) // Converts a number of kilo-bytes into bytes -#define KB(s) ((s) * 1024) +#define KB(s) ((s)*1024) -enum class ProfileType { - None, - Console, - Bars -}; +enum class ProfileType { None, Console, Bars }; -enum class OverdrawColorSet { - Default = 0, - Deuteranomaly -}; +enum class OverdrawColorSet { Default = 0, Deuteranomaly }; -enum class StencilClipDebug { - Hide, - ShowHighlight, - ShowRegion -}; +enum class StencilClipDebug { Hide, ShowHighlight, ShowRegion }; -enum class RenderPipelineType { - OpenGL = 0, - SkiaGL, - SkiaVulkan, - NotInitialized = 128 -}; +enum class RenderPipelineType { OpenGL = 0, SkiaGL, SkiaVulkan, NotInitialized = 128 }; /** * Renderthread-only singleton which manages several static rendering properties. Most of these @@ -234,9 +239,11 @@ public: static int overrideSpotShadowStrength; static ProfileType getProfileType(); - static RenderPipelineType getRenderPipelineType(); + ANDROID_API static RenderPipelineType getRenderPipelineType(); static bool isSkiaEnabled(); + ANDROID_API static bool enableHighContrastText; + // Should be used only by test apps static bool waitForGpuCompletion; static bool forceDrawFrame; @@ -251,18 +258,23 @@ public: // created after changing this. static bool disableVsync; + static bool skpCaptureEnabled; + + // For experimentation b/68769804 + ANDROID_API static bool enableRTAnimations; + // Used for testing only to change the render pipeline. -#ifdef HWUI_GLES_WRAP_ENABLED static void overrideRenderPipelineType(RenderPipelineType); -#endif + + static bool runningInEmulator; private: static ProfileType sProfileType; static bool sDisableProfileBars; static RenderPipelineType sRenderPipelineType; -}; // class Caches +}; // class Caches -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android -#endif // ANDROID_HWUI_PROPERTIES_H +#endif // ANDROID_HWUI_PROPERTIES_H diff --git a/libs/hwui/PropertyValuesAnimatorSet.cpp b/libs/hwui/PropertyValuesAnimatorSet.cpp index e3258e3c1a48..65a5aa4c0112 100644 --- a/libs/hwui/PropertyValuesAnimatorSet.cpp +++ b/libs/hwui/PropertyValuesAnimatorSet.cpp @@ -23,11 +23,11 @@ namespace android { namespace uirenderer { void PropertyValuesAnimatorSet::addPropertyAnimator(PropertyValuesHolder* propertyValuesHolder, - Interpolator* interpolator, nsecs_t startDelay, nsecs_t duration, int repeatCount, - RepeatMode repeatMode) { - - PropertyAnimator* animator = new PropertyAnimator(propertyValuesHolder, - interpolator, startDelay, duration, repeatCount, repeatMode); + Interpolator* interpolator, nsecs_t startDelay, + nsecs_t duration, int repeatCount, + RepeatMode repeatMode) { + PropertyAnimator* animator = new PropertyAnimator( + propertyValuesHolder, interpolator, startDelay, duration, repeatCount, repeatMode); mAnimators.emplace_back(animator); // Check whether any child animator is infinite after adding it them to the set. @@ -36,8 +36,7 @@ void PropertyValuesAnimatorSet::addPropertyAnimator(PropertyValuesHolder* proper } } -PropertyValuesAnimatorSet::PropertyValuesAnimatorSet() - : BaseRenderNodeAnimator(1.0f) { +PropertyValuesAnimatorSet::PropertyValuesAnimatorSet() : BaseRenderNodeAnimator(1.0f) { setStartValue(0); mLastFraction = 0.0f; setInterpolator(new LinearInterpolator()); @@ -77,7 +76,7 @@ void PropertyValuesAnimatorSet::onPlayTimeChanged(nsecs_t playTime) { // have the final say on what the property value should be. (*it)->setFraction(0, 0); } - } else { + } else { for (auto& anim : mAnimators) { anim->setCurrentPlayTime(playTime); } @@ -116,9 +115,8 @@ void PropertyValuesAnimatorSet::init() { // Sort the animators by their total duration. Note that all the animators in the set start at // the same time, so the ones with longer total duration (which includes start delay) will // be the ones that end later. - std::sort(mAnimators.begin(), mAnimators.end(), [](auto& a, auto&b) { - return a->getTotalDuration() < b->getTotalDuration(); - }); + std::sort(mAnimators.begin(), mAnimators.end(), + [](auto& a, auto& b) { return a->getTotalDuration() < b->getTotalDuration(); }); mDuration = mAnimators.empty() ? 0 : mAnimators[mAnimators.size() - 1]->getTotalDuration(); mInitialized = true; } @@ -128,17 +126,19 @@ uint32_t PropertyValuesAnimatorSet::dirtyMask() { } PropertyAnimator::PropertyAnimator(PropertyValuesHolder* holder, Interpolator* interpolator, - nsecs_t startDelay, nsecs_t duration, int repeatCount, - RepeatMode repeatMode) - : mPropertyValuesHolder(holder), mInterpolator(interpolator), mStartDelay(startDelay), - mDuration(duration) { + nsecs_t startDelay, nsecs_t duration, int repeatCount, + RepeatMode repeatMode) + : mPropertyValuesHolder(holder) + , mInterpolator(interpolator) + , mStartDelay(startDelay) + , mDuration(duration) { if (repeatCount < 0) { mRepeatCount = UINT32_MAX; } else { mRepeatCount = repeatCount; } mRepeatMode = repeatMode; - mTotalDuration = ((nsecs_t) mRepeatCount + 1) * mDuration + mStartDelay; + mTotalDuration = ((nsecs_t)mRepeatCount + 1) * mDuration + mStartDelay; } void PropertyAnimator::setCurrentPlayTime(nsecs_t playTime) { @@ -155,7 +155,7 @@ void PropertyAnimator::setCurrentPlayTime(nsecs_t playTime) { } else { // play time here is in range [mStartDelay, mTotalDuration) iteration = (playTime - mStartDelay) / mDuration; - currentIterationFraction = ((playTime - mStartDelay) % mDuration) / (float) mDuration; + currentIterationFraction = ((playTime - mStartDelay) % mDuration) / (float)mDuration; } setFraction(currentIterationFraction, iteration); } @@ -182,6 +182,5 @@ void PropertyAnimator::setFraction(float fraction, long iteration) { void PropertyAnimatorSetListener::onAnimationFinished(BaseRenderNodeAnimator* animator) { mSet->onFinished(animator); } - } } diff --git a/libs/hwui/PropertyValuesAnimatorSet.h b/libs/hwui/PropertyValuesAnimatorSet.h index a5d9e869196f..e4214b22d1cc 100644 --- a/libs/hwui/PropertyValuesAnimatorSet.h +++ b/libs/hwui/PropertyValuesAnimatorSet.h @@ -17,8 +17,8 @@ #pragma once #include "Animator.h" -#include "PropertyValuesHolder.h" #include "Interpolator.h" +#include "PropertyValuesHolder.h" namespace android { namespace uirenderer { @@ -26,11 +26,9 @@ namespace uirenderer { class PropertyAnimator { public: PropertyAnimator(PropertyValuesHolder* holder, Interpolator* interpolator, nsecs_t startDelay, - nsecs_t duration, int repeatCount, RepeatMode repeatMode); + nsecs_t duration, int repeatCount, RepeatMode repeatMode); void setCurrentPlayTime(nsecs_t playTime); - nsecs_t getTotalDuration() { - return mTotalDuration; - } + nsecs_t getTotalDuration() { return mTotalDuration; } // fraction range: [0, 1], iteration range [0, repeatCount] void setFraction(float fraction, long iteration); @@ -57,8 +55,8 @@ public: virtual void end() override; void addPropertyAnimator(PropertyValuesHolder* propertyValuesHolder, - Interpolator* interpolators, int64_t startDelays, - nsecs_t durations, int repeatCount, RepeatMode repeatMode); + Interpolator* interpolators, int64_t startDelays, nsecs_t durations, + int repeatCount, RepeatMode repeatMode); virtual uint32_t dirtyMask(); bool isInfinite() { return mIsInfinite; } void setVectorDrawable(VectorDrawableRoot* vd) { mVectorDrawable = vd; } @@ -77,7 +75,7 @@ private: void onFinished(BaseRenderNodeAnimator* animator); // Listener set from outside sp<AnimationListener> mOneShotListener; - std::vector< std::unique_ptr<PropertyAnimator> > mAnimators; + std::vector<std::unique_ptr<PropertyAnimator> > mAnimators; float mLastFraction = 0.0f; bool mInitialized = false; sp<VectorDrawableRoot> mVectorDrawable; @@ -96,5 +94,5 @@ private: PropertyValuesAnimatorSet* mSet; }; -} // namespace uirenderer -} // namespace android +} // namespace uirenderer +} // namespace android diff --git a/libs/hwui/PropertyValuesHolder.cpp b/libs/hwui/PropertyValuesHolder.cpp index 2a03e6a3ebc5..35526255759f 100644 --- a/libs/hwui/PropertyValuesHolder.cpp +++ b/libs/hwui/PropertyValuesHolder.cpp @@ -27,7 +27,7 @@ namespace uirenderer { using namespace VectorDrawable; inline constexpr float lerp(float fromValue, float toValue, float fraction) { - return float (fromValue * (1 - fraction) + toValue * fraction); + return float(fromValue * (1 - fraction) + toValue * fraction); } inline constexpr float linearize(U8CPU component) { @@ -35,25 +35,23 @@ inline constexpr float linearize(U8CPU component) { } // TODO: Add a test for this -void ColorEvaluator::evaluate(SkColor* outColor, - const SkColor& fromColor, const SkColor& toColor, float fraction) const { +void ColorEvaluator::evaluate(SkColor* outColor, const SkColor& fromColor, const SkColor& toColor, + float fraction) const { float a = lerp(SkColorGetA(fromColor) / 255.0f, SkColorGetA(toColor) / 255.0f, fraction); float r = lerp(linearize(SkColorGetR(fromColor)), linearize(SkColorGetR(toColor)), fraction); float g = lerp(linearize(SkColorGetG(fromColor)), linearize(SkColorGetG(toColor)), fraction); float b = lerp(linearize(SkColorGetB(fromColor)), linearize(SkColorGetB(toColor)), fraction); - *outColor = SkColorSetARGB( - (U8CPU) roundf(a * 255.0f), - (U8CPU) roundf(OECF_sRGB(r) * 255.0f), - (U8CPU) roundf(OECF_sRGB(g) * 255.0f), - (U8CPU) roundf(OECF_sRGB(b) * 255.0f)); + *outColor = SkColorSetARGB((U8CPU)roundf(a * 255.0f), (U8CPU)roundf(OECF_sRGB(r) * 255.0f), + (U8CPU)roundf(OECF_sRGB(g) * 255.0f), + (U8CPU)roundf(OECF_sRGB(b) * 255.0f)); } -void PathEvaluator::evaluate(PathData* out, - const PathData& from, const PathData& to, float fraction) const { +void PathEvaluator::evaluate(PathData* out, const PathData& from, const PathData& to, + float fraction) const { VectorDrawableUtils::interpolatePaths(out, from, to, fraction); } -template<typename T> +template <typename T> const T PropertyValuesHolderImpl<T>::getValueFromData(float fraction) const { if (mDataSource.size() == 0) { LOG_ALWAYS_FATAL("No data source is defined"); @@ -75,7 +73,7 @@ const T PropertyValuesHolderImpl<T>::getValueFromData(float fraction) const { return value; } -template<typename T> +template <typename T> const T PropertyValuesHolderImpl<T>::calculateAnimatedValue(float fraction) const { if (mDataSource.size() > 0) { return getValueFromData(fraction); @@ -111,5 +109,5 @@ void RootAlphaPropertyValuesHolder::setFraction(float fraction) { mTree->mutateProperties()->setRootAlpha(animatedValue); } -} // namepace uirenderer -} // namespace android +} // namepace uirenderer +} // namespace android diff --git a/libs/hwui/PropertyValuesHolder.h b/libs/hwui/PropertyValuesHolder.h index 432f8ba82afb..0a799d3c0b5c 100644 --- a/libs/hwui/PropertyValuesHolder.h +++ b/libs/hwui/PropertyValuesHolder.h @@ -43,8 +43,8 @@ public: class FloatEvaluator : public Evaluator<float> { public: - virtual void evaluate(float* out, const float& from, const float& to, float fraction) - const override { + virtual void evaluate(float* out, const float& from, const float& to, + float fraction) const override { *out = from * (1 - fraction) + to * fraction; } }; @@ -52,20 +52,19 @@ public: class ANDROID_API ColorEvaluator : public Evaluator<SkColor> { public: virtual void evaluate(SkColor* outColor, const SkColor& from, const SkColor& to, - float fraction) const override; + float fraction) const override; }; class ANDROID_API PathEvaluator : public Evaluator<PathData> { - virtual void evaluate(PathData* out, const PathData& from, const PathData& to, float fraction) - const override; + virtual void evaluate(PathData* out, const PathData& from, const PathData& to, + float fraction) const override; }; template <typename T> class ANDROID_API PropertyValuesHolderImpl : public PropertyValuesHolder { public: PropertyValuesHolderImpl(const T& startValue, const T& endValue) - : mStartValue(startValue) - , mEndValue(endValue) {} + : mStartValue(startValue), mEndValue(endValue) {} void setPropertyDataSource(T* dataSource, int length) { mDataSource.insert(mDataSource.begin(), dataSource, dataSource + length); } @@ -74,27 +73,27 @@ public: // Convenient method to favor getting animated value from data source. If no data source is set // fall back to linear interpolation. const T calculateAnimatedValue(float fraction) const; + protected: - std::unique_ptr<Evaluator<T>> mEvaluator = nullptr; - // This contains uniformly sampled data throughout the animation duration. The first element - // should be the start value and the last should be the end value of the animation. When the - // data source is set, we'll favor data source over the linear interpolation of start/end value - // for calculation of animated value. - std::vector<T> mDataSource; - T mStartValue; - T mEndValue; + std::unique_ptr<Evaluator<T>> mEvaluator = nullptr; + // This contains uniformly sampled data throughout the animation duration. The first element + // should be the start value and the last should be the end value of the animation. When the + // data source is set, we'll favor data source over the linear interpolation of start/end value + // for calculation of animated value. + std::vector<T> mDataSource; + T mStartValue; + T mEndValue; }; class ANDROID_API GroupPropertyValuesHolder : public PropertyValuesHolderImpl<float> { public: GroupPropertyValuesHolder(VectorDrawable::Group* ptr, int propertyId, float startValue, - float endValue) - : PropertyValuesHolderImpl(startValue, endValue) - , mGroup(ptr) - , mPropertyId(propertyId) { + float endValue) + : PropertyValuesHolderImpl(startValue, endValue), mGroup(ptr), mPropertyId(propertyId) { mEvaluator.reset(new FloatEvaluator()); } void setFraction(float fraction) override; + private: VectorDrawable::Group* mGroup; int mPropertyId; @@ -103,7 +102,7 @@ private: class ANDROID_API FullPathColorPropertyValuesHolder : public PropertyValuesHolderImpl<SkColor> { public: FullPathColorPropertyValuesHolder(VectorDrawable::FullPath* ptr, int propertyId, - SkColor startValue, SkColor endValue) + SkColor startValue, SkColor endValue) : PropertyValuesHolderImpl(startValue, endValue) , mFullPath(ptr) , mPropertyId(propertyId) { @@ -111,6 +110,7 @@ public: } void setFraction(float fraction) override; static SkColor interpolateColors(SkColor fromColor, SkColor toColor, float fraction); + private: VectorDrawable::FullPath* mFullPath; int mPropertyId; @@ -119,13 +119,14 @@ private: class ANDROID_API FullPathPropertyValuesHolder : public PropertyValuesHolderImpl<float> { public: FullPathPropertyValuesHolder(VectorDrawable::FullPath* ptr, int propertyId, float startValue, - float endValue) + float endValue) : PropertyValuesHolderImpl(startValue, endValue) , mFullPath(ptr) , mPropertyId(propertyId) { mEvaluator.reset(new FloatEvaluator()); }; void setFraction(float fraction) override; + private: VectorDrawable::FullPath* mFullPath; int mPropertyId; @@ -134,12 +135,12 @@ private: class ANDROID_API PathDataPropertyValuesHolder : public PropertyValuesHolderImpl<PathData> { public: PathDataPropertyValuesHolder(VectorDrawable::Path* ptr, PathData* startValue, - PathData* endValue) - : PropertyValuesHolderImpl(*startValue, *endValue) - , mPath(ptr) { + PathData* endValue) + : PropertyValuesHolderImpl(*startValue, *endValue), mPath(ptr) { mEvaluator.reset(new PathEvaluator()); }; void setFraction(float fraction) override; + private: VectorDrawable::Path* mPath; PathData mPathData; @@ -148,11 +149,11 @@ private: class ANDROID_API RootAlphaPropertyValuesHolder : public PropertyValuesHolderImpl<float> { public: RootAlphaPropertyValuesHolder(VectorDrawable::Tree* tree, float startValue, float endValue) - : PropertyValuesHolderImpl(startValue, endValue) - , mTree(tree) { + : PropertyValuesHolderImpl(startValue, endValue), mTree(tree) { mEvaluator.reset(new FloatEvaluator()); } void setFraction(float fraction) override; + private: VectorDrawable::Tree* mTree; }; diff --git a/libs/hwui/Readback.h b/libs/hwui/Readback.h index 7cf426228af6..ad3a8b690617 100644 --- a/libs/hwui/Readback.h +++ b/libs/hwui/Readback.h @@ -16,8 +16,8 @@ #pragma once -#include "renderthread/RenderThread.h" #include "Rect.h" +#include "renderthread/RenderThread.h" #include <SkBitmap.h> @@ -41,8 +41,7 @@ public: /** * Copies the surface's most recently queued buffer into the provided bitmap. */ - virtual CopyResult copySurfaceInto(Surface& surface, const Rect& srcRect, - SkBitmap* bitmap) = 0; + virtual CopyResult copySurfaceInto(Surface& surface, const Rect& srcRect, SkBitmap* bitmap) = 0; virtual CopyResult copyGraphicBufferInto(GraphicBuffer* graphicBuffer, SkBitmap* bitmap) = 0; protected: @@ -52,5 +51,5 @@ protected: renderthread::RenderThread& mRenderThread; }; -} // namespace uirenderer -} // namespace android +} // namespace uirenderer +} // namespace android diff --git a/libs/hwui/RecordedOp.h b/libs/hwui/RecordedOp.h index 3b87aef942b6..2d0185aaa9e2 100644 --- a/libs/hwui/RecordedOp.h +++ b/libs/hwui/RecordedOp.h @@ -16,15 +16,15 @@ #pragma once -#include "font/FontUtil.h" #include "GlLayer.h" #include "Matrix.h" #include "Rect.h" #include "RenderNode.h" #include "TessellationCache.h" +#include "Vector.h" +#include "font/FontUtil.h" #include "utils/LinearAllocator.h" #include "utils/PaintUtils.h" -#include "Vector.h" #include <androidfw/ResourceTypes.h> @@ -73,39 +73,40 @@ class Tree; * MERGEABLE - These ops can be recorded into DisplayLists and rendered individually, or merged * under certain circumstances. */ -#define MAP_OPS_BASED_ON_TYPE(PRE_RENDER_OP_FN, RENDER_ONLY_OP_FN, UNMERGEABLE_OP_FN, MERGEABLE_OP_FN) \ - PRE_RENDER_OP_FN(RenderNodeOp) \ - PRE_RENDER_OP_FN(CirclePropsOp) \ - PRE_RENDER_OP_FN(RoundRectPropsOp) \ - PRE_RENDER_OP_FN(BeginLayerOp) \ - PRE_RENDER_OP_FN(EndLayerOp) \ - PRE_RENDER_OP_FN(BeginUnclippedLayerOp) \ - PRE_RENDER_OP_FN(EndUnclippedLayerOp) \ - PRE_RENDER_OP_FN(VectorDrawableOp) \ - \ - RENDER_ONLY_OP_FN(ShadowOp) \ - RENDER_ONLY_OP_FN(LayerOp) \ - RENDER_ONLY_OP_FN(CopyToLayerOp) \ - RENDER_ONLY_OP_FN(CopyFromLayerOp) \ - \ - UNMERGEABLE_OP_FN(ArcOp) \ - UNMERGEABLE_OP_FN(BitmapMeshOp) \ - UNMERGEABLE_OP_FN(BitmapRectOp) \ - UNMERGEABLE_OP_FN(ColorOp) \ - UNMERGEABLE_OP_FN(FunctorOp) \ - UNMERGEABLE_OP_FN(LinesOp) \ - UNMERGEABLE_OP_FN(OvalOp) \ - UNMERGEABLE_OP_FN(PathOp) \ - UNMERGEABLE_OP_FN(PointsOp) \ - UNMERGEABLE_OP_FN(RectOp) \ - UNMERGEABLE_OP_FN(RoundRectOp) \ - UNMERGEABLE_OP_FN(SimpleRectsOp) \ - UNMERGEABLE_OP_FN(TextOnPathOp) \ - UNMERGEABLE_OP_FN(TextureLayerOp) \ - \ - MERGEABLE_OP_FN(BitmapOp) \ - MERGEABLE_OP_FN(PatchOp) \ - MERGEABLE_OP_FN(TextOp) +#define MAP_OPS_BASED_ON_TYPE(PRE_RENDER_OP_FN, RENDER_ONLY_OP_FN, UNMERGEABLE_OP_FN, \ + MERGEABLE_OP_FN) \ + PRE_RENDER_OP_FN(RenderNodeOp) \ + PRE_RENDER_OP_FN(CirclePropsOp) \ + PRE_RENDER_OP_FN(RoundRectPropsOp) \ + PRE_RENDER_OP_FN(BeginLayerOp) \ + PRE_RENDER_OP_FN(EndLayerOp) \ + PRE_RENDER_OP_FN(BeginUnclippedLayerOp) \ + PRE_RENDER_OP_FN(EndUnclippedLayerOp) \ + PRE_RENDER_OP_FN(VectorDrawableOp) \ + \ + RENDER_ONLY_OP_FN(ShadowOp) \ + RENDER_ONLY_OP_FN(LayerOp) \ + RENDER_ONLY_OP_FN(CopyToLayerOp) \ + RENDER_ONLY_OP_FN(CopyFromLayerOp) \ + \ + UNMERGEABLE_OP_FN(ArcOp) \ + UNMERGEABLE_OP_FN(BitmapMeshOp) \ + UNMERGEABLE_OP_FN(BitmapRectOp) \ + UNMERGEABLE_OP_FN(ColorOp) \ + UNMERGEABLE_OP_FN(FunctorOp) \ + UNMERGEABLE_OP_FN(LinesOp) \ + UNMERGEABLE_OP_FN(OvalOp) \ + UNMERGEABLE_OP_FN(PathOp) \ + UNMERGEABLE_OP_FN(PointsOp) \ + UNMERGEABLE_OP_FN(RectOp) \ + UNMERGEABLE_OP_FN(RoundRectOp) \ + UNMERGEABLE_OP_FN(SimpleRectsOp) \ + UNMERGEABLE_OP_FN(TextOnPathOp) \ + UNMERGEABLE_OP_FN(TextureLayerOp) \ + \ + MERGEABLE_OP_FN(BitmapOp) \ + MERGEABLE_OP_FN(PatchOp) \ + MERGEABLE_OP_FN(TextOp) /** * LUT generators, which will insert nullptr for unsupported ops @@ -113,16 +114,16 @@ class Tree; #define NULLPTR_OP_FN(Type) nullptr, #define BUILD_DEFERRABLE_OP_LUT(OP_FN) \ - { MAP_OPS_BASED_ON_TYPE(OP_FN, NULLPTR_OP_FN, OP_FN, OP_FN) } + { MAP_OPS_BASED_ON_TYPE(OP_FN, NULLPTR_OP_FN, OP_FN, OP_FN) } #define BUILD_MERGEABLE_OP_LUT(OP_FN) \ - { MAP_OPS_BASED_ON_TYPE(NULLPTR_OP_FN, NULLPTR_OP_FN, NULLPTR_OP_FN, OP_FN) } + { MAP_OPS_BASED_ON_TYPE(NULLPTR_OP_FN, NULLPTR_OP_FN, NULLPTR_OP_FN, OP_FN) } #define BUILD_RENDERABLE_OP_LUT(OP_FN) \ - { MAP_OPS_BASED_ON_TYPE(NULLPTR_OP_FN, OP_FN, OP_FN, OP_FN) } + { MAP_OPS_BASED_ON_TYPE(NULLPTR_OP_FN, OP_FN, OP_FN, OP_FN) } #define BUILD_FULL_OP_LUT(OP_FN) \ - { MAP_OPS_BASED_ON_TYPE(OP_FN, OP_FN, OP_FN, OP_FN) } + { MAP_OPS_BASED_ON_TYPE(OP_FN, OP_FN, OP_FN, OP_FN) } /** * Op mapping functions, which skip unsupported ops. @@ -131,30 +132,29 @@ class Tree; */ #define NULL_OP_FN(Type) -#define MAP_DEFERRABLE_OPS(OP_FN) \ - MAP_OPS_BASED_ON_TYPE(OP_FN, NULL_OP_FN, OP_FN, OP_FN) +#define MAP_DEFERRABLE_OPS(OP_FN) MAP_OPS_BASED_ON_TYPE(OP_FN, NULL_OP_FN, OP_FN, OP_FN) -#define MAP_MERGEABLE_OPS(OP_FN) \ - MAP_OPS_BASED_ON_TYPE(NULL_OP_FN, NULL_OP_FN, NULL_OP_FN, OP_FN) +#define MAP_MERGEABLE_OPS(OP_FN) MAP_OPS_BASED_ON_TYPE(NULL_OP_FN, NULL_OP_FN, NULL_OP_FN, OP_FN) -#define MAP_RENDERABLE_OPS(OP_FN) \ - MAP_OPS_BASED_ON_TYPE(NULL_OP_FN, OP_FN, OP_FN, OP_FN) +#define MAP_RENDERABLE_OPS(OP_FN) MAP_OPS_BASED_ON_TYPE(NULL_OP_FN, OP_FN, OP_FN, OP_FN) // Generate OpId enum #define IDENTITY_FN(Type) Type, namespace RecordedOpId { - enum { - MAP_OPS_BASED_ON_TYPE(IDENTITY_FN, IDENTITY_FN, IDENTITY_FN, IDENTITY_FN) - Count, - }; +enum { + MAP_OPS_BASED_ON_TYPE(IDENTITY_FN, IDENTITY_FN, IDENTITY_FN, IDENTITY_FN) Count, +}; } -static_assert(RecordedOpId::RenderNodeOp == 0, - "First index must be zero for LUTs to work"); +static_assert(RecordedOpId::RenderNodeOp == 0, "First index must be zero for LUTs to work"); -#define BASE_PARAMS const Rect& unmappedBounds, const Matrix4& localMatrix, const ClipBase* localClip, const SkPaint* paint -#define BASE_PARAMS_PAINTLESS const Rect& unmappedBounds, const Matrix4& localMatrix, const ClipBase* localClip +#define BASE_PARAMS \ + const Rect &unmappedBounds, const Matrix4 &localMatrix, const ClipBase *localClip, \ + const SkPaint *paint +#define BASE_PARAMS_PAINTLESS \ + const Rect &unmappedBounds, const Matrix4 &localMatrix, const ClipBase *localClip #define SUPER(Type) RecordedOp(RecordedOpId::Type, unmappedBounds, localMatrix, localClip, paint) -#define SUPER_PAINTLESS(Type) RecordedOp(RecordedOpId::Type, unmappedBounds, localMatrix, localClip, nullptr) +#define SUPER_PAINTLESS(Type) \ + RecordedOp(RecordedOpId::Type, unmappedBounds, localMatrix, localClip, nullptr) struct RecordedOp { /* ID from RecordedOpId - generally used for jumping into function tables */ @@ -171,6 +171,7 @@ struct RecordedOp { /* optional paint, stored in base object to simplify merging logic */ const SkPaint* paint; + protected: RecordedOp(unsigned int opId, BASE_PARAMS) : opId(opId) @@ -182,9 +183,8 @@ protected: struct RenderNodeOp : RecordedOp { RenderNodeOp(BASE_PARAMS_PAINTLESS, RenderNode* renderNode) - : SUPER_PAINTLESS(RenderNodeOp) - , renderNode(renderNode) {} - RenderNode * renderNode; // not const, since drawing modifies it + : SUPER_PAINTLESS(RenderNodeOp), renderNode(renderNode) {} + RenderNode* renderNode; // not const, since drawing modifies it /** * Holds the transformation between the projection surface ViewGroup and this RenderNode @@ -204,25 +204,20 @@ struct RenderNodeOp : RecordedOp { struct ArcOp : RecordedOp { ArcOp(BASE_PARAMS, float startAngle, float sweepAngle, bool useCenter) - : SUPER(ArcOp) - , startAngle(startAngle) - , sweepAngle(sweepAngle) - , useCenter(useCenter) {} + : SUPER(ArcOp), startAngle(startAngle), sweepAngle(sweepAngle), useCenter(useCenter) {} const float startAngle; const float sweepAngle; const bool useCenter; }; struct BitmapOp : RecordedOp { - BitmapOp(BASE_PARAMS, Bitmap* bitmap) - : SUPER(BitmapOp) - , bitmap(bitmap) {} + BitmapOp(BASE_PARAMS, Bitmap* bitmap) : SUPER(BitmapOp), bitmap(bitmap) {} Bitmap* bitmap; }; struct BitmapMeshOp : RecordedOp { - BitmapMeshOp(BASE_PARAMS, Bitmap* bitmap, int meshWidth, int meshHeight, - const float* vertices, const int* colors) + BitmapMeshOp(BASE_PARAMS, Bitmap* bitmap, int meshWidth, int meshHeight, const float* vertices, + const int* colors) : SUPER(BitmapMeshOp) , bitmap(bitmap) , meshWidth(meshWidth) @@ -238,16 +233,14 @@ struct BitmapMeshOp : RecordedOp { struct BitmapRectOp : RecordedOp { BitmapRectOp(BASE_PARAMS, Bitmap* bitmap, const Rect& src) - : SUPER(BitmapRectOp) - , bitmap(bitmap) - , src(src) {} + : SUPER(BitmapRectOp), bitmap(bitmap), src(src) {} Bitmap* bitmap; const Rect src; }; struct CirclePropsOp : RecordedOp { CirclePropsOp(const Matrix4& localMatrix, const ClipBase* localClip, const SkPaint* paint, - float* x, float* y, float* radius) + float* x, float* y, float* radius) : RecordedOp(RecordedOpId::CirclePropsOp, Rect(), localMatrix, localClip, paint) , x(x) , y(y) @@ -278,60 +271,47 @@ struct FunctorOp : RecordedOp { struct LinesOp : RecordedOp { LinesOp(BASE_PARAMS, const float* points, const int floatCount) - : SUPER(LinesOp) - , points(points) - , floatCount(floatCount) {} + : SUPER(LinesOp), points(points), floatCount(floatCount) {} const float* points; const int floatCount; }; struct OvalOp : RecordedOp { - OvalOp(BASE_PARAMS) - : SUPER(OvalOp) {} + OvalOp(BASE_PARAMS) : SUPER(OvalOp) {} }; struct PatchOp : RecordedOp { PatchOp(BASE_PARAMS, Bitmap* bitmap, const Res_png_9patch* patch) - : SUPER(PatchOp) - , bitmap(bitmap) - , patch(patch) {} + : SUPER(PatchOp), bitmap(bitmap), patch(patch) {} Bitmap* bitmap; const Res_png_9patch* patch; }; struct PathOp : RecordedOp { - PathOp(BASE_PARAMS, const SkPath* path) - : SUPER(PathOp) - , path(path) {} + PathOp(BASE_PARAMS, const SkPath* path) : SUPER(PathOp), path(path) {} const SkPath* path; }; struct PointsOp : RecordedOp { PointsOp(BASE_PARAMS, const float* points, const int floatCount) - : SUPER(PointsOp) - , points(points) - , floatCount(floatCount) {} + : SUPER(PointsOp), points(points), floatCount(floatCount) {} const float* points; const int floatCount; }; struct RectOp : RecordedOp { - RectOp(BASE_PARAMS) - : SUPER(RectOp) {} + RectOp(BASE_PARAMS) : SUPER(RectOp) {} }; struct RoundRectOp : RecordedOp { - RoundRectOp(BASE_PARAMS, float rx, float ry) - : SUPER(RoundRectOp) - , rx(rx) - , ry(ry) {} + RoundRectOp(BASE_PARAMS, float rx, float ry) : SUPER(RoundRectOp), rx(rx), ry(ry) {} const float rx; const float ry; }; struct RoundRectPropsOp : RecordedOp { RoundRectPropsOp(const Matrix4& localMatrix, const ClipBase* localClip, const SkPaint* paint, - float* left, float* top, float* right, float* bottom, float *rx, float *ry) + float* left, float* top, float* right, float* bottom, float* rx, float* ry) : RecordedOp(RecordedOpId::RoundRectPropsOp, Rect(), localMatrix, localClip, paint) , left(left) , top(top) @@ -349,8 +329,7 @@ struct RoundRectPropsOp : RecordedOp { struct VectorDrawableOp : RecordedOp { VectorDrawableOp(VectorDrawable::Tree* tree, BASE_PARAMS_PAINTLESS) - : SUPER_PAINTLESS(VectorDrawableOp) - , vectorDrawable(tree) {} + : SUPER_PAINTLESS(VectorDrawableOp), vectorDrawable(tree) {} VectorDrawable::Tree* vectorDrawable; }; @@ -366,24 +345,21 @@ struct ShadowOp : RecordedOp { ShadowOp(sp<TessellationCache::ShadowTask>& shadowTask, float casterAlpha) : RecordedOp(RecordedOpId::ShadowOp, Rect(), Matrix4::identity(), nullptr, nullptr) , shadowTask(shadowTask) - , casterAlpha(casterAlpha) { - }; + , casterAlpha(casterAlpha){}; sp<TessellationCache::ShadowTask> shadowTask; const float casterAlpha; }; -struct SimpleRectsOp : RecordedOp { // Filled, no AA (TODO: better name?) +struct SimpleRectsOp : RecordedOp { // Filled, no AA (TODO: better name?) SimpleRectsOp(BASE_PARAMS, Vertex* vertices, size_t vertexCount) - : SUPER(SimpleRectsOp) - , vertices(vertices) - , vertexCount(vertexCount) {} + : SUPER(SimpleRectsOp), vertices(vertices), vertexCount(vertexCount) {} Vertex* vertices; const size_t vertexCount; }; struct TextOp : RecordedOp { - TextOp(BASE_PARAMS, const glyph_t* glyphs, const float* positions, int glyphCount, - float x, float y) + TextOp(BASE_PARAMS, const glyph_t* glyphs, const float* positions, int glyphCount, float x, + float y) : SUPER(TextOp) , glyphs(glyphs) , positions(positions) @@ -400,7 +376,8 @@ struct TextOp : RecordedOp { struct TextOnPathOp : RecordedOp { // TODO: explicitly define bounds TextOnPathOp(const Matrix4& localMatrix, const ClipBase* localClip, const SkPaint* paint, - const glyph_t* glyphs, int glyphCount, const SkPath* path, float hOffset, float vOffset) + const glyph_t* glyphs, int glyphCount, const SkPath* path, float hOffset, + float vOffset) : RecordedOp(RecordedOpId::TextOnPathOp, Rect(), localMatrix, localClip, paint) , glyphs(glyphs) , glyphCount(glyphCount) @@ -417,16 +394,13 @@ struct TextOnPathOp : RecordedOp { struct TextureLayerOp : RecordedOp { TextureLayerOp(BASE_PARAMS_PAINTLESS, DeferredLayerUpdater* layer) - : SUPER_PAINTLESS(TextureLayerOp) - , layerHandle(layer) {} + : SUPER_PAINTLESS(TextureLayerOp), layerHandle(layer) {} // Copy an existing TextureLayerOp, replacing the underlying matrix TextureLayerOp(const TextureLayerOp& op, const Matrix4& replacementMatrix) : RecordedOp(RecordedOpId::TextureLayerOp, op.unmappedBounds, replacementMatrix, - op.localClip, op.paint) - , layerHandle(op.layerHandle) { - - } + op.localClip, op.paint) + , layerHandle(op.layerHandle) {} DeferredLayerUpdater* layerHandle; }; @@ -439,8 +413,7 @@ struct TextureLayerOp : RecordedOp { * and that commands following will render into it. */ struct BeginLayerOp : RecordedOp { - BeginLayerOp(BASE_PARAMS) - : SUPER(BeginLayerOp) {} + BeginLayerOp(BASE_PARAMS) : SUPER(BeginLayerOp) {} }; /** @@ -455,22 +428,20 @@ struct EndLayerOp : RecordedOp { }; struct BeginUnclippedLayerOp : RecordedOp { - BeginUnclippedLayerOp(BASE_PARAMS) - : SUPER(BeginUnclippedLayerOp) {} + BeginUnclippedLayerOp(BASE_PARAMS) : SUPER(BeginUnclippedLayerOp) {} }; struct EndUnclippedLayerOp : RecordedOp { EndUnclippedLayerOp() - : RecordedOp(RecordedOpId::EndUnclippedLayerOp, Rect(), Matrix4::identity(), nullptr, nullptr) {} + : RecordedOp(RecordedOpId::EndUnclippedLayerOp, Rect(), Matrix4::identity(), nullptr, + nullptr) {} }; struct CopyToLayerOp : RecordedOp { CopyToLayerOp(const RecordedOp& op, OffscreenBuffer** layerHandle) - : RecordedOp(RecordedOpId::CopyToLayerOp, - op.unmappedBounds, - op.localMatrix, - nullptr, // clip intentionally ignored - op.paint) + : RecordedOp(RecordedOpId::CopyToLayerOp, op.unmappedBounds, op.localMatrix, + nullptr, // clip intentionally ignored + op.paint) , layerHandle(layerHandle) {} // Records a handle to the Layer object, since the Layer itself won't be @@ -478,15 +449,12 @@ struct CopyToLayerOp : RecordedOp { OffscreenBuffer** layerHandle; }; - // draw the parameter layer underneath struct CopyFromLayerOp : RecordedOp { CopyFromLayerOp(const RecordedOp& op, OffscreenBuffer** layerHandle) - : RecordedOp(RecordedOpId::CopyFromLayerOp, - op.unmappedBounds, - op.localMatrix, - nullptr, // clip intentionally ignored - op.paint) + : RecordedOp(RecordedOpId::CopyFromLayerOp, op.unmappedBounds, op.localMatrix, + nullptr, // clip intentionally ignored + op.paint) , layerHandle(layerHandle) {} // Records a handle to the Layer object, since the Layer itself won't be @@ -510,7 +478,8 @@ struct LayerOp : RecordedOp { , colorFilter(paint ? paint->getColorFilter() : nullptr) {} explicit LayerOp(RenderNode& node) - : RecordedOp(RecordedOpId::LayerOp, Rect(node.getWidth(), node.getHeight()), Matrix4::identity(), nullptr, nullptr) + : RecordedOp(RecordedOpId::LayerOp, Rect(node.getWidth(), node.getHeight()), + Matrix4::identity(), nullptr, nullptr) , layerHandle(node.getLayerHandle()) , alpha(node.properties().layerProperties().alpha() / 255.0f) , mode(node.properties().layerProperties().xferMode()) @@ -527,5 +496,5 @@ struct LayerOp : RecordedOp { SkColorFilter* colorFilter; }; -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp index d966372a7699..e1df1e7725b5 100644 --- a/libs/hwui/RecordingCanvas.cpp +++ b/libs/hwui/RecordingCanvas.cpp @@ -26,19 +26,16 @@ namespace android { namespace uirenderer { RecordingCanvas::RecordingCanvas(size_t width, size_t height) - : mState(*this) - , mResourceCache(ResourceCache::getInstance()) { + : mState(*this), mResourceCache(ResourceCache::getInstance()) { resetRecording(width, height); } RecordingCanvas::~RecordingCanvas() { - LOG_ALWAYS_FATAL_IF(mDisplayList, - "Destroyed a RecordingCanvas during a record!"); + LOG_ALWAYS_FATAL_IF(mDisplayList, "Destroyed a RecordingCanvas during a record!"); } void RecordingCanvas::resetRecording(int width, int height, RenderNode* node) { - LOG_ALWAYS_FATAL_IF(mDisplayList, - "prepareDirty called a second time during a recording!"); + LOG_ALWAYS_FATAL_IF(mDisplayList, "prepareDirty called a second time during a recording!"); mDisplayList = new DisplayList(); mState.initializeRecordingSaveStack(width, height); @@ -68,8 +65,7 @@ void RecordingCanvas::insertReorderBarrier(bool enableReorder) { } SkCanvas* RecordingCanvas::asSkCanvas() { - LOG_ALWAYS_FATAL_IF(!mDisplayList, - "attempting to get an SkCanvas when we are not recording!"); + LOG_ALWAYS_FATAL_IF(!mDisplayList, "attempting to get an SkCanvas when we are not recording!"); if (!mSkiaCanvasProxy) { mSkiaCanvasProxy.reset(new SkiaCanvasProxy(this)); } @@ -88,8 +84,7 @@ SkCanvas* RecordingCanvas::asSkCanvas() { // CanvasStateClient implementation // ---------------------------------------------------------------------------- -void RecordingCanvas::onViewportInitialized() { -} +void RecordingCanvas::onViewportInitialized() {} void RecordingCanvas::onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) { if (removed.flags & Snapshot::kFlagIsFboLayer) { @@ -104,7 +99,7 @@ void RecordingCanvas::onSnapshotRestored(const Snapshot& removed, const Snapshot // ---------------------------------------------------------------------------- // Save (layer) int RecordingCanvas::save(SaveFlags::Flags flags) { - return mState.save((int) flags); + return mState.save((int)flags); } void RecordingCanvas::RecordingCanvas::restore() { @@ -116,7 +111,7 @@ void RecordingCanvas::restoreToCount(int saveCount) { } int RecordingCanvas::saveLayer(float left, float top, float right, float bottom, - const SkPaint* paint, SaveFlags::Flags flags) { + const SkPaint* paint, SaveFlags::Flags flags) { // force matrix/clip isolation for layer flags |= SaveFlags::MatrixClip; bool clippedLayer = flags & SaveFlags::ClipToLayer; @@ -134,9 +129,8 @@ int RecordingCanvas::saveLayer(float left, float top, float right, float bottom, Rect visibleBounds = unmappedBounds; previous.transform->mapRect(visibleBounds); - if (CC_UNLIKELY(!clippedLayer - && previous.transform->rectToRect() - && visibleBounds.contains(previous.getRenderTargetClip()))) { + if (CC_UNLIKELY(!clippedLayer && previous.transform->rectToRect() && + visibleBounds.contains(previous.getRenderTargetClip()))) { // unlikely case where an unclipped savelayer is recorded with a clip it can use, // as none of its unaffected/unclipped area is visible clippedLayer = true; @@ -157,18 +151,18 @@ int RecordingCanvas::saveLayer(float left, float top, float right, float bottom, layerBounds.doIntersect(unmappedBounds); } - int saveValue = mState.save((int) flags); + int saveValue = mState.save((int)flags); Snapshot& snapshot = *mState.writableSnapshot(); // layerBounds is in original bounds space, but clipped by current recording clip if (!layerBounds.isEmpty() && !unmappedBounds.isEmpty()) { if (CC_LIKELY(clippedLayer)) { - auto previousClip = getRecordedClip(); // capture before new snapshot clip has changed + auto previousClip = getRecordedClip(); // capture before new snapshot clip has changed if (addOp(alloc().create_trivial<BeginLayerOp>( - unmappedBounds, - *previous.transform, // transform to *draw* with - previousClip, // clip to *draw* with - refPaint(paint))) >= 0) { + unmappedBounds, + *previous.transform, // transform to *draw* with + previousClip, // clip to *draw* with + refPaint(paint))) >= 0) { snapshot.flags |= Snapshot::kFlagIsLayer | Snapshot::kFlagIsFboLayer; snapshot.initializeViewport(unmappedBounds.getWidth(), unmappedBounds.getHeight()); snapshot.transform->loadTranslate(-unmappedBounds.left, -unmappedBounds.top, 0.0f); @@ -181,10 +175,8 @@ int RecordingCanvas::saveLayer(float left, float top, float right, float bottom, } } else { if (addOp(alloc().create_trivial<BeginUnclippedLayerOp>( - unmappedBounds, - *mState.currentSnapshot()->transform, - getRecordedClip(), - refPaint(paint))) >= 0) { + unmappedBounds, *mState.currentSnapshot()->transform, getRecordedClip(), + refPaint(paint))) >= 0) { snapshot.flags |= Snapshot::kFlagIsLayer; return saveValue; } @@ -245,10 +237,7 @@ bool RecordingCanvas::clipPath(const SkPath* path, SkClipOp op) { // android/graphics/Canvas draw operations // ---------------------------------------------------------------------------- void RecordingCanvas::drawColor(int color, SkBlendMode mode) { - addOp(alloc().create_trivial<ColorOp>( - getRecordedClip(), - color, - mode)); + addOp(alloc().create_trivial<ColorOp>(getRecordedClip(), color, mode)); } void RecordingCanvas::drawPaint(const SkPaint& paint) { @@ -269,40 +258,35 @@ static Rect calcBoundsOfPoints(const float* points, int floatCount) { // Geometry void RecordingCanvas::drawPoints(const float* points, int floatCount, const SkPaint& paint) { if (CC_UNLIKELY(floatCount < 2 || paint.nothingToDraw())) return; - floatCount &= ~0x1; // round down to nearest two + floatCount &= ~0x1; // round down to nearest two addOp(alloc().create_trivial<PointsOp>( - calcBoundsOfPoints(points, floatCount), - *mState.currentSnapshot()->transform, - getRecordedClip(), - refPaint(&paint), refBuffer<float>(points, floatCount), floatCount)); + calcBoundsOfPoints(points, floatCount), *mState.currentSnapshot()->transform, + getRecordedClip(), refPaint(&paint), refBuffer<float>(points, floatCount), floatCount)); } void RecordingCanvas::drawLines(const float* points, int floatCount, const SkPaint& paint) { if (CC_UNLIKELY(floatCount < 4 || paint.nothingToDraw())) return; - floatCount &= ~0x3; // round down to nearest four + floatCount &= ~0x3; // round down to nearest four addOp(alloc().create_trivial<LinesOp>( - calcBoundsOfPoints(points, floatCount), - *mState.currentSnapshot()->transform, - getRecordedClip(), - refPaint(&paint), refBuffer<float>(points, floatCount), floatCount)); + calcBoundsOfPoints(points, floatCount), *mState.currentSnapshot()->transform, + getRecordedClip(), refPaint(&paint), refBuffer<float>(points, floatCount), floatCount)); } -void RecordingCanvas::drawRect(float left, float top, float right, float bottom, const SkPaint& paint) { +void RecordingCanvas::drawRect(float left, float top, float right, float bottom, + const SkPaint& paint) { if (CC_UNLIKELY(paint.nothingToDraw())) return; - addOp(alloc().create_trivial<RectOp>( - Rect(left, top, right, bottom), - *(mState.currentSnapshot()->transform), - getRecordedClip(), - refPaint(&paint))); + addOp(alloc().create_trivial<RectOp>(Rect(left, top, right, bottom), + *(mState.currentSnapshot()->transform), getRecordedClip(), + refPaint(&paint))); } void RecordingCanvas::drawSimpleRects(const float* rects, int vertexCount, const SkPaint* paint) { if (rects == nullptr) return; - Vertex* rectData = (Vertex*) mDisplayList->allocator.create_trivial_array<Vertex>(vertexCount); + Vertex* rectData = (Vertex*)mDisplayList->allocator.create_trivial_array<Vertex>(vertexCount); Vertex* vertex = rectData; float left = FLT_MAX; @@ -326,17 +310,15 @@ void RecordingCanvas::drawSimpleRects(const float* rects, int vertexCount, const bottom = std::max(bottom, b); } addOp(alloc().create_trivial<SimpleRectsOp>( - Rect(left, top, right, bottom), - *(mState.currentSnapshot()->transform), - getRecordedClip(), - refPaint(paint), rectData, vertexCount)); + Rect(left, top, right, bottom), *(mState.currentSnapshot()->transform), + getRecordedClip(), refPaint(paint), rectData, vertexCount)); } void RecordingCanvas::drawRegion(const SkRegion& region, const SkPaint& paint) { if (CC_UNLIKELY(paint.nothingToDraw())) return; - if (paint.getStyle() == SkPaint::kFill_Style - && (!paint.isAntiAlias() || mState.currentTransform()->isSimple())) { + if (paint.getStyle() == SkPaint::kFill_Style && + (!paint.isAntiAlias() || mState.currentTransform()->isSimple())) { int count = 0; Vector<float> rects; SkRegion::Iterator it(region); @@ -360,26 +342,23 @@ void RecordingCanvas::drawRegion(const SkRegion& region, const SkPaint& paint) { } } -void RecordingCanvas::drawRoundRect(float left, float top, float right, float bottom, - float rx, float ry, const SkPaint& paint) { +void RecordingCanvas::drawRoundRect(float left, float top, float right, float bottom, float rx, + float ry, const SkPaint& paint) { if (CC_UNLIKELY(paint.nothingToDraw())) return; if (CC_LIKELY(MathUtils::isPositive(rx) || MathUtils::isPositive(ry))) { - addOp(alloc().create_trivial<RoundRectOp>( - Rect(left, top, right, bottom), - *(mState.currentSnapshot()->transform), - getRecordedClip(), - refPaint(&paint), rx, ry)); + addOp(alloc().create_trivial<RoundRectOp>(Rect(left, top, right, bottom), + *(mState.currentSnapshot()->transform), + getRecordedClip(), refPaint(&paint), rx, ry)); } else { drawRect(left, top, right, bottom, paint); } } -void RecordingCanvas::drawRoundRect( - CanvasPropertyPrimitive* left, CanvasPropertyPrimitive* top, - CanvasPropertyPrimitive* right, CanvasPropertyPrimitive* bottom, - CanvasPropertyPrimitive* rx, CanvasPropertyPrimitive* ry, - CanvasPropertyPaint* paint) { +void RecordingCanvas::drawRoundRect(CanvasPropertyPrimitive* left, CanvasPropertyPrimitive* top, + CanvasPropertyPrimitive* right, CanvasPropertyPrimitive* bottom, + CanvasPropertyPrimitive* rx, CanvasPropertyPrimitive* ry, + CanvasPropertyPaint* paint) { mDisplayList->ref(left); mDisplayList->ref(top); mDisplayList->ref(right); @@ -389,11 +368,8 @@ void RecordingCanvas::drawRoundRect( mDisplayList->ref(paint); refBitmapsInShader(paint->value.getShader()); addOp(alloc().create_trivial<RoundRectPropsOp>( - *(mState.currentSnapshot()->transform), - getRecordedClip(), - &paint->value, - &left->value, &top->value, &right->value, &bottom->value, - &rx->value, &ry->value)); + *(mState.currentSnapshot()->transform), getRecordedClip(), &paint->value, &left->value, + &top->value, &right->value, &bottom->value, &rx->value, &ry->value)); } void RecordingCanvas::drawCircle(float x, float y, float radius, const SkPaint& paint) { @@ -403,65 +379,54 @@ void RecordingCanvas::drawCircle(float x, float y, float radius, const SkPaint& drawOval(x - radius, y - radius, x + radius, y + radius, paint); } -void RecordingCanvas::drawCircle( - CanvasPropertyPrimitive* x, CanvasPropertyPrimitive* y, - CanvasPropertyPrimitive* radius, CanvasPropertyPaint* paint) { +void RecordingCanvas::drawCircle(CanvasPropertyPrimitive* x, CanvasPropertyPrimitive* y, + CanvasPropertyPrimitive* radius, CanvasPropertyPaint* paint) { mDisplayList->ref(x); mDisplayList->ref(y); mDisplayList->ref(radius); mDisplayList->ref(paint); refBitmapsInShader(paint->value.getShader()); - addOp(alloc().create_trivial<CirclePropsOp>( - *(mState.currentSnapshot()->transform), - getRecordedClip(), - &paint->value, - &x->value, &y->value, &radius->value)); + addOp(alloc().create_trivial<CirclePropsOp>(*(mState.currentSnapshot()->transform), + getRecordedClip(), &paint->value, &x->value, + &y->value, &radius->value)); } -void RecordingCanvas::drawOval(float left, float top, float right, float bottom, const SkPaint& paint) { +void RecordingCanvas::drawOval(float left, float top, float right, float bottom, + const SkPaint& paint) { if (CC_UNLIKELY(paint.nothingToDraw())) return; - addOp(alloc().create_trivial<OvalOp>( - Rect(left, top, right, bottom), - *(mState.currentSnapshot()->transform), - getRecordedClip(), - refPaint(&paint))); + addOp(alloc().create_trivial<OvalOp>(Rect(left, top, right, bottom), + *(mState.currentSnapshot()->transform), getRecordedClip(), + refPaint(&paint))); } -void RecordingCanvas::drawArc(float left, float top, float right, float bottom, - float startAngle, float sweepAngle, bool useCenter, const SkPaint& paint) { +void RecordingCanvas::drawArc(float left, float top, float right, float bottom, float startAngle, + float sweepAngle, bool useCenter, const SkPaint& paint) { if (CC_UNLIKELY(paint.nothingToDraw())) return; if (fabs(sweepAngle) >= 360.0f) { drawOval(left, top, right, bottom, paint); } else { addOp(alloc().create_trivial<ArcOp>( - Rect(left, top, right, bottom), - *(mState.currentSnapshot()->transform), - getRecordedClip(), - refPaint(&paint), - startAngle, sweepAngle, useCenter)); + Rect(left, top, right, bottom), *(mState.currentSnapshot()->transform), + getRecordedClip(), refPaint(&paint), startAngle, sweepAngle, useCenter)); } } void RecordingCanvas::drawPath(const SkPath& path, const SkPaint& paint) { if (CC_UNLIKELY(paint.nothingToDraw())) return; - addOp(alloc().create_trivial<PathOp>( - Rect(path.getBounds()), - *(mState.currentSnapshot()->transform), - getRecordedClip(), - refPaint(&paint), refPath(&path))); + addOp(alloc().create_trivial<PathOp>(Rect(path.getBounds()), + *(mState.currentSnapshot()->transform), getRecordedClip(), + refPaint(&paint), refPath(&path))); } void RecordingCanvas::drawVectorDrawable(VectorDrawableRoot* tree) { mDisplayList->ref(tree); mDisplayList->vectorDrawables.push_back(tree); addOp(alloc().create_trivial<VectorDrawableOp>( - tree, - Rect(tree->stagingProperties()->getBounds()), - *(mState.currentSnapshot()->transform), - getRecordedClip())); + tree, Rect(tree->stagingProperties()->getBounds()), + *(mState.currentSnapshot()->transform), getRecordedClip())); } // Bitmap-based @@ -472,20 +437,19 @@ void RecordingCanvas::drawBitmap(Bitmap& bitmap, float left, float top, const Sk restore(); } -void RecordingCanvas::drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, - const SkPaint* paint) { +void RecordingCanvas::drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, const SkPaint* paint) { if (matrix.isIdentity()) { drawBitmap(bitmap, paint); - } else if (!(matrix.getType() & ~(SkMatrix::kScale_Mask | SkMatrix::kTranslate_Mask)) - && MathUtils::isPositive(matrix.getScaleX()) - && MathUtils::isPositive(matrix.getScaleY())) { + } else if (!(matrix.getType() & ~(SkMatrix::kScale_Mask | SkMatrix::kTranslate_Mask)) && + MathUtils::isPositive(matrix.getScaleX()) && + MathUtils::isPositive(matrix.getScaleY())) { // SkMatrix::isScaleTranslate() not available in L SkRect src; SkRect dst; bitmap.getBounds(&src); matrix.mapRect(&dst, src); - drawBitmap(bitmap, src.fLeft, src.fTop, src.fRight, src.fBottom, - dst.fLeft, dst.fTop, dst.fRight, dst.fBottom, paint); + drawBitmap(bitmap, src.fLeft, src.fTop, src.fRight, src.fBottom, dst.fLeft, dst.fTop, + dst.fRight, dst.fBottom, paint); } else { save(SaveFlags::Matrix); concat(matrix); @@ -494,14 +458,11 @@ void RecordingCanvas::drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, } } -void RecordingCanvas::drawBitmap(Bitmap& bitmap, float srcLeft, float srcTop, - float srcRight, float srcBottom, float dstLeft, float dstTop, - float dstRight, float dstBottom, const SkPaint* paint) { - if (srcLeft == 0 && srcTop == 0 - && srcRight == bitmap.width() - && srcBottom == bitmap.height() - && (srcBottom - srcTop == dstBottom - dstTop) - && (srcRight - srcLeft == dstRight - dstLeft)) { +void RecordingCanvas::drawBitmap(Bitmap& bitmap, float srcLeft, float srcTop, float srcRight, + float srcBottom, float dstLeft, float dstTop, float dstRight, + float dstBottom, const SkPaint* paint) { + if (srcLeft == 0 && srcTop == 0 && srcRight == bitmap.width() && srcBottom == bitmap.height() && + (srcBottom - srcTop == dstBottom - dstTop) && (srcRight - srcLeft == dstRight - dstLeft)) { // transform simple rect to rect drawing case into position bitmap ops, since they merge save(SaveFlags::Matrix); translate(dstLeft, dstTop); @@ -509,56 +470,55 @@ void RecordingCanvas::drawBitmap(Bitmap& bitmap, float srcLeft, float srcTop, restore(); } else { addOp(alloc().create_trivial<BitmapRectOp>( - Rect(dstLeft, dstTop, dstRight, dstBottom), - *(mState.currentSnapshot()->transform), - getRecordedClip(), - refPaint(paint), refBitmap(bitmap), + Rect(dstLeft, dstTop, dstRight, dstBottom), *(mState.currentSnapshot()->transform), + getRecordedClip(), refPaint(paint), refBitmap(bitmap), Rect(srcLeft, srcTop, srcRight, srcBottom))); } } void RecordingCanvas::drawBitmapMesh(Bitmap& bitmap, int meshWidth, int meshHeight, - const float* vertices, const int* colors, const SkPaint* paint) { + const float* vertices, const int* colors, + const SkPaint* paint) { int vertexCount = (meshWidth + 1) * (meshHeight + 1); addOp(alloc().create_trivial<BitmapMeshOp>( - calcBoundsOfPoints(vertices, vertexCount * 2), - *(mState.currentSnapshot()->transform), - getRecordedClip(), - refPaint(paint), refBitmap(bitmap), meshWidth, meshHeight, - refBuffer<float>(vertices, vertexCount * 2), // 2 floats per vertex - refBuffer<int>(colors, vertexCount))); // 1 color per vertex + calcBoundsOfPoints(vertices, vertexCount * 2), *(mState.currentSnapshot()->transform), + getRecordedClip(), refPaint(paint), refBitmap(bitmap), meshWidth, meshHeight, + refBuffer<float>(vertices, vertexCount * 2), // 2 floats per vertex + refBuffer<int>(colors, vertexCount))); // 1 color per vertex } void RecordingCanvas::drawNinePatch(Bitmap& bitmap, const android::Res_png_9patch& patch, - float dstLeft, float dstTop, float dstRight, float dstBottom, - const SkPaint* paint) { - addOp(alloc().create_trivial<PatchOp>( - Rect(dstLeft, dstTop, dstRight, dstBottom), - *(mState.currentSnapshot()->transform), - getRecordedClip(), - refPaint(paint), refBitmap(bitmap), refPatch(&patch))); + float dstLeft, float dstTop, float dstRight, float dstBottom, + const SkPaint* paint) { + addOp(alloc().create_trivial<PatchOp>(Rect(dstLeft, dstTop, dstRight, dstBottom), + *(mState.currentSnapshot()->transform), getRecordedClip(), + refPaint(paint), refBitmap(bitmap), refPatch(&patch))); +} + +double RecordingCanvas::drawAnimatedImage(AnimatedImageDrawable*) { + // Unimplemented + return 0; } // Text void RecordingCanvas::drawGlyphs(ReadGlyphFunc glyphFunc, int glyphCount, const SkPaint& paint, - float x, float y, float boundsLeft, float boundsTop, float boundsRight, float boundsBottom, - float totalAdvance) { + float x, float y, float boundsLeft, float boundsTop, + float boundsRight, float boundsBottom, float totalAdvance) { if (glyphCount <= 0 || paint.nothingToDraw()) return; uint16_t* glyphs = (glyph_t*)alloc().alloc<glyph_t>(glyphCount * sizeof(glyph_t)); float* positions = (float*)alloc().alloc<float>(2 * glyphCount * sizeof(float)); glyphFunc(glyphs, positions); // TODO: either must account for text shadow in bounds, or record separate ops for text shadows - addOp(alloc().create_trivial<TextOp>( - Rect(boundsLeft, boundsTop, boundsRight, boundsBottom), - *(mState.currentSnapshot()->transform), - getRecordedClip(), - refPaint(&paint), glyphs, positions, glyphCount, x, y)); + addOp(alloc().create_trivial<TextOp>(Rect(boundsLeft, boundsTop, boundsRight, boundsBottom), + *(mState.currentSnapshot()->transform), getRecordedClip(), + refPaint(&paint), glyphs, positions, glyphCount, x, y)); drawTextDecorations(x, y, totalAdvance, paint); } void RecordingCanvas::drawLayoutOnPath(const minikin::Layout& layout, float hOffset, float vOffset, - const SkPaint& paint, const SkPath& path, size_t start, size_t end) { + const SkPaint& paint, const SkPath& path, size_t start, + size_t end) { uint16_t glyphs[1]; for (size_t i = start; i < end; i++) { glyphs[0] = layout.getGlyphId(i); @@ -566,28 +526,23 @@ void RecordingCanvas::drawLayoutOnPath(const minikin::Layout& layout, float hOff float y = vOffset + layout.getY(i); if (paint.nothingToDraw()) return; const uint16_t* tempGlyphs = refBuffer<glyph_t>(glyphs, 1); - addOp(alloc().create_trivial<TextOnPathOp>( - *(mState.currentSnapshot()->transform), - getRecordedClip(), - refPaint(&paint), tempGlyphs, 1, refPath(&path), x, y)); + addOp(alloc().create_trivial<TextOnPathOp>(*(mState.currentSnapshot()->transform), + getRecordedClip(), refPaint(&paint), tempGlyphs, + 1, refPath(&path), x, y)); } } void RecordingCanvas::drawBitmap(Bitmap& bitmap, const SkPaint* paint) { - addOp(alloc().create_trivial<BitmapOp>( - Rect(bitmap.width(), bitmap.height()), - *(mState.currentSnapshot()->transform), - getRecordedClip(), - refPaint(paint), refBitmap(bitmap))); + addOp(alloc().create_trivial<BitmapOp>(Rect(bitmap.width(), bitmap.height()), + *(mState.currentSnapshot()->transform), + getRecordedClip(), refPaint(paint), refBitmap(bitmap))); } void RecordingCanvas::drawRenderNode(RenderNode* renderNode) { auto&& stagingProps = renderNode->stagingProperties(); RenderNodeOp* op = alloc().create_trivial<RenderNodeOp>( Rect(stagingProps.getWidth(), stagingProps.getHeight()), - *(mState.currentSnapshot()->transform), - getRecordedClip(), - renderNode); + *(mState.currentSnapshot()->transform), getRecordedClip(), renderNode); int opIndex = addOp(op); if (CC_LIKELY(opIndex >= 0)) { int childIndex = mDisplayList->addChild(op); @@ -612,18 +567,14 @@ void RecordingCanvas::drawLayer(DeferredLayerUpdater* layerHandle) { // its width, height, transform, etc...! addOp(alloc().create_trivial<TextureLayerOp>( Rect(layerHandle->getWidth(), layerHandle->getHeight()), - *(mState.currentSnapshot()->transform), - getRecordedClip(), layerHandle)); + *(mState.currentSnapshot()->transform), getRecordedClip(), layerHandle)); } -void RecordingCanvas::callDrawGLFunction(Functor* functor, - GlFunctorLifecycleListener* listener) { +void RecordingCanvas::callDrawGLFunction(Functor* functor, GlFunctorLifecycleListener* listener) { mDisplayList->functors.push_back({functor, listener}); mDisplayList->ref(listener); - addOp(alloc().create_trivial<FunctorOp>( - *(mState.currentSnapshot()->transform), - getRecordedClip(), - functor)); + addOp(alloc().create_trivial<FunctorOp>(*(mState.currentSnapshot()->transform), + getRecordedClip(), functor)); } int RecordingCanvas::addOp(RecordedOp* op) { @@ -676,5 +627,5 @@ void RecordingCanvas::refBitmapsInShader(const SkShader* shader) { } } -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h index ccdb4b0c1d8e..e663402a80f3 100644 --- a/libs/hwui/RecordingCanvas.h +++ b/libs/hwui/RecordingCanvas.h @@ -40,49 +40,50 @@ struct ClipBase; class DeferredLayerUpdater; struct RecordedOp; -class ANDROID_API RecordingCanvas: public Canvas, public CanvasStateClient { +class ANDROID_API RecordingCanvas : public Canvas, public CanvasStateClient { enum class DeferredBarrierType { None, InOrder, OutOfOrder, }; + public: RecordingCanvas(size_t width, size_t height); virtual ~RecordingCanvas(); virtual void resetRecording(int width, int height, RenderNode* node = nullptr) override; virtual WARN_UNUSED_RESULT DisplayList* finishRecording() override; -// ---------------------------------------------------------------------------- -// MISC HWUI OPERATIONS - TODO: CATEGORIZE -// ---------------------------------------------------------------------------- + // ---------------------------------------------------------------------------- + // MISC HWUI OPERATIONS - TODO: CATEGORIZE + // ---------------------------------------------------------------------------- virtual void insertReorderBarrier(bool enableReorder) override; virtual void drawLayer(DeferredLayerUpdater* layerHandle) override; virtual void drawRenderNode(RenderNode* renderNode) override; virtual void callDrawGLFunction(Functor* functor, - GlFunctorLifecycleListener* listener) override; + GlFunctorLifecycleListener* listener) override; -// ---------------------------------------------------------------------------- -// CanvasStateClient interface -// ---------------------------------------------------------------------------- + // ---------------------------------------------------------------------------- + // CanvasStateClient interface + // ---------------------------------------------------------------------------- virtual void onViewportInitialized() override; virtual void onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) override; virtual GLuint getTargetFbo() const override { return -1; } -// ---------------------------------------------------------------------------- -// HWUI Canvas draw operations -// ---------------------------------------------------------------------------- + // ---------------------------------------------------------------------------- + // HWUI Canvas draw operations + // ---------------------------------------------------------------------------- virtual void drawRoundRect(CanvasPropertyPrimitive* left, CanvasPropertyPrimitive* top, - CanvasPropertyPrimitive* right, CanvasPropertyPrimitive* bottom, - CanvasPropertyPrimitive* rx, CanvasPropertyPrimitive* ry, - CanvasPropertyPaint* paint) override; + CanvasPropertyPrimitive* right, CanvasPropertyPrimitive* bottom, + CanvasPropertyPrimitive* rx, CanvasPropertyPrimitive* ry, + CanvasPropertyPaint* paint) override; virtual void drawCircle(CanvasPropertyPrimitive* x, CanvasPropertyPrimitive* y, - CanvasPropertyPrimitive* radius, CanvasPropertyPaint* paint) override; + CanvasPropertyPrimitive* radius, CanvasPropertyPaint* paint) override; -// ---------------------------------------------------------------------------- -// android/graphics/Canvas interface -// ---------------------------------------------------------------------------- + // ---------------------------------------------------------------------------- + // android/graphics/Canvas interface + // ---------------------------------------------------------------------------- virtual SkCanvas* asSkCanvas() override; virtual void setBitmap(const SkBitmap& bitmap) override { @@ -93,14 +94,9 @@ public: virtual int width() override { return mState.getWidth(); } virtual int height() override { return mState.getHeight(); } - virtual void setHighContrastText(bool highContrastText) override { - mHighContrastText = highContrastText; - } - virtual bool isHighContrastText() override { return mHighContrastText; } - -// ---------------------------------------------------------------------------- -// android/graphics/Canvas state operations -// ---------------------------------------------------------------------------- + // ---------------------------------------------------------------------------- + // android/graphics/Canvas state operations + // ---------------------------------------------------------------------------- // Save (layer) virtual int getSaveCount() const override { return mState.getSaveCount(); } virtual int save(SaveFlags::Flags flags) override; @@ -108,9 +104,9 @@ public: virtual void restoreToCount(int saveCount) override; virtual int saveLayer(float left, float top, float right, float bottom, const SkPaint* paint, - SaveFlags::Flags flags) override; - virtual int saveLayerAlpha(float left, float top, float right, float bottom, - int alpha, SaveFlags::Flags flags) override { + SaveFlags::Flags flags) override; + virtual int saveLayerAlpha(float left, float top, float right, float bottom, int alpha, + SaveFlags::Flags flags) override { SkPaint paint; paint.setAlpha(alpha); return saveLayer(left, top, right, bottom, &paint, flags); @@ -131,8 +127,7 @@ public: virtual bool quickRejectRect(float left, float top, float right, float bottom) const override; virtual bool quickRejectPath(const SkPath& path) const override; - virtual bool clipRect(float left, float top, float right, float bottom, - SkClipOp op) override; + virtual bool clipRect(float left, float top, float right, float bottom, SkClipOp op) override; virtual bool clipPath(const SkPath* path, SkClipOp op) override; // Misc @@ -141,59 +136,65 @@ public: mDrawFilter.reset(SkSafeRef(filter)); } -// ---------------------------------------------------------------------------- -// android/graphics/Canvas draw operations -// ---------------------------------------------------------------------------- + // ---------------------------------------------------------------------------- + // android/graphics/Canvas draw operations + // ---------------------------------------------------------------------------- virtual void drawColor(int color, SkBlendMode mode) override; virtual void drawPaint(const SkPaint& paint) override; // Geometry virtual void drawPoint(float x, float y, const SkPaint& paint) override { - float points[2] = { x, y }; + float points[2] = {x, y}; drawPoints(points, 2, paint); } virtual void drawPoints(const float* points, int floatCount, const SkPaint& paint) override; virtual void drawLine(float startX, float startY, float stopX, float stopY, - const SkPaint& paint) override { - float points[4] = { startX, startY, stopX, stopY }; + const SkPaint& paint) override { + float points[4] = {startX, startY, stopX, stopY}; drawLines(points, 4, paint); } virtual void drawLines(const float* points, int floatCount, const SkPaint& paint) override; - virtual void drawRect(float left, float top, float right, float bottom, const SkPaint& paint) override; + virtual void drawRect(float left, float top, float right, float bottom, + const SkPaint& paint) override; virtual void drawRegion(const SkRegion& region, const SkPaint& paint) override; - virtual void drawRoundRect(float left, float top, float right, float bottom, - float rx, float ry, const SkPaint& paint) override; + virtual void drawRoundRect(float left, float top, float right, float bottom, float rx, float ry, + const SkPaint& paint) override; virtual void drawCircle(float x, float y, float radius, const SkPaint& paint) override; - virtual void drawOval(float left, float top, float right, float bottom, const SkPaint& paint) override; - virtual void drawArc(float left, float top, float right, float bottom, - float startAngle, float sweepAngle, bool useCenter, const SkPaint& paint) override; + virtual void drawOval(float left, float top, float right, float bottom, + const SkPaint& paint) override; + virtual void drawArc(float left, float top, float right, float bottom, float startAngle, + float sweepAngle, bool useCenter, const SkPaint& paint) override; virtual void drawPath(const SkPath& path, const SkPaint& paint) override; - virtual void drawVertices(const SkVertices*, SkBlendMode, const SkPaint& paint) override - { /* RecordingCanvas does not support drawVertices(); ignore */ } + virtual void drawVertices(const SkVertices*, SkBlendMode, const SkPaint& paint) + override { /* RecordingCanvas does not support drawVertices(); ignore */ + } virtual void drawVectorDrawable(VectorDrawableRoot* tree) override; // Bitmap-based virtual void drawBitmap(Bitmap& bitmap, float left, float top, const SkPaint* paint) override; virtual void drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, const SkPaint* paint) override; - virtual void drawBitmap(Bitmap& bitmap, float srcLeft, float srcTop, - float srcRight, float srcBottom, float dstLeft, float dstTop, - float dstRight, float dstBottom, const SkPaint* paint) override; + virtual void drawBitmap(Bitmap& bitmap, float srcLeft, float srcTop, float srcRight, + float srcBottom, float dstLeft, float dstTop, float dstRight, + float dstBottom, const SkPaint* paint) override; virtual void drawBitmapMesh(Bitmap& bitmap, int meshWidth, int meshHeight, - const float* vertices, const int* colors, const SkPaint* paint) override; - virtual void drawNinePatch(Bitmap& bitmap, const android::Res_png_9patch& chunk, - float dstLeft, float dstTop, float dstRight, float dstBottom, - const SkPaint* paint) override; + const float* vertices, const int* colors, + const SkPaint* paint) override; + virtual void drawNinePatch(Bitmap& bitmap, const android::Res_png_9patch& chunk, float dstLeft, + float dstTop, float dstRight, float dstBottom, + const SkPaint* paint) override; + virtual double drawAnimatedImage(AnimatedImageDrawable*) override; // Text virtual bool drawTextAbsolutePos() const override { return false; } protected: virtual void drawGlyphs(ReadGlyphFunc glyphFunc, int count, const SkPaint& paint, float x, - float y, float boundsLeft, float boundsTop, float boundsRight, float boundsBottom, - float totalAdvance) override; + float y, float boundsLeft, float boundsTop, float boundsRight, + float boundsBottom, float totalAdvance) override; virtual void drawLayoutOnPath(const minikin::Layout& layout, float hOffset, float vOffset, - const SkPaint& paint, const SkPath& path, size_t start, size_t end) override; + const SkPaint& paint, const SkPath& path, size_t start, + size_t end) override; private: const ClipBase* getRecordedClip() { @@ -203,20 +204,19 @@ private: void drawBitmap(Bitmap& bitmap, const SkPaint* paint); void drawSimpleRects(const float* rects, int vertexCount, const SkPaint* paint); - int addOp(RecordedOp* op); -// ---------------------------------------------------------------------------- -// lazy object copy -// ---------------------------------------------------------------------------- + // ---------------------------------------------------------------------------- + // lazy object copy + // ---------------------------------------------------------------------------- LinearAllocator& alloc() { return mDisplayList->allocator; } void refBitmapsInShader(const SkShader* shader); - template<class T> + template <class T> inline const T* refBuffer(const T* srcBuffer, int32_t count) { if (!srcBuffer) return nullptr; - T* dstBuffer = (T*) mDisplayList->allocator.alloc<T>(count * sizeof(T)); + T* dstBuffer = (T*)mDisplayList->allocator.alloc<T>(count * sizeof(T)); memcpy(dstBuffer, srcBuffer, count * sizeof(T)); return dstBuffer; } @@ -311,11 +311,10 @@ private: DeferredBarrierType mDeferredBarrierType = DeferredBarrierType::None; const ClipBase* mDeferredBarrierClip = nullptr; DisplayList* mDisplayList = nullptr; - bool mHighContrastText = false; sk_sp<SkDrawFilter> mDrawFilter; -}; // class RecordingCanvas +}; // class RecordingCanvas -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android -#endif // ANDROID_HWUI_RECORDING_CANVAS_H +#endif // ANDROID_HWUI_RECORDING_CANVAS_H diff --git a/libs/hwui/Rect.h b/libs/hwui/Rect.h index eb05e9171335..0715187e19ea 100644 --- a/libs/hwui/Rect.h +++ b/libs/hwui/Rect.h @@ -20,20 +20,18 @@ #include <utils/Log.h> +#include <SkRect.h> #include <algorithm> #include <cmath> #include <iomanip> #include <ostream> -#include <SkRect.h> namespace android { namespace uirenderer { #define RECT_STRING "%5.2f %5.2f %5.2f %5.2f" -#define RECT_ARGS(r) \ - (r).left, (r).top, (r).right, (r).bottom -#define SK_RECT_ARGS(r) \ - (r).left(), (r).top(), (r).right(), (r).bottom() +#define RECT_ARGS(r) (r).left, (r).top, (r).right, (r).bottom +#define SK_RECT_ARGS(r) (r).left(), (r).top(), (r).right(), (r).bottom() /////////////////////////////////////////////////////////////////////////////// // Structs @@ -52,52 +50,32 @@ public: // we don't provide copy-ctor and operator= on purpose // because we want the compiler generated versions - inline Rect(): - left(0), - top(0), - right(0), - bottom(0) { - } + inline Rect() : left(0), top(0), right(0), bottom(0) {} - inline Rect(float left, float top, float right, float bottom): - left(left), - top(top), - right(right), - bottom(bottom) { - } + inline Rect(float left, float top, float right, float bottom) + : left(left), top(top), right(right), bottom(bottom) {} - inline Rect(float width, float height): - left(0.0f), - top(0.0f), - right(width), - bottom(height) { - } + inline Rect(float width, float height) : left(0.0f), top(0.0f), right(width), bottom(height) {} - inline Rect(const SkIRect& rect): // NOLINT, implicit - left(rect.fLeft), - top(rect.fTop), - right(rect.fRight), - bottom(rect.fBottom) { - } + inline Rect(const SkIRect& rect) + : // NOLINT, implicit + left(rect.fLeft) + , top(rect.fTop) + , right(rect.fRight) + , bottom(rect.fBottom) {} - inline Rect(const SkRect& rect): // NOLINT, implicit - left(rect.fLeft), - top(rect.fTop), - right(rect.fRight), - bottom(rect.fBottom) { - } + inline Rect(const SkRect& rect) + : // NOLINT, implicit + left(rect.fLeft) + , top(rect.fTop) + , right(rect.fRight) + , bottom(rect.fBottom) {} - friend int operator==(const Rect& a, const Rect& b) { - return !memcmp(&a, &b, sizeof(a)); - } + friend int operator==(const Rect& a, const Rect& b) { return !memcmp(&a, &b, sizeof(a)); } - friend int operator!=(const Rect& a, const Rect& b) { - return memcmp(&a, &b, sizeof(a)); - } + friend int operator!=(const Rect& a, const Rect& b) { return memcmp(&a, &b, sizeof(a)); } - inline void clear() { - left = top = right = bottom = 0.0f; - } + inline void clear() { left = top = right = bottom = 0.0f; } inline bool isEmpty() const { // this is written in such way this it'll handle NANs to return @@ -105,9 +83,7 @@ public: return !((left < right) && (top < bottom)); } - inline void setEmpty() { - left = top = right = bottom = 0.0f; - } + inline void setEmpty() { left = top = right = bottom = 0.0f; } inline void set(float left, float top, float right, float bottom) { this->left = left; @@ -116,21 +92,13 @@ public: this->bottom = bottom; } - inline void set(const Rect& r) { - set(r.left, r.top, r.right, r.bottom); - } + inline void set(const Rect& r) { set(r.left, r.top, r.right, r.bottom); } - inline void set(const SkIRect& r) { - set(r.left(), r.top(), r.right(), r.bottom()); - } + inline void set(const SkIRect& r) { set(r.left(), r.top(), r.right(), r.bottom()); } - inline float getWidth() const { - return right - left; - } + inline float getWidth() const { return right - left; } - inline float getHeight() const { - return bottom - top; - } + inline float getHeight() const { return bottom - top; } bool intersects(float l, float t, float r, float b) const { float tempLeft = std::max(left, l); @@ -138,12 +106,10 @@ public: float tempRight = std::min(right, r); float tempBottom = std::min(bottom, b); - return ((tempLeft < tempRight) && (tempTop < tempBottom)); // !isEmpty + return ((tempLeft < tempRight) && (tempTop < tempBottom)); // !isEmpty } - bool intersects(const Rect& r) const { - return intersects(r.left, r.top, r.right, r.bottom); - } + bool intersects(const Rect& r) const { return intersects(r.left, r.top, r.right, r.bottom); } /** * This method is named 'doIntersect' instead of 'intersect' so as not to be confused with @@ -157,17 +123,13 @@ public: bottom = std::min(bottom, b); } - void doIntersect(const Rect& r) { - doIntersect(r.left, r.top, r.right, r.bottom); - } + void doIntersect(const Rect& r) { doIntersect(r.left, r.top, r.right, r.bottom); } inline bool contains(float l, float t, float r, float b) const { return l >= left && t >= top && r <= right && b <= bottom; } - inline bool contains(const Rect& r) const { - return contains(r.left, r.top, r.right, r.bottom); - } + inline bool contains(const Rect& r) const { return contains(r.left, r.top, r.right, r.bottom); } bool unionWith(const Rect& r) { if (r.left < r.right && r.top < r.bottom) { @@ -195,9 +157,7 @@ public: bottom += dy; } - void inset(float delta) { - outset(-delta); - } + void inset(float delta) { outset(-delta); } void outset(float delta) { left -= delta; @@ -279,13 +239,9 @@ public: bottom = std::max(bottom, y); } - SkRect toSkRect() const { - return SkRect::MakeLTRB(left, top, right, bottom); - } + SkRect toSkRect() const { return SkRect::MakeLTRB(left, top, right, bottom); } - SkIRect toSkIRect() const { - return SkIRect::MakeLTRB(left, top, right, bottom); - } + SkIRect toSkIRect() const { return SkIRect::MakeLTRB(left, top, right, bottom); } void dump(const char* label = nullptr) const { ALOGD("%s[l=%.2f t=%.2f r=%.2f b=%.2f]", label ? label : "Rect", left, top, right, bottom); @@ -301,13 +257,10 @@ public: return os << "[" << rect.right << " x " << rect.bottom << "]"; } - return os << "[" << rect.left - << " " << rect.top - << " " << rect.right - << " " << rect.bottom << "]"; + return os << "[" << rect.left << " " << rect.top << " " << rect.right << " " << rect.bottom + << "]"; } -}; // class Rect - -}; // namespace uirenderer -}; // namespace android +}; // class Rect +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/RenderBuffer.h b/libs/hwui/RenderBuffer.h index 35a516a58022..191a66a6d741 100644 --- a/libs/hwui/RenderBuffer.h +++ b/libs/hwui/RenderBuffer.h @@ -32,9 +32,8 @@ struct RenderBuffer { * Creates a new render buffer in the specified format and dimensions. * The format must be one of the formats allowed by glRenderbufferStorage(). */ - RenderBuffer(GLenum format, uint32_t width, uint32_t height): - mFormat(format), mWidth(width), mHeight(height), mAllocated(false) { - + RenderBuffer(GLenum format, uint32_t width, uint32_t height) + : mFormat(format), mWidth(width), mHeight(height), mAllocated(false) { glGenRenderbuffers(1, &mName); } @@ -47,31 +46,23 @@ struct RenderBuffer { /** * Returns the GL name of this render buffer. */ - GLuint getName() const { - return mName; - } + GLuint getName() const { return mName; } /** * Returns the format of this render buffer. */ - GLenum getFormat() const { - return mFormat; - } + GLenum getFormat() const { return mFormat; } /** * Binds this render buffer to the current GL context. */ - void bind() const { - glBindRenderbuffer(GL_RENDERBUFFER, mName); - } + void bind() const { glBindRenderbuffer(GL_RENDERBUFFER, mName); } /** * Indicates whether this render buffer has allocated its * storage. See allocate() and resize(). */ - bool isAllocated() const { - return mAllocated; - } + bool isAllocated() const { return mAllocated; } /** * Allocates this render buffer's storage if needed. @@ -101,23 +92,19 @@ struct RenderBuffer { /** * Returns the width of the render buffer in pixels. */ - uint32_t getWidth() const { - return mWidth; - } + uint32_t getWidth() const { return mWidth; } /** * Returns the height of the render buffer in pixels. */ - uint32_t getHeight() const { - return mHeight; - } + uint32_t getHeight() const { return mHeight; } /** * Returns the size of this render buffer in bytes. */ uint32_t getSize() const { // Round to the nearest byte - return (uint32_t) ((mWidth * mHeight * formatSize(mFormat)) / 8.0f + 0.5f); + return (uint32_t)((mWidth * mHeight * formatSize(mFormat)) / 8.0f + 0.5f); } /** @@ -186,9 +173,9 @@ private: bool mAllocated; GLuint mName; -}; // struct RenderBuffer +}; // struct RenderBuffer -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android -#endif // ANDROID_HWUI_RENDER_BUFFER_H +#endif // ANDROID_HWUI_RENDER_BUFFER_H diff --git a/libs/hwui/RenderBufferCache.cpp b/libs/hwui/RenderBufferCache.cpp index 2f8ddfe6d660..98010d8da1bd 100644 --- a/libs/hwui/RenderBufferCache.cpp +++ b/libs/hwui/RenderBufferCache.cpp @@ -14,10 +14,10 @@ * limitations under the License. */ -#include "Debug.h" -#include "Properties.h" #include "RenderBufferCache.h" +#include "Debug.h" #include "DeviceInfo.h" +#include "Properties.h" #include <utils/Log.h> @@ -32,9 +32,9 @@ namespace uirenderer { // Debug #if DEBUG_RENDER_BUFFERS - #define RENDER_BUFFER_LOGD(...) ALOGD(__VA_ARGS__) +#define RENDER_BUFFER_LOGD(...) ALOGD(__VA_ARGS__) #else - #define RENDER_BUFFER_LOGD(...) +#define RENDER_BUFFER_LOGD(...) #endif static uint32_t calculateRboCacheSize() { @@ -48,9 +48,7 @@ static uint32_t calculateRboCacheSize() { // Constructors/destructor /////////////////////////////////////////////////////////////////////////////// -RenderBufferCache::RenderBufferCache() - : mSize(0) - , mMaxSize(calculateRboCacheSize()) {} +RenderBufferCache::RenderBufferCache() : mSize(0), mMaxSize(calculateRboCacheSize()) {} RenderBufferCache::~RenderBufferCache() { clear(); @@ -72,9 +70,8 @@ uint32_t RenderBufferCache::getMaxSize() { // Caching /////////////////////////////////////////////////////////////////////////////// -int RenderBufferCache::RenderBufferEntry::compare( - const RenderBufferCache::RenderBufferEntry& lhs, - const RenderBufferCache::RenderBufferEntry& rhs) { +int RenderBufferCache::RenderBufferEntry::compare(const RenderBufferCache::RenderBufferEntry& lhs, + const RenderBufferCache::RenderBufferEntry& rhs) { int deltaInt = int(lhs.mWidth) - int(rhs.mWidth); if (deltaInt != 0) return deltaInt; @@ -87,8 +84,8 @@ int RenderBufferCache::RenderBufferEntry::compare( void RenderBufferCache::deleteBuffer(RenderBuffer* buffer) { if (buffer) { RENDER_BUFFER_LOGD("Deleted %s render buffer (%dx%d)", - RenderBuffer::formatName(buffer->getFormat()), - buffer->getWidth(), buffer->getHeight()); + RenderBuffer::formatName(buffer->getFormat()), buffer->getWidth(), + buffer->getHeight()); mSize -= buffer->getSize(); delete buffer; @@ -115,13 +112,13 @@ RenderBuffer* RenderBufferCache::get(GLenum format, const uint32_t width, const buffer = entry.mBuffer; mSize -= buffer->getSize(); - RENDER_BUFFER_LOGD("Found %s render buffer (%dx%d)", - RenderBuffer::formatName(format), width, height); + RENDER_BUFFER_LOGD("Found %s render buffer (%dx%d)", RenderBuffer::formatName(format), + width, height); } else { buffer = new RenderBuffer(format, width, height); - RENDER_BUFFER_LOGD("Created new %s render buffer (%dx%d)", - RenderBuffer::formatName(format), width, height); + RENDER_BUFFER_LOGD("Created new %s render buffer (%dx%d)", RenderBuffer::formatName(format), + width, height); } buffer->bind(); @@ -147,18 +144,18 @@ bool RenderBufferCache::put(RenderBuffer* buffer) { mSize += size; RENDER_BUFFER_LOGD("Added %s render buffer (%dx%d)", - RenderBuffer::formatName(buffer->getFormat()), - buffer->getWidth(), buffer->getHeight()); + RenderBuffer::formatName(buffer->getFormat()), buffer->getWidth(), + buffer->getHeight()); return true; } else { RENDER_BUFFER_LOGD("Deleted %s render buffer (%dx%d) Size=%d, MaxSize=%d", - RenderBuffer::formatName(buffer->getFormat()), - buffer->getWidth(), buffer->getHeight(), size, mMaxSize); + RenderBuffer::formatName(buffer->getFormat()), buffer->getWidth(), + buffer->getHeight(), size, mMaxSize); delete buffer; } return false; } -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/RenderBufferCache.h b/libs/hwui/RenderBufferCache.h index 644433145cd0..c936a5283965 100644 --- a/libs/hwui/RenderBufferCache.h +++ b/libs/hwui/RenderBufferCache.h @@ -74,28 +74,22 @@ public: private: struct RenderBufferEntry { - RenderBufferEntry(): - mBuffer(nullptr), mWidth(0), mHeight(0) { - } + RenderBufferEntry() : mBuffer(nullptr), mWidth(0), mHeight(0) {} - RenderBufferEntry(GLenum format, const uint32_t width, const uint32_t height): - mBuffer(nullptr), mFormat(format), mWidth(width), mHeight(height) { - } + RenderBufferEntry(GLenum format, const uint32_t width, const uint32_t height) + : mBuffer(nullptr), mFormat(format), mWidth(width), mHeight(height) {} - explicit RenderBufferEntry(RenderBuffer* buffer): - mBuffer(buffer), mFormat(buffer->getFormat()), - mWidth(buffer->getWidth()), mHeight(buffer->getHeight()) { - } + explicit RenderBufferEntry(RenderBuffer* buffer) + : mBuffer(buffer) + , mFormat(buffer->getFormat()) + , mWidth(buffer->getWidth()) + , mHeight(buffer->getHeight()) {} static int compare(const RenderBufferEntry& lhs, const RenderBufferEntry& rhs); - bool operator==(const RenderBufferEntry& other) const { - return compare(*this, other) == 0; - } + bool operator==(const RenderBufferEntry& other) const { return compare(*this, other) == 0; } - bool operator!=(const RenderBufferEntry& other) const { - return compare(*this, other) != 0; - } + bool operator!=(const RenderBufferEntry& other) const { return compare(*this, other) != 0; } bool operator<(const RenderBufferEntry& other) const { return RenderBufferEntry::compare(*this, other) < 0; @@ -105,7 +99,7 @@ private: GLenum mFormat; uint32_t mWidth; uint32_t mHeight; - }; // struct RenderBufferEntry + }; // struct RenderBufferEntry void deleteBuffer(RenderBuffer* buffer); @@ -113,9 +107,9 @@ private: uint32_t mSize; uint32_t mMaxSize; -}; // class RenderBufferCache +}; // class RenderBufferCache -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android -#endif // ANDROID_HWUI_RENDER_BUFFER_CACHE_H +#endif // ANDROID_HWUI_RENDER_BUFFER_CACHE_H diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp index 61b3876cd095..fedcc10abb64 100644 --- a/libs/hwui/RenderNode.cpp +++ b/libs/hwui/RenderNode.cpp @@ -21,17 +21,18 @@ #include "Debug.h" #include "RecordedOp.h" #include "TreeInfo.h" +#include "VectorDrawable.h" +#include "renderstate/RenderState.h" +#include "renderthread/CanvasContext.h" #include "utils/FatVector.h" #include "utils/MathUtils.h" #include "utils/StringUtils.h" #include "utils/TraceUtils.h" -#include "VectorDrawable.h" -#include "renderstate/RenderState.h" -#include "renderthread/CanvasContext.h" -#include "protos/hwui.pb.h" #include "protos/ProtoHelpers.h" +#include "protos/hwui.pb.h" +#include <SkPathOps.h> #include <algorithm> #include <sstream> #include <string> @@ -45,9 +46,7 @@ class ImmediateRemoved : public TreeObserver { public: explicit ImmediateRemoved(TreeInfo* info) : mTreeInfo(info) {} - void onMaybeRemovedFromTree(RenderNode* node) override { - node->onRemovedFromTree(mTreeInfo); - } + void onMaybeRemovedFromTree(RenderNode* node) override { node->onRemovedFromTree(mTreeInfo); } private: TreeInfo* mTreeInfo; @@ -59,8 +58,7 @@ RenderNode::RenderNode() , mDisplayList(nullptr) , mStagingDisplayList(nullptr) , mAnimatorManager(*this) - , mParentCount(0) { -} + , mParentCount(0) {} RenderNode::~RenderNode() { ImmediateRemoved observer(nullptr); @@ -88,12 +86,11 @@ void RenderNode::output() { void RenderNode::output(std::ostream& output, uint32_t level) { output << " (" << getName() << " " << this - << (MathUtils::isZero(properties().getAlpha()) ? ", zero alpha" : "") - << (properties().hasShadow() ? ", casting shadow" : "") - << (isRenderable() ? "" : ", empty") - << (properties().getProjectBackwards() ? ", projected" : "") - << (hasLayer() ? ", on HW Layer" : "") - << ")" << std::endl; + << (MathUtils::isZero(properties().getAlpha()) ? ", zero alpha" : "") + << (properties().hasShadow() ? ", casting shadow" : "") + << (isRenderable() ? "" : ", empty") + << (properties().getProjectBackwards() ? ", projected" : "") + << (hasLayer() ? ", on HW Layer" : "") << ")" << std::endl; properties().debugOutputProperties(output, level + 1); @@ -104,9 +101,8 @@ void RenderNode::output(std::ostream& output, uint32_t level) { output << std::endl; } -void RenderNode::copyTo(proto::RenderNode *pnode) { - pnode->set_id(static_cast<uint64_t>( - reinterpret_cast<uintptr_t>(this))); +void RenderNode::copyTo(proto::RenderNode* pnode) { + pnode->set_id(static_cast<uint64_t>(reinterpret_cast<uintptr_t>(this))); pnode->set_name(mName.string(), mName.length()); proto::RenderProperties* pprops = pnode->mutable_properties(); @@ -238,18 +234,16 @@ void RenderNode::pushLayerUpdate(TreeInfo& info) { LayerType layerType = properties().effectiveLayerType(); // If we are not a layer OR we cannot be rendered (eg, view was detached) // we need to destroy any Layers we may have had previously - if (CC_LIKELY(layerType != LayerType::RenderLayer) - || CC_UNLIKELY(!isRenderable()) - || CC_UNLIKELY(properties().getWidth() == 0) - || CC_UNLIKELY(properties().getHeight() == 0) - || CC_UNLIKELY(!properties().fitsOnLayer())) { + if (CC_LIKELY(layerType != LayerType::RenderLayer) || CC_UNLIKELY(!isRenderable()) || + CC_UNLIKELY(properties().getWidth() == 0) || CC_UNLIKELY(properties().getHeight() == 0) || + CC_UNLIKELY(!properties().fitsOnLayer())) { if (CC_UNLIKELY(hasLayer())) { renderthread::CanvasContext::destroyLayer(this); } return; } - if(info.canvasContext.createOrUpdateLayer(this, *info.damageAccumulator)) { + if (info.canvasContext.createOrUpdateLayer(this, *info.damageAccumulator)) { damageSelf(info); } @@ -260,8 +254,8 @@ void RenderNode::pushLayerUpdate(TreeInfo& info) { err << "Unable to create layer for " << getName(); const int maxTextureSize = Caches::getInstance().maxTextureSize; if (getWidth() > maxTextureSize || getHeight() > maxTextureSize) { - err << ", size " << getWidth() << "x" << getHeight() - << " exceeds max size " << maxTextureSize; + err << ", size " << getWidth() << "x" << getHeight() << " exceeds max size " + << maxTextureSize; } else { err << ", see logcat for more info"; } @@ -305,8 +299,8 @@ void RenderNode::prepareTreeImpl(TreeObserver& observer, TreeInfo& info, bool fu } else if (mDisplayList) { willHaveFunctor = mDisplayList->hasFunctor(); } - bool childFunctorsNeedLayer = mProperties.prepareForFunctorPresence( - willHaveFunctor, functorsNeedLayer); + bool childFunctorsNeedLayer = + mProperties.prepareForFunctorPresence(willHaveFunctor, functorsNeedLayer); if (CC_UNLIKELY(mPositionListener.get())) { mPositionListener->onPositionUpdated(*this, info); @@ -319,10 +313,12 @@ void RenderNode::prepareTreeImpl(TreeObserver& observer, TreeInfo& info, bool fu if (mDisplayList) { info.out.hasFunctors |= mDisplayList->hasFunctor(); - bool isDirty = mDisplayList->prepareListAndChildren(observer, info, childFunctorsNeedLayer, - [](RenderNode* child, TreeObserver& observer, TreeInfo& info, bool functorsNeedLayer) { - child->prepareTreeImpl(observer, info, functorsNeedLayer); - }); + bool isDirty = mDisplayList->prepareListAndChildren( + observer, info, childFunctorsNeedLayer, + [](RenderNode* child, TreeObserver& observer, TreeInfo& info, + bool functorsNeedLayer) { + child->prepareTreeImpl(observer, info, functorsNeedLayer); + }); if (isDirty) { damageSelf(info); } @@ -362,9 +358,7 @@ void RenderNode::syncDisplayList(TreeObserver& observer, TreeInfo* info) { // Make sure we inc first so that we don't fluctuate between 0 and 1, // which would thrash the layer cache if (mStagingDisplayList) { - mStagingDisplayList->updateChildren([](RenderNode* child) { - child->incParentRefCount(); - }); + mStagingDisplayList->updateChildren([](RenderNode* child) { child->incParentRefCount(); }); } deleteDisplayList(observer, info); mDisplayList = mStagingDisplayList; @@ -387,9 +381,8 @@ void RenderNode::pushStagingDisplayListChanges(TreeObserver& observer, TreeInfo& void RenderNode::deleteDisplayList(TreeObserver& observer, TreeInfo* info) { if (mDisplayList) { - mDisplayList->updateChildren([&observer, info](RenderNode* child) { - child->decParentRefCount(observer, info); - }); + mDisplayList->updateChildren( + [&observer, info](RenderNode* child) { child->decParentRefCount(observer, info); }); if (!mDisplayList->reuseDisplayList(this, info ? &info->canvasContext : nullptr)) { delete mDisplayList; } @@ -412,9 +405,7 @@ void RenderNode::destroyLayers() { renderthread::CanvasContext::destroyLayer(this); } if (mDisplayList) { - mDisplayList->updateChildren([](RenderNode* child) { - child->destroyLayers(); - }); + mDisplayList->updateChildren([](RenderNode* child) { child->destroyLayers(); }); } } @@ -460,16 +451,15 @@ void RenderNode::applyViewPropertyTransforms(mat4& matrix, bool true3dTransform) if (properties().hasTransformMatrix() || applyTranslationZ) { if (properties().isTransformTranslateOnly()) { matrix.translate(properties().getTranslationX(), properties().getTranslationY(), - true3dTransform ? properties().getZ() : 0.0f); + true3dTransform ? properties().getZ() : 0.0f); } else { if (!true3dTransform) { matrix.multiply(*properties().getTransformMatrix()); } else { mat4 true3dMat; - true3dMat.loadTranslate( - properties().getPivotX() + properties().getTranslationX(), - properties().getPivotY() + properties().getTranslationY(), - properties().getZ()); + true3dMat.loadTranslate(properties().getPivotX() + properties().getTranslationX(), + properties().getPivotY() + properties().getTranslationY(), + properties().getZ()); true3dMat.rotate(properties().getRotationX(), 1, 0, 0); true3dMat.rotate(properties().getRotationY(), 0, 1, 0); true3dMat.rotate(properties().getRotation(), 0, 0, 1); @@ -504,8 +494,7 @@ void RenderNode::computeOrdering() { } void RenderNode::computeOrderingImpl( - RenderNodeOp* opState, - std::vector<RenderNodeOp*>* compositedChildrenOfProjectionSurface, + RenderNodeOp* opState, std::vector<RenderNodeOp*>* compositedChildrenOfProjectionSurface, const mat4* transformFromProjectionSurface) { mProjectedNodes.clear(); if (mDisplayList == nullptr || mDisplayList->isEmpty()) return; @@ -555,5 +544,22 @@ void RenderNode::computeOrderingImpl( } } +const SkPath* RenderNode::getClippedOutline(const SkRect& clipRect) const { + const SkPath* outlinePath = properties().getOutline().getPath(); + const uint32_t outlineID = outlinePath->getGenerationID(); + + if (outlineID != mClippedOutlineCache.outlineID || clipRect != mClippedOutlineCache.clipRect) { + // update the cache keys + mClippedOutlineCache.outlineID = outlineID; + mClippedOutlineCache.clipRect = clipRect; + + // update the cache value by recomputing a new path + SkPath clipPath; + clipPath.addRect(clipRect); + Op(*outlinePath, clipPath, kIntersect_SkPathOp, &mClippedOutlineCache.clippedOutline); + } + return &mClippedOutlineCache.clippedOutline; +} + } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h index c4ae82af430c..1469a156e2d8 100644 --- a/libs/hwui/RenderNode.h +++ b/libs/hwui/RenderNode.h @@ -63,7 +63,8 @@ class RenderNode; } /** - * Primary class for storing recorded canvas commands, as well as per-View/ViewGroup display properties. + * Primary class for storing recorded canvas commands, as well as per-View/ViewGroup display + * properties. * * Recording of canvas commands is somewhat similar to SkPicture, except the canvas-recording * functionality is split between RecordingCanvas (which manages the recording), DisplayList @@ -74,33 +75,32 @@ class RenderNode; * attached. */ class RenderNode : public VirtualLightRefBase { -friend class TestUtils; // allow TestUtils to access syncDisplayList / syncProperties -friend class FrameBuilder; + friend class TestUtils; // allow TestUtils to access syncDisplayList / syncProperties + friend class FrameBuilder; + public: enum DirtyPropertyMask { - GENERIC = 1 << 1, - TRANSLATION_X = 1 << 2, - TRANSLATION_Y = 1 << 3, - TRANSLATION_Z = 1 << 4, - SCALE_X = 1 << 5, - SCALE_Y = 1 << 6, - ROTATION = 1 << 7, - ROTATION_X = 1 << 8, - ROTATION_Y = 1 << 9, - X = 1 << 10, - Y = 1 << 11, - Z = 1 << 12, - ALPHA = 1 << 13, - DISPLAY_LIST = 1 << 14, + GENERIC = 1 << 1, + TRANSLATION_X = 1 << 2, + TRANSLATION_Y = 1 << 3, + TRANSLATION_Z = 1 << 4, + SCALE_X = 1 << 5, + SCALE_Y = 1 << 6, + ROTATION = 1 << 7, + ROTATION_X = 1 << 8, + ROTATION_Y = 1 << 9, + X = 1 << 10, + Y = 1 << 11, + Z = 1 << 12, + ALPHA = 1 << 13, + DISPLAY_LIST = 1 << 14, }; ANDROID_API RenderNode(); ANDROID_API virtual ~RenderNode(); // See flags defined in DisplayList.java - enum ReplayFlag { - kReplayFlag_ClipChildren = 0x1 - }; + enum ReplayFlag { kReplayFlag_ClipChildren = 0x1 }; ANDROID_API void setStagingDisplayList(DisplayList* newData); @@ -110,17 +110,13 @@ public: ANDROID_API int getDebugSize(); void copyTo(proto::RenderNode* node); - bool isRenderable() const { - return mDisplayList && !mDisplayList->isEmpty(); - } + bool isRenderable() const { return mDisplayList && !mDisplayList->isEmpty(); } bool hasProjectionReceiver() const { return mDisplayList && mDisplayList->projectionReceiveIndex >= 0; } - const char* getName() const { - return mName.string(); - } + const char* getName() const { return mName.string(); } void setName(const char* name) { if (name) { @@ -133,49 +129,29 @@ public: } } - VirtualLightRefBase* getUserContext() const { - return mUserContext.get(); - } + VirtualLightRefBase* getUserContext() const { return mUserContext.get(); } - void setUserContext(VirtualLightRefBase* context) { - mUserContext = context; - } + void setUserContext(VirtualLightRefBase* context) { mUserContext = context; } bool isPropertyFieldDirty(DirtyPropertyMask field) const { return mDirtyPropertyFields & field; } - void setPropertyFieldsDirty(uint32_t fields) { - mDirtyPropertyFields |= fields; - } + void setPropertyFieldsDirty(uint32_t fields) { mDirtyPropertyFields |= fields; } - const RenderProperties& properties() const { - return mProperties; - } + const RenderProperties& properties() const { return mProperties; } - RenderProperties& animatorProperties() { - return mProperties; - } + RenderProperties& animatorProperties() { return mProperties; } - const RenderProperties& stagingProperties() { - return mStagingProperties; - } + const RenderProperties& stagingProperties() { return mStagingProperties; } - RenderProperties& mutateStagingProperties() { - return mStagingProperties; - } + RenderProperties& mutateStagingProperties() { return mStagingProperties; } - bool isValid() { - return mValid; - } + bool isValid() { return mValid; } - int getWidth() const { - return properties().getWidth(); - } + int getWidth() const { return properties().getWidth(); } - int getHeight() const { - return properties().getHeight(); - } + int getHeight() const { return properties().getHeight(); } ANDROID_API virtual void prepareTree(TreeInfo& info); void destroyHardwareResources(TreeInfo* info = nullptr); @@ -196,18 +172,14 @@ public: bool nothingToDraw() const { const Outline& outline = properties().getOutline(); - return mDisplayList == nullptr - || properties().getAlpha() <= 0 - || (outline.getShouldClip() && outline.isEmpty()) - || properties().getScaleX() == 0 - || properties().getScaleY() == 0; + return mDisplayList == nullptr || properties().getAlpha() <= 0 || + (outline.getShouldClip() && outline.isEmpty()) || properties().getScaleX() == 0 || + properties().getScaleY() == 0; } - const DisplayList* getDisplayList() const { - return mDisplayList; - } + const DisplayList* getDisplayList() const { return mDisplayList; } OffscreenBuffer* getLayer() const { return mLayer; } - OffscreenBuffer** getLayerHandle() { return &mLayer; } // ugh... + OffscreenBuffer** getLayerHandle() { return &mLayer; } // ugh... void setLayer(OffscreenBuffer* layer) { mLayer = layer; } // Note: The position callbacks are relying on the listener using @@ -234,16 +206,12 @@ public: // This is only modified in MODE_FULL, so it can be safely accessed // on the UI thread. - ANDROID_API bool hasParents() { - return mParentCount; - } + ANDROID_API bool hasParents() { return mParentCount; } void onRemovedFromTree(TreeInfo* info); // Called by CanvasContext to promote a RenderNode to be a root node - void makeRoot() { - incParentRefCount(); - } + void makeRoot() { incParentRefCount(); } // Called by CanvasContext when it drops a RenderNode from being a root node void clearRoot(); @@ -252,8 +220,8 @@ public: private: void computeOrderingImpl(RenderNodeOp* opState, - std::vector<RenderNodeOp*>* compositedChildrenOfProjectionSurface, - const mat4* transformFromProjectionSurface); + std::vector<RenderNodeOp*>* compositedChildrenOfProjectionSurface, + const mat4* transformFromProjectionSurface); void syncProperties(); void syncDisplayList(TreeObserver& observer, TreeInfo* info); @@ -309,7 +277,7 @@ private: sp<PositionListener> mPositionListener; -// METHODS & FIELDS ONLY USED BY THE SKIA RENDERER + // METHODS & FIELDS ONLY USED BY THE SKIA RENDERER public: /** * Detach and transfer ownership of an already allocated displayList for use @@ -361,9 +329,19 @@ public: return mSkiaLayer.get() ? mSkiaLayer->layerSurface.get() : nullptr; } - skiapipeline::SkiaLayer* getSkiaLayer() const { - return mSkiaLayer.get(); - } + skiapipeline::SkiaLayer* getSkiaLayer() const { return mSkiaLayer.get(); } + + /** + * Returns the path that represents the outline of RenderNode intersected with + * the provided rect. This call will internally cache the resulting path in + * order to potentially return that path for subsequent calls to this method. + * By reusing the same path we get better performance on the GPU backends since + * those resources are cached in the hardware based on the path's genID. + * + * The returned path is only guaranteed to be valid until this function is called + * again or the RenderNode's outline is mutated. + */ + const SkPath* getClippedOutline(const SkRect& clipRect) const; private: /** @@ -380,17 +358,25 @@ private: * when it has been set to draw as a LayerType::RenderLayer. */ std::unique_ptr<skiapipeline::SkiaLayer> mSkiaLayer; -}; // class RenderNode + + struct ClippedOutlineCache { + // keys + uint32_t outlineID = 0; + SkRect clipRect; + + // value + SkPath clippedOutline; + }; + mutable ClippedOutlineCache mClippedOutlineCache; +}; // class RenderNode class MarkAndSweepRemoved : public TreeObserver { -PREVENT_COPY_AND_ASSIGN(MarkAndSweepRemoved); + PREVENT_COPY_AND_ASSIGN(MarkAndSweepRemoved); public: explicit MarkAndSweepRemoved(TreeInfo* info) : mTreeInfo(info) {} - void onMaybeRemovedFromTree(RenderNode* node) override { - mMarked.emplace_back(node); - } + void onMaybeRemovedFromTree(RenderNode* node) override { mMarked.emplace_back(node); } ~MarkAndSweepRemoved() { for (auto& node : mMarked) { diff --git a/libs/hwui/RenderProperties.cpp b/libs/hwui/RenderProperties.cpp index 146fbe73a48a..e49574462e9e 100644 --- a/libs/hwui/RenderProperties.cpp +++ b/libs/hwui/RenderProperties.cpp @@ -44,9 +44,9 @@ void LayerProperties::reset() { } bool LayerProperties::setColorFilter(SkColorFilter* filter) { - if (mColorFilter == filter) return false; - SkRefCnt_SafeAssign(mColorFilter, filter); - return true; + if (mColorFilter == filter) return false; + SkRefCnt_SafeAssign(mColorFilter, filter); + return true; } bool LayerProperties::setFromPaint(const SkPaint* paint) { @@ -66,18 +66,13 @@ LayerProperties& LayerProperties::operator=(const LayerProperties& other) { return *this; } -RenderProperties::ComputedFields::ComputedFields() - : mTransformMatrix(nullptr) { -} +RenderProperties::ComputedFields::ComputedFields() : mTransformMatrix(nullptr) {} RenderProperties::ComputedFields::~ComputedFields() { delete mTransformMatrix; } -RenderProperties::RenderProperties() - : mStaticMatrix(nullptr) - , mAnimationMatrix(nullptr) { -} +RenderProperties::RenderProperties() : mStaticMatrix(nullptr), mAnimationMatrix(nullptr) {} RenderProperties::~RenderProperties() { delete mStaticMatrix; @@ -99,14 +94,14 @@ RenderProperties& RenderProperties::operator=(const RenderProperties& other) { return *this; } -static void dumpMatrix(std::ostream& output, std::string& indent, - const char* label, SkMatrix* matrix) { - if (matrix) { - output << indent << "(" << label << " " << matrix << ": "; +static void dumpMatrix(std::ostream& output, std::string& indent, const char* label, + SkMatrix* matrix) { + if (matrix) { + output << indent << "(" << label << " " << matrix << ": "; output << std::fixed << std::setprecision(2); - output << "[" << matrix->get(0) << " "<< matrix->get(1) << " " << matrix->get(2) << "]"; - output << " [" << matrix->get(3) << " "<< matrix->get(4) << " " << matrix->get(5) << "]"; - output << " [" << matrix->get(6) << " "<< matrix->get(7) << " " << matrix->get(8) << "]"; + output << "[" << matrix->get(0) << " " << matrix->get(1) << " " << matrix->get(2) << "]"; + output << " [" << matrix->get(3) << " " << matrix->get(4) << " " << matrix->get(5) << "]"; + output << " [" << matrix->get(6) << " " << matrix->get(7) << " " << matrix->get(8) << "]"; output << ")" << std::endl; } } @@ -114,8 +109,8 @@ static void dumpMatrix(std::ostream& output, std::string& indent, void RenderProperties::debugOutputProperties(std::ostream& output, const int level) const { auto indent = std::string(level * 2, ' '); if (mPrimitiveFields.mLeft != 0 || mPrimitiveFields.mTop != 0) { - output << indent << "(Translate (left, top) " << mPrimitiveFields.mLeft - << ", " << mPrimitiveFields.mTop << ")" << std::endl; + output << indent << "(Translate (left, top) " << mPrimitiveFields.mLeft << ", " + << mPrimitiveFields.mTop << ")" << std::endl; } dumpMatrix(output, indent, "ConcatMatrix (static)", mStaticMatrix); dumpMatrix(output, indent, "ConcatMatrix (animation)", mAnimationMatrix); @@ -124,7 +119,7 @@ void RenderProperties::debugOutputProperties(std::ostream& output, const int lev if (hasTransformMatrix()) { if (isTransformTranslateOnly()) { output << indent << "(Translate " << getTranslationX() << ", " << getTranslationY() - << ", " << getZ() << ")" << std::endl; + << ", " << getZ() << ")" << std::endl; } else { dumpMatrix(output, indent, "ConcatMatrix ", mComputedFields.mTransformMatrix); } @@ -132,10 +127,9 @@ void RenderProperties::debugOutputProperties(std::ostream& output, const int lev const bool isLayer = effectiveLayerType() != LayerType::None; int clipFlags = getClippingFlags(); - if (mPrimitiveFields.mAlpha < 1 - && !MathUtils::isZero(mPrimitiveFields.mAlpha)) { + if (mPrimitiveFields.mAlpha < 1 && !MathUtils::isZero(mPrimitiveFields.mAlpha)) { if (isLayer) { - clipFlags &= ~CLIP_TO_BOUNDS; // bounds clipping done by layer + clipFlags &= ~CLIP_TO_BOUNDS; // bounds clipping done by layer } if (CC_LIKELY(isLayer || !getHasOverlappingRendering())) { @@ -146,31 +140,28 @@ void RenderProperties::debugOutputProperties(std::ostream& output, const int lev Rect layerBounds(0, 0, getWidth(), getHeight()); if (clipFlags) { getClippingRectForFlags(clipFlags, &layerBounds); - clipFlags = 0; // all clipping done by savelayer + clipFlags = 0; // all clipping done by savelayer } - output << indent << "(SaveLayerAlpha " - << (int)layerBounds.left << ", " << (int)layerBounds.top << ", " - << (int)layerBounds.right << ", " << (int)layerBounds.bottom << ", " - << (int)(mPrimitiveFields.mAlpha * 255) << ", 0x" << std::hex - << (SaveFlags::HasAlphaLayer | SaveFlags::ClipToLayer) << ")" << std::dec - << std::endl; + output << indent << "(SaveLayerAlpha " << (int)layerBounds.left << ", " + << (int)layerBounds.top << ", " << (int)layerBounds.right << ", " + << (int)layerBounds.bottom << ", " << (int)(mPrimitiveFields.mAlpha * 255) + << ", 0x" << std::hex << (SaveFlags::HasAlphaLayer | SaveFlags::ClipToLayer) + << ")" << std::dec << std::endl; } } if (clipFlags) { Rect clipRect; getClippingRectForFlags(clipFlags, &clipRect); - output << indent << "(ClipRect " - << (int)clipRect.left << ", " << (int)clipRect.top << ", " - << (int)clipRect.right << ", " << (int)clipRect.bottom << ")" << std::endl; + output << indent << "(ClipRect " << (int)clipRect.left << ", " << (int)clipRect.top << ", " + << (int)clipRect.right << ", " << (int)clipRect.bottom << ")" << std::endl; } if (getRevealClip().willClip()) { Rect bounds; getRevealClip().getBounds(&bounds); - output << indent << "(Clip to reveal clip with bounds " - << bounds.left << ", " << bounds.top << ", " - << bounds.right << ", " << bounds.bottom << ")" << std::endl; + output << indent << "(Clip to reveal clip with bounds " << bounds.left << ", " << bounds.top + << ", " << bounds.right << ", " << bounds.bottom << ")" << std::endl; } auto& outline = mPrimitiveFields.mOutline; @@ -179,9 +170,8 @@ void RenderProperties::debugOutputProperties(std::ostream& output, const int lev output << indent << "(Clip to empty outline)"; } else if (outline.willClip()) { const Rect& bounds = outline.getBounds(); - output << indent << "(Clip to outline with bounds " - << bounds.left << ", " << bounds.top << ", " - << bounds.right << ", " << bounds.bottom << ")" << std::endl; + output << indent << "(Clip to outline with bounds " << bounds.left << ", " << bounds.top + << ", " << bounds.right << ", " << bounds.bottom << ")" << std::endl; } } } @@ -212,7 +202,7 @@ void RenderProperties::updateMatrix() { mComputedFields.mTransformCamera.getMatrix(&transform3D); transform3D.preTranslate(-getPivotX(), -getPivotY()); transform3D.postTranslate(getPivotX() + getTranslationX(), - getPivotY() + getTranslationY()); + getPivotY() + getTranslationY()); transform->postConcat(transform3D); mComputedFields.mTransformCamera.restore(); } diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h index 9ee2f9c69343..3d2c2520624b 100644 --- a/libs/hwui/RenderProperties.h +++ b/libs/hwui/RenderProperties.h @@ -18,24 +18,25 @@ #include "Caches.h" #include "DeviceInfo.h" +#include "Outline.h" #include "Rect.h" #include "RevealClip.h" -#include "Outline.h" #include "utils/MathUtils.h" #include "utils/PaintUtils.h" #include <SkBlendMode.h> #include <SkCamera.h> +#include <SkColor.h> #include <SkMatrix.h> #include <SkRegion.h> -#include <algorithm> -#include <stddef.h> -#include <vector> -#include <cutils/compiler.h> #include <androidfw/ResourceTypes.h> +#include <cutils/compiler.h> +#include <stddef.h> #include <utils/Log.h> +#include <algorithm> #include <ostream> +#include <vector> class SkBitmap; class SkColorFilter; @@ -63,7 +64,7 @@ enum class LayerType { }; enum ClippingFlags { - CLIP_TO_BOUNDS = 0x1 << 0, + CLIP_TO_BOUNDS = 0x1 << 0, CLIP_TO_CLIP_BOUNDS = 0x1 << 1, }; @@ -77,43 +78,27 @@ public: return false; } - bool setOpaque(bool opaque) { - return RP_SET(mOpaque, opaque); - } + bool setOpaque(bool opaque) { return RP_SET(mOpaque, opaque); } - bool opaque() const { - return mOpaque; - } + bool opaque() const { return mOpaque; } - bool setAlpha(uint8_t alpha) { - return RP_SET(mAlpha, alpha); - } + bool setAlpha(uint8_t alpha) { return RP_SET(mAlpha, alpha); } - uint8_t alpha() const { - return mAlpha; - } + uint8_t alpha() const { return mAlpha; } - bool setXferMode(SkBlendMode mode) { - return RP_SET(mMode, mode); - } + bool setXferMode(SkBlendMode mode) { return RP_SET(mMode, mode); } - SkBlendMode xferMode() const { - return mMode; - } + SkBlendMode xferMode() const { return mMode; } bool setColorFilter(SkColorFilter* filter); - SkColorFilter* colorFilter() const { - return mColorFilter; - } + SkColorFilter* colorFilter() const { return mColorFilter; } // Sets alpha, xfermode, and colorfilter from an SkPaint // paint may be NULL, in which case defaults will be set bool setFromPaint(const SkPaint* paint); - bool needsBlending() const { - return !opaque() || alpha() < 255; - } + bool needsBlending() const { return !opaque() || alpha() < 255; } LayerProperties& operator=(const LayerProperties& other); @@ -123,9 +108,7 @@ private: void reset(); // Private since external users should go through properties().effectiveLayerType() - LayerType type() const { - return mType; - } + LayerType type() const { return mType; } friend class RenderProperties; @@ -169,16 +152,17 @@ public: */ bool prepareForFunctorPresence(bool willHaveFunctor, bool ancestorDictatesFunctorsNeedLayer) { // parent may have already dictated that a descendant layer is needed - bool functorsNeedLayer = ancestorDictatesFunctorsNeedLayer + bool functorsNeedLayer = + ancestorDictatesFunctorsNeedLayer // Round rect clipping forces layer for functors - || CC_UNLIKELY(getOutline().willRoundRectClip()) - || CC_UNLIKELY(getRevealClip().willClip()) + || CC_UNLIKELY(getOutline().willRoundRectClip()) || + CC_UNLIKELY(getRevealClip().willClip()) // Complex matrices forces layer, due to stencil clipping - || CC_UNLIKELY(getTransformMatrix() && !getTransformMatrix()->isScaleTranslate()) - || CC_UNLIKELY(getAnimationMatrix() && !getAnimationMatrix()->isScaleTranslate()) - || CC_UNLIKELY(getStaticMatrix() && !getStaticMatrix()->isScaleTranslate()); + || CC_UNLIKELY(getTransformMatrix() && !getTransformMatrix()->isScaleTranslate()) || + CC_UNLIKELY(getAnimationMatrix() && !getAnimationMatrix()->isScaleTranslate()) || + CC_UNLIKELY(getStaticMatrix() && !getStaticMatrix()->isScaleTranslate()); mComputedFields.mNeedLayerForFunctors = (willHaveFunctor && functorsNeedLayer); @@ -210,9 +194,7 @@ public: return RP_SET(mPrimitiveFields.mProjectionReceiver, shouldReceive); } - bool isProjectionReceiver() const { - return mPrimitiveFields.mProjectionReceiver; - } + bool isProjectionReceiver() const { return mPrimitiveFields.mProjectionReceiver; } bool setStaticMatrix(const SkMatrix* matrix) { delete mStaticMatrix; @@ -225,9 +207,7 @@ public: } // Can return NULL - const SkMatrix* getStaticMatrix() const { - return mStaticMatrix; - } + const SkMatrix* getStaticMatrix() const { return mStaticMatrix; } bool setAnimationMatrix(const SkMatrix* matrix) { delete mAnimationMatrix; @@ -244,124 +224,85 @@ public: return RP_SET(mPrimitiveFields.mAlpha, alpha); } - float getAlpha() const { - return mPrimitiveFields.mAlpha; - } + float getAlpha() const { return mPrimitiveFields.mAlpha; } bool setHasOverlappingRendering(bool hasOverlappingRendering) { return RP_SET(mPrimitiveFields.mHasOverlappingRendering, hasOverlappingRendering); } - bool hasOverlappingRendering() const { - return mPrimitiveFields.mHasOverlappingRendering; - } + bool hasOverlappingRendering() const { return mPrimitiveFields.mHasOverlappingRendering; } bool setElevation(float elevation) { return RP_SET(mPrimitiveFields.mElevation, elevation); // Don't dirty matrix/pivot, since they don't respect Z } - float getElevation() const { - return mPrimitiveFields.mElevation; - } + float getElevation() const { return mPrimitiveFields.mElevation; } bool setTranslationX(float translationX) { return RP_SET_AND_DIRTY(mPrimitiveFields.mTranslationX, translationX); } - float getTranslationX() const { - return mPrimitiveFields.mTranslationX; - } + float getTranslationX() const { return mPrimitiveFields.mTranslationX; } bool setTranslationY(float translationY) { return RP_SET_AND_DIRTY(mPrimitiveFields.mTranslationY, translationY); } - float getTranslationY() const { - return mPrimitiveFields.mTranslationY; - } + float getTranslationY() const { return mPrimitiveFields.mTranslationY; } bool setTranslationZ(float translationZ) { return RP_SET(mPrimitiveFields.mTranslationZ, translationZ); // mMatrixOrPivotDirty not set, since matrix doesn't respect Z } - float getTranslationZ() const { - return mPrimitiveFields.mTranslationZ; - } + float getTranslationZ() const { return mPrimitiveFields.mTranslationZ; } // Animation helper - bool setX(float value) { - return setTranslationX(value - getLeft()); - } + bool setX(float value) { return setTranslationX(value - getLeft()); } // Animation helper - float getX() const { - return getLeft() + getTranslationX(); - } + float getX() const { return getLeft() + getTranslationX(); } // Animation helper - bool setY(float value) { - return setTranslationY(value - getTop()); - } + bool setY(float value) { return setTranslationY(value - getTop()); } // Animation helper - float getY() const { - return getTop() + getTranslationY(); - } + float getY() const { return getTop() + getTranslationY(); } // Animation helper - bool setZ(float value) { - return setTranslationZ(value - getElevation()); - } + bool setZ(float value) { return setTranslationZ(value - getElevation()); } - float getZ() const { - return getElevation() + getTranslationZ(); - } + float getZ() const { return getElevation() + getTranslationZ(); } bool setRotation(float rotation) { return RP_SET_AND_DIRTY(mPrimitiveFields.mRotation, rotation); } - float getRotation() const { - return mPrimitiveFields.mRotation; - } + float getRotation() const { return mPrimitiveFields.mRotation; } bool setRotationX(float rotationX) { return RP_SET_AND_DIRTY(mPrimitiveFields.mRotationX, rotationX); } - float getRotationX() const { - return mPrimitiveFields.mRotationX; - } + float getRotationX() const { return mPrimitiveFields.mRotationX; } bool setRotationY(float rotationY) { return RP_SET_AND_DIRTY(mPrimitiveFields.mRotationY, rotationY); } - float getRotationY() const { - return mPrimitiveFields.mRotationY; - } + float getRotationY() const { return mPrimitiveFields.mRotationY; } - bool setScaleX(float scaleX) { - return RP_SET_AND_DIRTY(mPrimitiveFields.mScaleX, scaleX); - } + bool setScaleX(float scaleX) { return RP_SET_AND_DIRTY(mPrimitiveFields.mScaleX, scaleX); } - float getScaleX() const { - return mPrimitiveFields.mScaleX; - } + float getScaleX() const { return mPrimitiveFields.mScaleX; } - bool setScaleY(float scaleY) { - return RP_SET_AND_DIRTY(mPrimitiveFields.mScaleY, scaleY); - } + bool setScaleY(float scaleY) { return RP_SET_AND_DIRTY(mPrimitiveFields.mScaleY, scaleY); } - float getScaleY() const { - return mPrimitiveFields.mScaleY; - } + float getScaleY() const { return mPrimitiveFields.mScaleY; } bool setPivotX(float pivotX) { - if (RP_SET(mPrimitiveFields.mPivotX, pivotX) - || !mPrimitiveFields.mPivotExplicitlySet) { + if (RP_SET(mPrimitiveFields.mPivotX, pivotX) || !mPrimitiveFields.mPivotExplicitlySet) { mPrimitiveFields.mMatrixOrPivotDirty = true; mPrimitiveFields.mPivotExplicitlySet = true; return true; @@ -373,13 +314,10 @@ public: * so the value returned may be stale if the RenderProperties has been * modified since the last call to updateMatrix() */ - float getPivotX() const { - return mPrimitiveFields.mPivotX; - } + float getPivotX() const { return mPrimitiveFields.mPivotX; } bool setPivotY(float pivotY) { - if (RP_SET(mPrimitiveFields.mPivotY, pivotY) - || !mPrimitiveFields.mPivotExplicitlySet) { + if (RP_SET(mPrimitiveFields.mPivotY, pivotY) || !mPrimitiveFields.mPivotExplicitlySet) { mPrimitiveFields.mMatrixOrPivotDirty = true; mPrimitiveFields.mPivotExplicitlySet = true; return true; @@ -387,13 +325,9 @@ public: return false; } - float getPivotY() const { - return mPrimitiveFields.mPivotY; - } + float getPivotY() const { return mPrimitiveFields.mPivotY; } - bool isPivotExplicitlySet() const { - return mPrimitiveFields.mPivotExplicitlySet; - } + bool isPivotExplicitlySet() const { return mPrimitiveFields.mPivotExplicitlySet; } bool setCameraDistance(float distance) { if (distance != getCameraDistance()) { @@ -420,9 +354,7 @@ public: return false; } - int getLeft() const { - return mPrimitiveFields.mLeft; - } + int getLeft() const { return mPrimitiveFields.mLeft; } bool setTop(int top) { if (RP_SET(mPrimitiveFields.mTop, top)) { @@ -435,9 +367,7 @@ public: return false; } - int getTop() const { - return mPrimitiveFields.mTop; - } + int getTop() const { return mPrimitiveFields.mTop; } bool setRight(int right) { if (RP_SET(mPrimitiveFields.mRight, right)) { @@ -450,9 +380,7 @@ public: return false; } - int getRight() const { - return mPrimitiveFields.mRight; - } + int getRight() const { return mPrimitiveFields.mRight; } bool setBottom(int bottom) { if (RP_SET(mPrimitiveFields.mBottom, bottom)) { @@ -465,9 +393,7 @@ public: return false; } - int getBottom() const { - return mPrimitiveFields.mBottom; - } + int getBottom() const { return mPrimitiveFields.mBottom; } bool setLeftTop(int left, int top) { bool leftResult = setLeft(left); @@ -476,8 +402,8 @@ public: } bool setLeftTopRightBottom(int left, int top, int right, int bottom) { - if (left != mPrimitiveFields.mLeft || top != mPrimitiveFields.mTop - || right != mPrimitiveFields.mRight || bottom != mPrimitiveFields.mBottom) { + if (left != mPrimitiveFields.mLeft || top != mPrimitiveFields.mTop || + right != mPrimitiveFields.mRight || bottom != mPrimitiveFields.mBottom) { mPrimitiveFields.mLeft = left; mPrimitiveFields.mTop = top; mPrimitiveFields.mRight = right; @@ -510,17 +436,11 @@ public: return false; } - int getWidth() const { - return mPrimitiveFields.mWidth; - } + int getWidth() const { return mPrimitiveFields.mWidth; } - int getHeight() const { - return mPrimitiveFields.mHeight; - } + int getHeight() const { return mPrimitiveFields.mHeight; } - const SkMatrix* getAnimationMatrix() const { - return mAnimationMatrix; - } + const SkMatrix* getAnimationMatrix() const { return mAnimationMatrix; } bool hasTransformMatrix() const { return getTransformMatrix() && !getTransformMatrix()->isIdentity(); @@ -536,17 +456,11 @@ public: return mComputedFields.mTransformMatrix; } - int getClippingFlags() const { - return mPrimitiveFields.mClippingFlags; - } + int getClippingFlags() const { return mPrimitiveFields.mClippingFlags; } - bool getClipToBounds() const { - return mPrimitiveFields.mClippingFlags & CLIP_TO_BOUNDS; - } + bool getClipToBounds() const { return mPrimitiveFields.mClippingFlags & CLIP_TO_BOUNDS; } - const Rect& getClipBounds() const { - return mPrimitiveFields.mClipBounds; - } + const Rect& getClipBounds() const { return mPrimitiveFields.mClipBounds; } void getClippingRectForFlags(uint32_t flags, Rect* outRect) const { if (flags & CLIP_TO_BOUNDS) { @@ -559,41 +473,25 @@ public: } } - bool getHasOverlappingRendering() const { - return mPrimitiveFields.mHasOverlappingRendering; - } + bool getHasOverlappingRendering() const { return mPrimitiveFields.mHasOverlappingRendering; } - const Outline& getOutline() const { - return mPrimitiveFields.mOutline; - } + const Outline& getOutline() const { return mPrimitiveFields.mOutline; } - const RevealClip& getRevealClip() const { - return mPrimitiveFields.mRevealClip; - } + const RevealClip& getRevealClip() const { return mPrimitiveFields.mRevealClip; } - bool getProjectBackwards() const { - return mPrimitiveFields.mProjectBackwards; - } + bool getProjectBackwards() const { return mPrimitiveFields.mProjectBackwards; } void debugOutputProperties(std::ostream& output, const int level) const; void updateMatrix(); - Outline& mutableOutline() { - return mPrimitiveFields.mOutline; - } + Outline& mutableOutline() { return mPrimitiveFields.mOutline; } - RevealClip& mutableRevealClip() { - return mPrimitiveFields.mRevealClip; - } + RevealClip& mutableRevealClip() { return mPrimitiveFields.mRevealClip; } - const LayerProperties& layerProperties() const { - return mLayerProperties; - } + const LayerProperties& layerProperties() const { return mLayerProperties; } - LayerProperties& mutateLayerProperties() { - return mLayerProperties; - } + LayerProperties& mutateLayerProperties() { return mLayerProperties; } // Returns true if damage calculations should be clipped to bounds // TODO: Figure out something better for getZ(), as children should still be @@ -605,24 +503,29 @@ public: } bool hasShadow() const { - return getZ() > 0.0f - && getOutline().getPath() != nullptr - && getOutline().getAlpha() != 0.0f; + return getZ() > 0.0f && getOutline().getPath() != nullptr && + getOutline().getAlpha() != 0.0f; + } + + SkColor getShadowColor() const { + return mPrimitiveFields.mShadowColor; + } + + bool setShadowColor(SkColor shadowColor) { + return RP_SET(mPrimitiveFields.mShadowColor, shadowColor); } bool fitsOnLayer() const { const DeviceInfo* deviceInfo = DeviceInfo::get(); - return mPrimitiveFields.mWidth <= deviceInfo->maxTextureSize() - && mPrimitiveFields.mHeight <= deviceInfo->maxTextureSize(); + return mPrimitiveFields.mWidth <= deviceInfo->maxTextureSize() && + mPrimitiveFields.mHeight <= deviceInfo->maxTextureSize(); } bool promotedToLayer() const { - return mLayerProperties.mType == LayerType::None - && fitsOnLayer() - && (mComputedFields.mNeedLayerForFunctors - || (!MathUtils::isZero(mPrimitiveFields.mAlpha) - && mPrimitiveFields.mAlpha < 1 - && mPrimitiveFields.mHasOverlappingRendering)); + return mLayerProperties.mType == LayerType::None && fitsOnLayer() && + (mComputedFields.mNeedLayerForFunctors || + (!MathUtils::isZero(mPrimitiveFields.mAlpha) && mPrimitiveFields.mAlpha < 1 && + mPrimitiveFields.mHasOverlappingRendering)); } LayerType effectiveLayerType() const { @@ -635,6 +538,7 @@ private: int mLeft = 0, mTop = 0, mRight = 0, mBottom = 0; int mWidth = 0, mHeight = 0; int mClippingFlags = CLIP_TO_BOUNDS; + SkColor mShadowColor = SK_ColorBLACK; float mAlpha = 1; float mTranslationX = 0, mTranslationY = 0, mTranslationZ = 0; float mElevation = 0; diff --git a/libs/hwui/ResourceCache.cpp b/libs/hwui/ResourceCache.cpp index b26e433cfa4c..d60b99469368 100644 --- a/libs/hwui/ResourceCache.cpp +++ b/libs/hwui/ResourceCache.cpp @@ -32,10 +32,10 @@ void ResourceCache::logCache() { ALOGD("ResourceCache: cacheReport:"); for (size_t i = 0; i < mCache->size(); ++i) { ResourceReference* ref = mCache->valueAt(i); - ALOGD(" ResourceCache: mCache(%zu): resource, ref = 0x%p, 0x%p", - i, mCache->keyAt(i), mCache->valueAt(i)); - ALOGD(" ResourceCache: mCache(%zu): refCount, destroyed, type = %d, %d, %d", - i, ref->refCount, ref->destroyed, ref->resourceType); + ALOGD(" ResourceCache: mCache(%zu): resource, ref = 0x%p, 0x%p", i, mCache->keyAt(i), + mCache->valueAt(i)); + ALOGD(" ResourceCache: mCache(%zu): refCount, destroyed, type = %d, %d, %d", i, + ref->refCount, ref->destroyed, ref->resourceType); } } @@ -63,7 +63,7 @@ void ResourceCache::incrementRefcount(void* resource, ResourceType resourceType) } void ResourceCache::incrementRefcount(const Res_png_9patch* patchResource) { - incrementRefcount((void*) patchResource, kNinePatch); + incrementRefcount((void*)patchResource, kNinePatch); } void ResourceCache::incrementRefcountLocked(void* resource, ResourceType resourceType) { @@ -82,7 +82,7 @@ void ResourceCache::decrementRefcount(void* resource) { } void ResourceCache::decrementRefcount(const Res_png_9patch* patchResource) { - decrementRefcount((void*) patchResource); + decrementRefcount((void*)patchResource); } void ResourceCache::decrementRefcountLocked(void* resource) { @@ -99,7 +99,7 @@ void ResourceCache::decrementRefcountLocked(void* resource) { } void ResourceCache::decrementRefcountLocked(const Res_png_9patch* patchResource) { - decrementRefcountLocked((void*) patchResource); + decrementRefcountLocked((void*)patchResource); } void ResourceCache::destructor(Res_png_9patch* resource) { @@ -117,7 +117,7 @@ void ResourceCache::destructorLocked(Res_png_9patch* resource) { } else { // A Res_png_9patch is actually an array of byte that's larger // than sizeof(Res_png_9patch). It must be freed as an array. - delete[] (int8_t*) resource; + delete[](int8_t*) resource; } return; } @@ -136,20 +136,19 @@ void ResourceCache::deleteResourceReferenceLocked(const void* resource, Resource switch (ref->resourceType) { case kNinePatch: { if (Caches::hasInstance()) { - Caches::getInstance().patchCache.removeDeferred((Res_png_9patch*) resource); + Caches::getInstance().patchCache.removeDeferred((Res_png_9patch*)resource); } else { // A Res_png_9patch is actually an array of byte that's larger // than sizeof(Res_png_9patch). It must be freed as an array. - int8_t* patch = (int8_t*) resource; + int8_t* patch = (int8_t*)resource; delete[] patch; } - } - break; + } break; } } mCache->removeItem(resource); delete ref; } -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/ResourceCache.h b/libs/hwui/ResourceCache.h index 3ac78642c7ee..fd3f9fd05d58 100644 --- a/libs/hwui/ResourceCache.h +++ b/libs/hwui/ResourceCache.h @@ -41,9 +41,10 @@ enum ResourceType { class ResourceReference { public: - explicit ResourceReference(ResourceType type) { - refCount = 0; destroyed = false; resourceType = type; + refCount = 0; + destroyed = false; + resourceType = type; } int refCount; @@ -51,14 +52,13 @@ public: ResourceType resourceType; }; -class ANDROID_API ResourceCache: public Singleton<ResourceCache> { +class ANDROID_API ResourceCache : public Singleton<ResourceCache> { ResourceCache(); ~ResourceCache(); friend class Singleton<ResourceCache>; public: - /** * When using these two methods, make sure to only invoke the *Locked() * variants of increment/decrementRefcount(), recyle() and destructor() @@ -97,7 +97,7 @@ private: KeyedVector<const void*, ResourceReference*>* mCache; }; -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android -#endif // ANDROID_HWUI_RESOURCE_CACHE_H +#endif // ANDROID_HWUI_RESOURCE_CACHE_H diff --git a/libs/hwui/RevealClip.h b/libs/hwui/RevealClip.h index 63821dddd369..a5678d48482a 100644 --- a/libs/hwui/RevealClip.h +++ b/libs/hwui/RevealClip.h @@ -25,11 +25,7 @@ namespace uirenderer { class RevealClip { public: - RevealClip() - : mShouldClip(false) - , mX(0) - , mY(0) - , mRadius(0) {} + RevealClip() : mShouldClip(false), mX(0), mY(0), mRadius(0) {} void set(bool shouldClip, float x, float y, float radius) { mShouldClip = shouldClip; @@ -43,13 +39,10 @@ public: } } - bool willClip() const { - return mShouldClip; - } + bool willClip() const { return mShouldClip; } void getBounds(Rect* outBounds) const { - outBounds->set(mX - mRadius, mY - mRadius, - mX + mRadius, mY + mRadius); + outBounds->set(mX - mRadius, mY - mRadius, mX + mRadius, mY + mRadius); } float getRadius() const { return mRadius; } diff --git a/libs/hwui/ShadowTessellator.cpp b/libs/hwui/ShadowTessellator.cpp index e94a70a9f80d..d0155ee473f7 100644 --- a/libs/hwui/ShadowTessellator.cpp +++ b/libs/hwui/ShadowTessellator.cpp @@ -16,8 +16,8 @@ #include <math.h> #include <utils/Log.h> -#include <utils/Trace.h> #include <utils/MathUtils.h> +#include <utils/Trace.h> #include "AmbientShadow.h" #include "Properties.h" @@ -28,10 +28,10 @@ namespace android { namespace uirenderer { -void ShadowTessellator::tessellateAmbientShadow(bool isCasterOpaque, - const Vector3* casterPolygon, int casterVertexCount, - const Vector3& centroid3d, const Rect& casterBounds, - const Rect& localClip, float maxZ, VertexBuffer& shadowVertexBuffer) { +void ShadowTessellator::tessellateAmbientShadow(bool isCasterOpaque, const Vector3* casterPolygon, + int casterVertexCount, const Vector3& centroid3d, + const Rect& casterBounds, const Rect& localClip, + float maxZ, VertexBuffer& shadowVertexBuffer) { ATRACE_CALL(); // A bunch of parameters to tweak the shadow. @@ -53,32 +53,32 @@ void ShadowTessellator::tessellateAmbientShadow(bool isCasterOpaque, return; } - AmbientShadow::createAmbientShadow(isCasterOpaque, casterPolygon, - casterVertexCount, centroid3d, heightFactor, geomFactor, - shadowVertexBuffer); + AmbientShadow::createAmbientShadow(isCasterOpaque, casterPolygon, casterVertexCount, centroid3d, + heightFactor, geomFactor, shadowVertexBuffer); } -void ShadowTessellator::tessellateSpotShadow(bool isCasterOpaque, - const Vector3* casterPolygon, int casterVertexCount, const Vector3& casterCentroid, - const mat4& receiverTransform, const Vector3& lightCenter, int lightRadius, - const Rect& casterBounds, const Rect& localClip, VertexBuffer& shadowVertexBuffer) { +void ShadowTessellator::tessellateSpotShadow(bool isCasterOpaque, const Vector3* casterPolygon, + int casterVertexCount, const Vector3& casterCentroid, + const mat4& receiverTransform, + const Vector3& lightCenter, int lightRadius, + const Rect& casterBounds, const Rect& localClip, + VertexBuffer& shadowVertexBuffer) { ATRACE_CALL(); Vector3 adjustedLightCenter(lightCenter); if (CC_UNLIKELY(Properties::overrideLightPosY > 0)) { - adjustedLightCenter.y = - Properties::overrideLightPosY; // negated since this shifts up + adjustedLightCenter.y = -Properties::overrideLightPosY; // negated since this shifts up } if (CC_UNLIKELY(Properties::overrideLightPosZ > 0)) { adjustedLightCenter.z = Properties::overrideLightPosZ; } #if DEBUG_SHADOW - ALOGD("light center %f %f %f %d", - adjustedLightCenter.x, adjustedLightCenter.y, adjustedLightCenter.z, lightRadius); + ALOGD("light center %f %f %f %d", adjustedLightCenter.x, adjustedLightCenter.y, + adjustedLightCenter.z, lightRadius); #endif - if (isnan(adjustedLightCenter.x) - || isnan(adjustedLightCenter.y) - || isnan(adjustedLightCenter.z)) { + if (isnan(adjustedLightCenter.x) || isnan(adjustedLightCenter.y) || + isnan(adjustedLightCenter.z)) { return; } @@ -95,7 +95,7 @@ void ShadowTessellator::tessellateSpotShadow(bool isCasterOpaque, // Now light and caster are both in local space, we will check whether // the shadow is within the clip area. Rect lightRect = Rect(adjustedLightCenter.x - lightRadius, adjustedLightCenter.y - lightRadius, - adjustedLightCenter.x + lightRadius, adjustedLightCenter.y + lightRadius); + adjustedLightCenter.x + lightRadius, adjustedLightCenter.y + lightRadius); lightRect.unionWith(localClip); if (!lightRect.intersects(casterBounds)) { #if DEBUG_SHADOW @@ -104,13 +104,13 @@ void ShadowTessellator::tessellateSpotShadow(bool isCasterOpaque, return; } - SpotShadow::createSpotShadow(isCasterOpaque, adjustedLightCenter, lightRadius, - casterPolygon, casterVertexCount, casterCentroid, shadowVertexBuffer); + SpotShadow::createSpotShadow(isCasterOpaque, adjustedLightCenter, lightRadius, casterPolygon, + casterVertexCount, casterCentroid, shadowVertexBuffer); #if DEBUG_SHADOW - if(shadowVertexBuffer.getVertexCount() <= 0) { + if (shadowVertexBuffer.getVertexCount() <= 0) { ALOGD("Spot shadow generation failed %d", shadowVertexBuffer.getVertexCount()); - } + } #endif } @@ -141,7 +141,7 @@ Vector2 ShadowTessellator::centroid2d(const Vector2* poly, int polyLength) { Vector2 centroid = poly[0]; if (area != 0) { centroid = (Vector2){static_cast<float>(sumx / (3 * area)), - static_cast<float>(sumy / (3 * area))}; + static_cast<float>(sumy / (3 * area))}; } else { ALOGW("Area is 0 while computing centroid!"); } @@ -161,8 +161,8 @@ Vector2 ShadowTessellator::calculateNormal(const Vector2& p1, const Vector2& p2) return result; } -int ShadowTessellator::getExtraVertexNumber(const Vector2& vector1, - const Vector2& vector2, float divisor) { +int ShadowTessellator::getExtraVertexNumber(const Vector2& vector1, const Vector2& vector2, + float divisor) { // When there is no distance difference, there is no need for extra vertices. if (vector1.lengthSquared() == 0 || vector2.lengthSquared() == 0) { return 0; @@ -179,13 +179,13 @@ int ShadowTessellator::getExtraVertexNumber(const Vector2& vector1, // TODO: Use look up table for the dotProduct to extraVerticesNumber // computation, if needed. float angle = acosf(dotProduct); - return (int) floor(angle / divisor); + return (int)floor(angle / divisor); } void ShadowTessellator::checkOverflow(int used, int total, const char* bufferName) { - LOG_ALWAYS_FATAL_IF(used > total, "Error: %s overflow!!! used %d, total %d", - bufferName, used, total); + LOG_ALWAYS_FATAL_IF(used > total, "Error: %s overflow!!! used %d, total %d", bufferName, used, + total); } -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/ShadowTessellator.h b/libs/hwui/ShadowTessellator.h index 2eaf187a1af1..79f46f9e9a06 100644 --- a/libs/hwui/ShadowTessellator.h +++ b/libs/hwui/ShadowTessellator.h @@ -68,27 +68,27 @@ class VertexBuffer; class ShadowTessellator { public: - static void tessellateAmbientShadow(bool isCasterOpaque, - const Vector3* casterPolygon, int casterVertexCount, - const Vector3& centroid3d, const Rect& casterBounds, - const Rect& localClip, float maxZ, VertexBuffer& shadowVertexBuffer); + static void tessellateAmbientShadow(bool isCasterOpaque, const Vector3* casterPolygon, + int casterVertexCount, const Vector3& centroid3d, + const Rect& casterBounds, const Rect& localClip, float maxZ, + VertexBuffer& shadowVertexBuffer); - static void tessellateSpotShadow(bool isCasterOpaque, - const Vector3* casterPolygon, int casterVertexCount, const Vector3& casterCentroid, - const mat4& receiverTransform, const Vector3& lightCenter, int lightRadius, - const Rect& casterBounds, const Rect& localClip, VertexBuffer& shadowVertexBuffer); + static void tessellateSpotShadow(bool isCasterOpaque, const Vector3* casterPolygon, + int casterVertexCount, const Vector3& casterCentroid, + const mat4& receiverTransform, const Vector3& lightCenter, + int lightRadius, const Rect& casterBounds, + const Rect& localClip, VertexBuffer& shadowVertexBuffer); static Vector2 centroid2d(const Vector2* poly, int polyLength); static Vector2 calculateNormal(const Vector2& p1, const Vector2& p2); - static int getExtraVertexNumber(const Vector2& vector1, const Vector2& vector2, - float divisor); + static int getExtraVertexNumber(const Vector2& vector1, const Vector2& vector2, float divisor); static void checkOverflow(int used, int total, const char* bufferName); -}; // ShadowTessellator +}; // ShadowTessellator -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android -#endif // ANDROID_HWUI_SHADOW_TESSELLATOR_H +#endif // ANDROID_HWUI_SHADOW_TESSELLATOR_H diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp index 0a642b60d4c7..b2edd3392873 100644 --- a/libs/hwui/SkiaCanvas.cpp +++ b/libs/hwui/SkiaCanvas.cpp @@ -23,14 +23,17 @@ #include "hwui/MinikinUtils.h" #include "pipeline/skia/AnimatedDrawables.h" +#include <SkAnimatedImage.h> #include <SkCanvasStateUtils.h> +#include <SkColorFilter.h> #include <SkColorSpaceXformCanvas.h> -#include <SkDrawable.h> #include <SkDeque.h> #include <SkDrawFilter.h> +#include <SkDrawable.h> #include <SkGraphics.h> #include <SkImage.h> #include <SkImagePriv.h> +#include <SkPicture.h> #include <SkRSXform.h> #include <SkShader.h> #include <SkTemplates.h> @@ -46,25 +49,34 @@ Canvas* Canvas::create_canvas(const SkBitmap& bitmap) { return new SkiaCanvas(bitmap); } -Canvas* Canvas::create_canvas(SkCanvas* skiaCanvas, XformToSRGB xformToSRGB) { - return new SkiaCanvas(skiaCanvas, xformToSRGB); +Canvas* Canvas::create_canvas(SkCanvas* skiaCanvas) { + return new SkiaCanvas(skiaCanvas); } SkiaCanvas::SkiaCanvas() {} -SkiaCanvas::SkiaCanvas(SkCanvas* canvas, XformToSRGB xformToSRGB) - : mCanvas(canvas) -{ - LOG_ALWAYS_FATAL_IF(XformToSRGB::kImmediate == xformToSRGB); -} +SkiaCanvas::SkiaCanvas(SkCanvas* canvas) : mCanvas(canvas) {} SkiaCanvas::SkiaCanvas(const SkBitmap& bitmap) { sk_sp<SkColorSpace> cs = bitmap.refColorSpace(); mCanvasOwned = std::unique_ptr<SkCanvas>(new SkCanvas(bitmap, SkCanvas::ColorBehavior::kLegacy)); - mCanvasWrapper = SkCreateColorSpaceXformCanvas(mCanvasOwned.get(), - cs == nullptr ? SkColorSpace::MakeSRGB() : std::move(cs)); - mCanvas = mCanvasWrapper.get(); + if (cs.get() == nullptr || cs->isSRGB()) { + if (!uirenderer::Properties::isSkiaEnabled()) { + mCanvasWrapper = + SkCreateColorSpaceXformCanvas(mCanvasOwned.get(), SkColorSpace::MakeSRGB()); + mCanvas = mCanvasWrapper.get(); + } else { + mCanvas = mCanvasOwned.get(); + } + } else { + /** The wrapper is needed if we are drawing into a non-sRGB destination, since + * we need to transform all colors (not just bitmaps via filters) into the + * destination's colorspace. + */ + mCanvasWrapper = SkCreateColorSpaceXformCanvas(mCanvasOwned.get(), std::move(cs)); + mCanvas = mCanvasWrapper.get(); + } } SkiaCanvas::~SkiaCanvas() {} @@ -73,9 +85,9 @@ void SkiaCanvas::reset(SkCanvas* skiaCanvas) { if (mCanvas != skiaCanvas) { mCanvas = skiaCanvas; mCanvasOwned.reset(); + mCanvasWrapper.reset(); } mSaveStack.reset(nullptr); - mHighContrastText = false; } // ---------------------------------------------------------------------------- @@ -86,13 +98,17 @@ void SkiaCanvas::setBitmap(const SkBitmap& bitmap) { sk_sp<SkColorSpace> cs = bitmap.refColorSpace(); std::unique_ptr<SkCanvas> newCanvas = std::unique_ptr<SkCanvas>(new SkCanvas(bitmap, SkCanvas::ColorBehavior::kLegacy)); - std::unique_ptr<SkCanvas> newCanvasWrapper = SkCreateColorSpaceXformCanvas(newCanvas.get(), - cs == nullptr ? SkColorSpace::MakeSRGB() : std::move(cs)); + std::unique_ptr<SkCanvas> newCanvasWrapper; + if (cs.get() != nullptr && !cs->isSRGB()) { + newCanvasWrapper = SkCreateColorSpaceXformCanvas(newCanvas.get(), std::move(cs)); + } else if (!uirenderer::Properties::isSkiaEnabled()) { + newCanvasWrapper = SkCreateColorSpaceXformCanvas(newCanvas.get(), SkColorSpace::MakeSRGB()); + } // deletes the previously owned canvas (if any) mCanvasOwned = std::move(newCanvas); mCanvasWrapper = std::move(newCanvasWrapper); - mCanvas = mCanvasWrapper.get(); + mCanvas = mCanvasWrapper ? mCanvasWrapper.get() : mCanvasOwned.get(); // clean up the old save stack mSaveStack.reset(nullptr); @@ -141,7 +157,7 @@ void SkiaCanvas::restore() { } bool preserveMatrix = !(rec->saveFlags & SaveFlags::Matrix); - bool preserveClip = !(rec->saveFlags & SaveFlags::Clip); + bool preserveClip = !(rec->saveFlags & SaveFlags::Clip); SkMatrix savedMatrix; if (preserveMatrix) { @@ -183,16 +199,16 @@ static inline SkCanvas::SaveLayerFlags layerFlags(SaveFlags::Flags flags) { return layerFlags; } -int SkiaCanvas::saveLayer(float left, float top, float right, float bottom, - const SkPaint* paint, SaveFlags::Flags flags) { +int SkiaCanvas::saveLayer(float left, float top, float right, float bottom, const SkPaint* paint, + SaveFlags::Flags flags) { const SkRect bounds = SkRect::MakeLTRB(left, top, right, bottom); const SkCanvas::SaveLayerRec rec(&bounds, paint, layerFlags(flags)); return mCanvas->saveLayer(rec); } -int SkiaCanvas::saveLayerAlpha(float left, float top, float right, float bottom, - int alpha, SaveFlags::Flags flags) { +int SkiaCanvas::saveLayerAlpha(float left, float top, float right, float bottom, int alpha, + SaveFlags::Flags flags) { if (static_cast<unsigned>(alpha) < 0xFF) { SkPaint alphaPaint; alphaPaint.setAlpha(alpha); @@ -204,24 +220,24 @@ int SkiaCanvas::saveLayerAlpha(float left, float top, float right, float bottom, class SkiaCanvas::Clip { public: Clip(const SkRect& rect, SkClipOp op, const SkMatrix& m) - : mType(Type::Rect), mOp(op), mMatrix(m), mRRect(SkRRect::MakeRect(rect)) {} + : mType(Type::Rect), mOp(op), mMatrix(m), mRRect(SkRRect::MakeRect(rect)) {} Clip(const SkRRect& rrect, SkClipOp op, const SkMatrix& m) - : mType(Type::RRect), mOp(op), mMatrix(m), mRRect(rrect) {} + : mType(Type::RRect), mOp(op), mMatrix(m), mRRect(rrect) {} Clip(const SkPath& path, SkClipOp op, const SkMatrix& m) - : mType(Type::Path), mOp(op), mMatrix(m), mPath(&path) {} + : mType(Type::Path), mOp(op), mMatrix(m), mPath(&path) {} void apply(SkCanvas* canvas) const { canvas->setMatrix(mMatrix); switch (mType) { - case Type::Rect: - canvas->clipRect(mRRect.rect(), mOp); - break; - case Type::RRect: - canvas->clipRRect(mRRect, mOp); - break; - case Type::Path: - canvas->clipPath(*mPath.get(), mOp); - break; + case Type::Rect: + canvas->clipRect(mRRect.rect(), mOp); + break; + case Type::RRect: + canvas->clipRRect(mRRect, mOp); + break; + case Type::Path: + canvas->clipPath(*mPath.get(), mOp); + break; } } @@ -232,19 +248,17 @@ private: Path, }; - Type mType; - SkClipOp mOp; - SkMatrix mMatrix; + Type mType; + SkClipOp mOp; + SkMatrix mMatrix; // These are logically a union (tracked separately due to non-POD path). SkTLazy<SkPath> mPath; - SkRRect mRRect; + SkRRect mRRect; }; const SkiaCanvas::SaveRec* SkiaCanvas::currentSaveRec() const { - const SaveRec* rec = mSaveStack - ? static_cast<const SaveRec*>(mSaveStack->back()) - : nullptr; + const SaveRec* rec = mSaveStack ? static_cast<const SaveRec*>(mSaveStack->back()) : nullptr; int currentSaveCount = mCanvas->getSaveCount(); SkASSERT(!rec || currentSaveCount >= rec->saveCount); @@ -422,10 +436,9 @@ SkCanvasState* SkiaCanvas::captureCanvasState() const { // translate & scale transforms, and a simple rectangular clip. // (This also avoids significant wasted time in calling // SkCanvasStateUtils::CaptureCanvasState when the clip is complex). - if (!canvas->isClipRect() || - (canvas->getTotalMatrix().getType() & - ~(SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask))) { - return nullptr; + if (!canvas->isClipRect() || (canvas->getTotalMatrix().getType() & + ~(SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask))) { + return nullptr; } return SkCanvasStateUtils::CaptureCanvasState(canvas); @@ -451,7 +464,7 @@ void SkiaCanvas::drawPoints(const float* points, int count, const SkPaint& paint SkCanvas::PointMode mode) { if (CC_UNLIKELY(count < 2 || paint.nothingToDraw())) return; // convert the floats into SkPoints - count >>= 1; // now it is the number of points + count >>= 1; // now it is the number of points std::unique_ptr<SkPoint[]> pts(new SkPoint[count]); for (int i = 0; i < count; i++) { pts[i].set(points[0], points[1]); @@ -460,7 +473,6 @@ void SkiaCanvas::drawPoints(const float* points, int count, const SkPaint& paint mCanvas->drawPoints(mode, count, pts.get(), paint); } - void SkiaCanvas::drawPoint(float x, float y, const SkPaint& paint) { mCanvas->drawPoint(x, y, paint); } @@ -479,11 +491,9 @@ void SkiaCanvas::drawLines(const float* points, int count, const SkPaint& paint) this->drawPoints(points, count, paint, SkCanvas::kLines_PointMode); } -void SkiaCanvas::drawRect(float left, float top, float right, float bottom, - const SkPaint& paint) { +void SkiaCanvas::drawRect(float left, float top, float right, float bottom, const SkPaint& paint) { if (CC_UNLIKELY(paint.nothingToDraw())) return; mCanvas->drawRect({left, top, right, bottom}, paint); - } void SkiaCanvas::drawRegion(const SkRegion& region, const SkPaint& paint) { @@ -491,8 +501,8 @@ void SkiaCanvas::drawRegion(const SkRegion& region, const SkPaint& paint) { mCanvas->drawRegion(region, paint); } -void SkiaCanvas::drawRoundRect(float left, float top, float right, float bottom, - float rx, float ry, const SkPaint& paint) { +void SkiaCanvas::drawRoundRect(float left, float top, float right, float bottom, float rx, float ry, + const SkPaint& paint) { if (CC_UNLIKELY(paint.nothingToDraw())) return; SkRect rect = SkRect::MakeLTRB(left, top, right, bottom); mCanvas->drawRoundRect(rect, rx, ry, paint); @@ -509,15 +519,22 @@ void SkiaCanvas::drawOval(float left, float top, float right, float bottom, cons mCanvas->drawOval(oval, paint); } -void SkiaCanvas::drawArc(float left, float top, float right, float bottom, - float startAngle, float sweepAngle, bool useCenter, const SkPaint& paint) { +void SkiaCanvas::drawArc(float left, float top, float right, float bottom, float startAngle, + float sweepAngle, bool useCenter, const SkPaint& paint) { if (CC_UNLIKELY(paint.nothingToDraw())) return; SkRect arc = SkRect::MakeLTRB(left, top, right, bottom); - mCanvas->drawArc(arc, startAngle, sweepAngle, useCenter, paint); + if (fabs(sweepAngle) >= 360.0f) { + mCanvas->drawOval(arc, paint); + } else { + mCanvas->drawArc(arc, startAngle, sweepAngle, useCenter, paint); + } } void SkiaCanvas::drawPath(const SkPath& path, const SkPaint& paint) { if (CC_UNLIKELY(paint.nothingToDraw())) return; + if (CC_UNLIKELY(path.isEmpty() && (!path.isInverseFillType()))) { + return; + } mCanvas->drawPath(path, paint); } @@ -529,26 +546,65 @@ void SkiaCanvas::drawVertices(const SkVertices* vertices, SkBlendMode mode, cons // Canvas draw operations: Bitmaps // ---------------------------------------------------------------------------- +const SkPaint* SkiaCanvas::addFilter(const SkPaint* origPaint, SkPaint* tmpPaint, + sk_sp<SkColorFilter> colorSpaceFilter) { + /* We don't apply the colorSpace filter if this canvas is already wrapped with + * a SkColorSpaceXformCanvas since it already takes care of converting the + * contents of the bitmap into the appropriate colorspace. The mCanvasWrapper + * should only be used if this canvas is backed by a surface/bitmap that is known + * to have a non-sRGB colorspace. + */ + if (!mCanvasWrapper && colorSpaceFilter) { + if (origPaint) { + *tmpPaint = *origPaint; + } + + if (tmpPaint->getColorFilter()) { + tmpPaint->setColorFilter( + SkColorFilter::MakeComposeFilter(tmpPaint->refColorFilter(), colorSpaceFilter)); + LOG_ALWAYS_FATAL_IF(!tmpPaint->getColorFilter()); + } else { + tmpPaint->setColorFilter(colorSpaceFilter); + } + + return tmpPaint; + } else { + return origPaint; + } +} + void SkiaCanvas::drawBitmap(Bitmap& bitmap, float left, float top, const SkPaint* paint) { - mCanvas->drawImage(bitmap.makeImage(), left, top, paint); + SkPaint tmpPaint; + sk_sp<SkColorFilter> colorFilter; + sk_sp<SkImage> image = bitmap.makeImage(&colorFilter); + mCanvas->drawImage(image, left, top, addFilter(paint, &tmpPaint, colorFilter)); } -void SkiaCanvas::drawBitmap(Bitmap& hwuiBitmap, const SkMatrix& matrix, const SkPaint* paint) { +void SkiaCanvas::drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, const SkPaint* paint) { SkAutoCanvasRestore acr(mCanvas, true); mCanvas->concat(matrix); - mCanvas->drawImage(hwuiBitmap.makeImage(), 0, 0, paint); + + SkPaint tmpPaint; + sk_sp<SkColorFilter> colorFilter; + sk_sp<SkImage> image = bitmap.makeImage(&colorFilter); + mCanvas->drawImage(image, 0, 0, addFilter(paint, &tmpPaint, colorFilter)); } -void SkiaCanvas::drawBitmap(Bitmap& hwuiBitmap, float srcLeft, float srcTop, - float srcRight, float srcBottom, float dstLeft, float dstTop, - float dstRight, float dstBottom, const SkPaint* paint) { +void SkiaCanvas::drawBitmap(Bitmap& bitmap, float srcLeft, float srcTop, float srcRight, + float srcBottom, float dstLeft, float dstTop, float dstRight, + float dstBottom, const SkPaint* paint) { SkRect srcRect = SkRect::MakeLTRB(srcLeft, srcTop, srcRight, srcBottom); SkRect dstRect = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom); - mCanvas->drawImageRect(hwuiBitmap.makeImage(), srcRect, dstRect, paint); + + SkPaint tmpPaint; + sk_sp<SkColorFilter> colorFilter; + sk_sp<SkImage> image = bitmap.makeImage(&colorFilter); + mCanvas->drawImageRect(image, srcRect, dstRect, addFilter(paint, &tmpPaint, colorFilter), + SkCanvas::kFast_SrcRectConstraint); } -void SkiaCanvas::drawBitmapMesh(Bitmap& hwuiBitmap, int meshWidth, int meshHeight, - const float* vertices, const int* colors, const SkPaint* paint) { +void SkiaCanvas::drawBitmapMesh(Bitmap& bitmap, int meshWidth, int meshHeight, + const float* vertices, const int* colors, const SkPaint* paint) { const int ptCount = (meshWidth + 1) * (meshHeight + 1); const int indexCount = meshWidth * meshHeight * 6; uint32_t flags = SkVertices::kHasTexCoords_BuilderFlag; @@ -565,8 +621,8 @@ void SkiaCanvas::drawBitmapMesh(Bitmap& hwuiBitmap, int meshWidth, int meshHeigh // cons up texture coordinates and indices { - const SkScalar w = SkIntToScalar(hwuiBitmap.width()); - const SkScalar h = SkIntToScalar(hwuiBitmap.height()); + const SkScalar w = SkIntToScalar(bitmap.width()); + const SkScalar h = SkIntToScalar(bitmap.height()); const SkScalar dx = w / meshWidth; const SkScalar dy = h / meshHeight; @@ -612,7 +668,7 @@ void SkiaCanvas::drawBitmapMesh(Bitmap& hwuiBitmap, int meshWidth, int meshHeigh SkASSERT(indexPtr - indices == indexCount); } - // double-check that we have legal indices +// double-check that we have legal indices #ifdef SK_DEBUG { for (int i = 0; i < indexCount; i++) { @@ -627,19 +683,26 @@ void SkiaCanvas::drawBitmapMesh(Bitmap& hwuiBitmap, int meshWidth, int meshHeigh tmpPaint = *paint; } - sk_sp<SkImage> image = hwuiBitmap.makeImage(); - tmpPaint.setShader(image->makeShader(SkShader::kClamp_TileMode, SkShader::kClamp_TileMode)); + sk_sp<SkColorFilter> colorFilter; + sk_sp<SkImage> image = bitmap.makeImage(&colorFilter); + sk_sp<SkShader> shader = + image->makeShader(SkShader::kClamp_TileMode, SkShader::kClamp_TileMode); + if (colorFilter) { + shader = shader->makeWithColorFilter(colorFilter); + } + tmpPaint.setShader(shader); mCanvas->drawVertices(builder.detach(), SkBlendMode::kModulate, tmpPaint); } -void SkiaCanvas::drawNinePatch(Bitmap& hwuiBitmap, const Res_png_9patch& chunk, - float dstLeft, float dstTop, float dstRight, float dstBottom, const SkPaint* paint) { - +void SkiaCanvas::drawNinePatch(Bitmap& bitmap, const Res_png_9patch& chunk, float dstLeft, + float dstTop, float dstRight, float dstBottom, + const SkPaint* paint) { SkCanvas::Lattice lattice; - NinePatchUtils::SetLatticeDivs(&lattice, chunk, hwuiBitmap.width(), hwuiBitmap.height()); + NinePatchUtils::SetLatticeDivs(&lattice, chunk, bitmap.width(), bitmap.height()); - lattice.fFlags = nullptr; + lattice.fRectTypes = nullptr; + lattice.fColors = nullptr; int numFlags = 0; if (chunk.numColors > 0 && chunk.numColors == NinePatchUtils::NumDistinctRects(lattice)) { // We can expect the framework to give us a color for every distinct rect. @@ -647,14 +710,23 @@ void SkiaCanvas::drawNinePatch(Bitmap& hwuiBitmap, const Res_png_9patch& chunk, numFlags = (lattice.fXCount + 1) * (lattice.fYCount + 1); } - SkAutoSTMalloc<25, SkCanvas::Lattice::Flags> flags(numFlags); + SkAutoSTMalloc<25, SkCanvas::Lattice::RectType> flags(numFlags); + SkAutoSTMalloc<25, SkColor> colors(numFlags); if (numFlags > 0) { - NinePatchUtils::SetLatticeFlags(&lattice, flags.get(), numFlags, chunk); + NinePatchUtils::SetLatticeFlags(&lattice, flags.get(), numFlags, chunk, colors.get()); } lattice.fBounds = nullptr; SkRect dst = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom); - mCanvas->drawImageLattice(hwuiBitmap.makeImage().get(), lattice, dst, paint); + + SkPaint tmpPaint; + sk_sp<SkColorFilter> colorFilter; + sk_sp<SkImage> image = bitmap.makeImage(&colorFilter); + mCanvas->drawImageLattice(image.get(), lattice, dst, addFilter(paint, &tmpPaint, colorFilter)); +} + +double SkiaCanvas::drawAnimatedImage(AnimatedImageDrawable* imgDrawable) { + return imgDrawable->drawStaging(mCanvas); } void SkiaCanvas::drawVectorDrawable(VectorDrawableRoot* vectorDrawable) { @@ -666,17 +738,18 @@ void SkiaCanvas::drawVectorDrawable(VectorDrawableRoot* vectorDrawable) { // ---------------------------------------------------------------------------- void SkiaCanvas::drawGlyphs(ReadGlyphFunc glyphFunc, int count, const SkPaint& paint, float x, - float y, float boundsLeft, float boundsTop, float boundsRight, float boundsBottom, - float totalAdvance) { + float y, float boundsLeft, float boundsTop, float boundsRight, + float boundsBottom, float totalAdvance) { if (count <= 0 || paint.nothingToDraw()) return; // Set align to left for drawing, as we don't want individual // glyphs centered or right-aligned; the offset above takes // care of all alignment. SkPaint paintCopy(paint); paintCopy.setTextAlign(SkPaint::kLeft_Align); + SkASSERT(paintCopy.getTextEncoding() == SkPaint::kGlyphID_TextEncoding); - SkRect bounds = SkRect::MakeLTRB(boundsLeft + x, boundsTop + y, - boundsRight + x, boundsBottom + y); + SkRect bounds = + SkRect::MakeLTRB(boundsLeft + x, boundsTop + y, boundsRight + x, boundsBottom + y); SkTextBlobBuilder builder; const SkTextBlobBuilder::RunBuffer& buffer = builder.allocRunPos(paintCopy, count, &bounds); @@ -688,7 +761,8 @@ void SkiaCanvas::drawGlyphs(ReadGlyphFunc glyphFunc, int count, const SkPaint& p } void SkiaCanvas::drawLayoutOnPath(const minikin::Layout& layout, float hOffset, float vOffset, - const SkPaint& paint, const SkPath& path, size_t start, size_t end) { + const SkPaint& paint, const SkPath& path, size_t start, + size_t end) { const int N = end - start; SkAutoSTMalloc<1024, uint8_t> storage(N * (sizeof(uint16_t) + sizeof(SkRSXform))); SkRSXform* xform = (SkRSXform*)storage.get(); @@ -697,7 +771,8 @@ void SkiaCanvas::drawLayoutOnPath(const minikin::Layout& layout, float hOffset, for (size_t i = start; i < end; i++) { glyphs[i - start] = layout.getGlyphId(i); - float x = hOffset + layout.getX(i); + float halfWidth = layout.getCharAdvance(i) * 0.5f; + float x = hOffset + layout.getX(i) + halfWidth; float y = vOffset + layout.getY(i); SkPoint pos; @@ -708,8 +783,8 @@ void SkiaCanvas::drawLayoutOnPath(const minikin::Layout& layout, float hOffset, } xform[i - start].fSCos = tan.x(); xform[i - start].fSSin = tan.y(); - xform[i - start].fTx = pos.x() - tan.y() * y; - xform[i - start].fTy = pos.y() + tan.x() * y; + xform[i - start].fTx = pos.x() - tan.y() * y - halfWidth * tan.x(); + xform[i - start].fTy = pos.y() + tan.x() * y - halfWidth * tan.y(); } this->asSkCanvas()->drawTextRSXform(glyphs, sizeof(uint16_t) * N, xform, nullptr, paint); @@ -720,17 +795,24 @@ void SkiaCanvas::drawLayoutOnPath(const minikin::Layout& layout, float hOffset, // ---------------------------------------------------------------------------- void SkiaCanvas::drawRoundRect(uirenderer::CanvasPropertyPrimitive* left, - uirenderer::CanvasPropertyPrimitive* top, uirenderer::CanvasPropertyPrimitive* right, - uirenderer::CanvasPropertyPrimitive* bottom, uirenderer::CanvasPropertyPrimitive* rx, - uirenderer::CanvasPropertyPrimitive* ry, uirenderer::CanvasPropertyPaint* paint) { + uirenderer::CanvasPropertyPrimitive* top, + uirenderer::CanvasPropertyPrimitive* right, + uirenderer::CanvasPropertyPrimitive* bottom, + uirenderer::CanvasPropertyPrimitive* rx, + uirenderer::CanvasPropertyPrimitive* ry, + uirenderer::CanvasPropertyPaint* paint) { sk_sp<uirenderer::skiapipeline::AnimatedRoundRect> drawable( - new uirenderer::skiapipeline::AnimatedRoundRect(left, top, right, bottom, rx, ry, paint)); + new uirenderer::skiapipeline::AnimatedRoundRect(left, top, right, bottom, rx, ry, + paint)); mCanvas->drawDrawable(drawable.get()); } -void SkiaCanvas::drawCircle(uirenderer::CanvasPropertyPrimitive* x, uirenderer::CanvasPropertyPrimitive* y, - uirenderer::CanvasPropertyPrimitive* radius, uirenderer::CanvasPropertyPaint* paint) { - sk_sp<uirenderer::skiapipeline::AnimatedCircle> drawable(new uirenderer::skiapipeline::AnimatedCircle(x, y, radius, paint)); +void SkiaCanvas::drawCircle(uirenderer::CanvasPropertyPrimitive* x, + uirenderer::CanvasPropertyPrimitive* y, + uirenderer::CanvasPropertyPrimitive* radius, + uirenderer::CanvasPropertyPaint* paint) { + sk_sp<uirenderer::skiapipeline::AnimatedCircle> drawable( + new uirenderer::skiapipeline::AnimatedCircle(x, y, radius, paint)); mCanvas->drawDrawable(drawable.get()); } @@ -747,8 +829,8 @@ void SkiaCanvas::drawRenderNode(uirenderer::RenderNode* renderNode) { } void SkiaCanvas::callDrawGLFunction(Functor* functor, - uirenderer::GlFunctorLifecycleListener* listener) { + uirenderer::GlFunctorLifecycleListener* listener) { LOG_ALWAYS_FATAL("SkiaCanvas can't directly draw GL Content"); } -} // namespace android +} // namespace android diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h index af2c23e4a2b7..3efc22a03cdf 100644 --- a/libs/hwui/SkiaCanvas.h +++ b/libs/hwui/SkiaCanvas.h @@ -15,11 +15,11 @@ */ #pragma once -#include "hwui/Canvas.h" #include "CanvasProperty.h" #include "DeferredLayerUpdater.h" #include "RenderNode.h" #include "VectorDrawable.h" +#include "hwui/Canvas.h" #include <SkCanvas.h> #include <SkTLazy.h> @@ -37,21 +37,15 @@ public: * @param canvas SkCanvas to handle calls made to this SkiaCanvas. Must * not be NULL. This constructor does not take ownership, so the caller * must guarantee that it remains valid while the SkiaCanvas is valid. - * @param xformToSRGB Indicates if bitmaps should be xformed to the sRGB - * color space before drawing. This makes sense for software rendering. - * For the picture case, it may make more sense to leave bitmaps as is, - * and handle the xform when replaying the picture. */ - explicit SkiaCanvas(SkCanvas* canvas, XformToSRGB xformToSRGB); + explicit SkiaCanvas(SkCanvas* canvas); virtual ~SkiaCanvas(); - virtual SkCanvas* asSkCanvas() override { - return mCanvas; - } + virtual SkCanvas* asSkCanvas() override { return mCanvas; } virtual void resetRecording(int width, int height, - uirenderer::RenderNode* renderNode) override { + uirenderer::RenderNode* renderNode) override { LOG_ALWAYS_FATAL("SkiaCanvas cannot be reset as a recording canvas"); } @@ -69,20 +63,15 @@ public: virtual int width() override; virtual int height() override; - virtual void setHighContrastText(bool highContrastText) override { - mHighContrastText = highContrastText; - } - virtual bool isHighContrastText() override { return mHighContrastText; } - virtual int getSaveCount() const override; virtual int save(SaveFlags::Flags flags) override; virtual void restore() override; virtual void restoreToCount(int saveCount) override; - virtual int saveLayer(float left, float top, float right, float bottom, - const SkPaint* paint, SaveFlags::Flags flags) override; - virtual int saveLayerAlpha(float left, float top, float right, float bottom, - int alpha, SaveFlags::Flags flags) override; + virtual int saveLayer(float left, float top, float right, float bottom, const SkPaint* paint, + SaveFlags::Flags flags) override; + virtual int saveLayerAlpha(float left, float top, float right, float bottom, int alpha, + SaveFlags::Flags flags) override; virtual void getMatrix(SkMatrix* outMatrix) const override; virtual void setMatrix(const SkMatrix& matrix) override; @@ -95,8 +84,7 @@ public: virtual bool getClipBounds(SkRect* outRect) const override; virtual bool quickRejectRect(float left, float top, float right, float bottom) const override; virtual bool quickRejectPath(const SkPath& path) const override; - virtual bool clipRect(float left, float top, float right, float bottom, - SkClipOp op) override; + virtual bool clipRect(float left, float top, float right, float bottom, SkClipOp op) override; virtual bool clipPath(const SkPath* path, SkClipOp op) override; virtual SkDrawFilter* getDrawFilter() override; @@ -110,47 +98,53 @@ public: virtual void drawPoint(float x, float y, const SkPaint& paint) override; virtual void drawPoints(const float* points, int count, const SkPaint& paint) override; virtual void drawLine(float startX, float startY, float stopX, float stopY, - const SkPaint& paint) override; + const SkPaint& paint) override; virtual void drawLines(const float* points, int count, const SkPaint& paint) override; virtual void drawRect(float left, float top, float right, float bottom, - const SkPaint& paint) override; + const SkPaint& paint) override; virtual void drawRegion(const SkRegion& region, const SkPaint& paint) override; - virtual void drawRoundRect(float left, float top, float right, float bottom, - float rx, float ry, const SkPaint& paint) override; + virtual void drawRoundRect(float left, float top, float right, float bottom, float rx, float ry, + const SkPaint& paint) override; virtual void drawCircle(float x, float y, float radius, const SkPaint& paint) override; virtual void drawOval(float left, float top, float right, float bottom, - const SkPaint& paint) override; - virtual void drawArc(float left, float top, float right, float bottom, - float startAngle, float sweepAngle, bool useCenter, const SkPaint& paint) override; + const SkPaint& paint) override; + virtual void drawArc(float left, float top, float right, float bottom, float startAngle, + float sweepAngle, bool useCenter, const SkPaint& paint) override; virtual void drawPath(const SkPath& path, const SkPaint& paint) override; virtual void drawVertices(const SkVertices*, SkBlendMode, const SkPaint& paint) override; virtual void drawBitmap(Bitmap& bitmap, float left, float top, const SkPaint* paint) override; virtual void drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, const SkPaint* paint) override; - virtual void drawBitmap(Bitmap& bitmap, float srcLeft, float srcTop, - float srcRight, float srcBottom, float dstLeft, float dstTop, - float dstRight, float dstBottom, const SkPaint* paint) override; + virtual void drawBitmap(Bitmap& bitmap, float srcLeft, float srcTop, float srcRight, + float srcBottom, float dstLeft, float dstTop, float dstRight, + float dstBottom, const SkPaint* paint) override; virtual void drawBitmapMesh(Bitmap& bitmap, int meshWidth, int meshHeight, - const float* vertices, const int* colors, const SkPaint* paint) override; - virtual void drawNinePatch(Bitmap& bitmap, const android::Res_png_9patch& chunk, - float dstLeft, float dstTop, float dstRight, float dstBottom, - const SkPaint* paint) override; - - virtual bool drawTextAbsolutePos() const override { return true; } + const float* vertices, const int* colors, + const SkPaint* paint) override; + virtual void drawNinePatch(Bitmap& bitmap, const android::Res_png_9patch& chunk, float dstLeft, + float dstTop, float dstRight, float dstBottom, + const SkPaint* paint) override; + virtual double drawAnimatedImage(AnimatedImageDrawable* imgDrawable) override; + + virtual bool drawTextAbsolutePos() const override { return true; } virtual void drawVectorDrawable(VectorDrawableRoot* vectorDrawable) override; virtual void drawRoundRect(uirenderer::CanvasPropertyPrimitive* left, - uirenderer::CanvasPropertyPrimitive* top, uirenderer::CanvasPropertyPrimitive* right, - uirenderer::CanvasPropertyPrimitive* bottom, uirenderer::CanvasPropertyPrimitive* rx, - uirenderer::CanvasPropertyPrimitive* ry, uirenderer::CanvasPropertyPaint* paint) override; + uirenderer::CanvasPropertyPrimitive* top, + uirenderer::CanvasPropertyPrimitive* right, + uirenderer::CanvasPropertyPrimitive* bottom, + uirenderer::CanvasPropertyPrimitive* rx, + uirenderer::CanvasPropertyPrimitive* ry, + uirenderer::CanvasPropertyPaint* paint) override; virtual void drawCircle(uirenderer::CanvasPropertyPrimitive* x, - uirenderer::CanvasPropertyPrimitive* y, uirenderer::CanvasPropertyPrimitive* radius, - uirenderer::CanvasPropertyPaint* paint) override; + uirenderer::CanvasPropertyPrimitive* y, + uirenderer::CanvasPropertyPrimitive* radius, + uirenderer::CanvasPropertyPaint* paint) override; virtual void drawLayer(uirenderer::DeferredLayerUpdater* layerHandle) override; virtual void drawRenderNode(uirenderer::RenderNode* renderNode) override; virtual void callDrawGLFunction(Functor* functor, - uirenderer::GlFunctorLifecycleListener* listener) override; + uirenderer::GlFunctorLifecycleListener* listener) override; protected: SkiaCanvas(); @@ -158,39 +152,39 @@ protected: void drawDrawable(SkDrawable* drawable) { mCanvas->drawDrawable(drawable); } virtual void drawGlyphs(ReadGlyphFunc glyphFunc, int count, const SkPaint& paint, float x, - float y, float boundsLeft, float boundsTop, float boundsRight, float boundsBottom, - float totalAdvance) override; + float y, float boundsLeft, float boundsTop, float boundsRight, + float boundsBottom, float totalAdvance) override; virtual void drawLayoutOnPath(const minikin::Layout& layout, float hOffset, float vOffset, - const SkPaint& paint, const SkPath& path, size_t start, size_t end) override; + const SkPaint& paint, const SkPath& path, size_t start, + size_t end) override; private: struct SaveRec { - int saveCount; + int saveCount; SaveFlags::Flags saveFlags; - size_t clipIndex; + size_t clipIndex; }; - bool mHighContrastText = false; - const SaveRec* currentSaveRec() const; void recordPartialSave(SaveFlags::Flags flags); - template<typename T> + template <typename T> void recordClip(const T&, SkClipOp); void applyPersistentClips(size_t clipStartIndex); - void drawPoints(const float* points, int count, const SkPaint& paint, - SkCanvas::PointMode mode); + void drawPoints(const float* points, int count, const SkPaint& paint, SkCanvas::PointMode mode); + + const SkPaint* addFilter(const SkPaint* origPaint, SkPaint* tmpPaint, + sk_sp<SkColorFilter> colorSpaceFilter); class Clip; - std::unique_ptr<SkCanvas> mCanvasWrapper; // might own a wrapper on the canvas - std::unique_ptr<SkCanvas> mCanvasOwned; // might own a canvas we allocated - SkCanvas* mCanvas; // we do NOT own this canvas, it must survive us - // unless it is the same as mCanvasOwned.get() - std::unique_ptr<SkDeque> mSaveStack; // lazily allocated, tracks partial saves. - std::vector<Clip> mClipStack; // tracks persistent clips. + std::unique_ptr<SkCanvas> mCanvasWrapper; // might own a wrapper on the canvas + std::unique_ptr<SkCanvas> mCanvasOwned; // might own a canvas we allocated + SkCanvas* mCanvas; // we do NOT own this canvas, it must survive us + // unless it is the same as mCanvasOwned.get() + std::unique_ptr<SkDeque> mSaveStack; // lazily allocated, tracks partial saves. + std::vector<Clip> mClipStack; // tracks persistent clips. }; -} // namespace android - +} // namespace android diff --git a/libs/hwui/SkiaCanvasProxy.cpp b/libs/hwui/SkiaCanvasProxy.cpp index 16a19ca4d36a..06e2d6c00ef5 100644 --- a/libs/hwui/SkiaCanvasProxy.cpp +++ b/libs/hwui/SkiaCanvasProxy.cpp @@ -20,18 +20,18 @@ #include <log/log.h> -#include "hwui/Bitmap.h" #include <SkLatticeIter.h> -#include <SkPatchUtils.h> #include <SkPaint.h> +#include <SkPatchUtils.h> #include <SkPath.h> #include <SkPixelRef.h> -#include <SkRect.h> #include <SkRRect.h> #include <SkRSXform.h> +#include <SkRect.h> #include <SkSurface.h> #include <SkTextBlobRunIterator.h> #include <SkVertices.h> +#include "hwui/Bitmap.h" namespace android { namespace uirenderer { @@ -46,13 +46,13 @@ void SkiaCanvasProxy::onDrawPaint(const SkPaint& paint) { } void SkiaCanvasProxy::onDrawPoints(PointMode pointMode, size_t count, const SkPoint pts[], - const SkPaint& paint) { + const SkPaint& paint) { if (!pts || count == 0) { return; } // convert the SkPoints into floats - static_assert(sizeof(SkPoint) == sizeof(float)*2, "SkPoint is no longer two floats"); + static_assert(sizeof(SkPoint) == sizeof(float) * 2, "SkPoint is no longer two floats"); const size_t floatCount = count << 1; const float* floatArray = &pts[0].fX; @@ -72,7 +72,7 @@ void SkiaCanvasProxy::onDrawPoints(PointMode pointMode, size_t count, const SkPo SkPath path; for (size_t i = 0; i < count - 1; i++) { path.moveTo(pts[i]); - path.lineTo(pts[i+1]); + path.lineTo(pts[i + 1]); this->drawPath(path, strokedPaint); path.rewind(); } @@ -95,8 +95,8 @@ void SkiaCanvasProxy::onDrawRRect(const SkRRect& roundRect, const SkPaint& paint if (!roundRect.isComplex()) { const SkRect& rect = roundRect.rect(); SkVector radii = roundRect.getSimpleRadii(); - mCanvas->drawRoundRect(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, - radii.fX, radii.fY, paint); + mCanvas->drawRoundRect(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, radii.fX, radii.fY, + paint); } else { SkPath path; path.addRRect(roundRect); @@ -106,8 +106,8 @@ void SkiaCanvasProxy::onDrawRRect(const SkRRect& roundRect, const SkPaint& paint void SkiaCanvasProxy::onDrawArc(const SkRect& rect, SkScalar startAngle, SkScalar sweepAngle, bool useCenter, const SkPaint& paint) { - mCanvas->drawArc(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, - startAngle, sweepAngle, useCenter, paint); + mCanvas->drawArc(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, startAngle, sweepAngle, + useCenter, paint); } void SkiaCanvasProxy::onDrawPath(const SkPath& path, const SkPaint& paint) { @@ -115,41 +115,38 @@ void SkiaCanvasProxy::onDrawPath(const SkPath& path, const SkPaint& paint) { } void SkiaCanvasProxy::onDrawBitmap(const SkBitmap& bitmap, SkScalar left, SkScalar top, - const SkPaint* paint) { + const SkPaint* paint) { sk_sp<Bitmap> hwuiBitmap = Bitmap::createFrom(bitmap.info(), *bitmap.pixelRef()); // HWUI doesn't support extractSubset(), so convert any subsetted bitmap into // a drawBitmapRect(); pass through an un-subsetted bitmap. if (hwuiBitmap && bitmap.dimensions() != hwuiBitmap->info().dimensions()) { SkIPoint origin = bitmap.pixelRefOrigin(); - mCanvas->drawBitmap(*hwuiBitmap, origin.fX, origin.fY, - origin.fX + bitmap.dimensions().width(), - origin.fY + bitmap.dimensions().height(), - left, top, - left + bitmap.dimensions().width(), - top + bitmap.dimensions().height(), - paint); + mCanvas->drawBitmap( + *hwuiBitmap, origin.fX, origin.fY, origin.fX + bitmap.dimensions().width(), + origin.fY + bitmap.dimensions().height(), left, top, + left + bitmap.dimensions().width(), top + bitmap.dimensions().height(), paint); } else { mCanvas->drawBitmap(*hwuiBitmap, left, top, paint); } } void SkiaCanvasProxy::onDrawBitmapRect(const SkBitmap& skBitmap, const SkRect* srcPtr, - const SkRect& dst, const SkPaint* paint, SrcRectConstraint) { + const SkRect& dst, const SkPaint* paint, SrcRectConstraint) { SkRect src = (srcPtr) ? *srcPtr : SkRect::MakeWH(skBitmap.width(), skBitmap.height()); // TODO: if bitmap is a subset, do we need to add pixelRefOrigin to src? - Bitmap* bitmap = reinterpret_cast<Bitmap*>(skBitmap.pixelRef()); - mCanvas->drawBitmap(*bitmap, src.fLeft, src.fTop, src.fRight, src.fBottom, - dst.fLeft, dst.fTop, dst.fRight, dst.fBottom, paint); + Bitmap* bitmap = reinterpret_cast<Bitmap*>(skBitmap.pixelRef()); + mCanvas->drawBitmap(*bitmap, src.fLeft, src.fTop, src.fRight, src.fBottom, dst.fLeft, dst.fTop, + dst.fRight, dst.fBottom, paint); } void SkiaCanvasProxy::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, - const SkRect& dst, const SkPaint*) { - //TODO make nine-patch drawing a method on Canvas.h + const SkRect& dst, const SkPaint*) { + // TODO make nine-patch drawing a method on Canvas.h SkDEBUGFAIL("SkiaCanvasProxy::onDrawBitmapNine is not yet supported"); } void SkiaCanvasProxy::onDrawImage(const SkImage* image, SkScalar left, SkScalar top, - const SkPaint* paint) { + const SkPaint* paint) { SkBitmap skiaBitmap; SkPixmap pixmap; if (image->peekPixels(&pixmap) && skiaBitmap.installPixels(pixmap)) { @@ -158,33 +155,33 @@ void SkiaCanvasProxy::onDrawImage(const SkImage* image, SkScalar left, SkScalar } void SkiaCanvasProxy::onDrawImageRect(const SkImage* image, const SkRect* srcPtr, const SkRect& dst, - const SkPaint* paint, SrcRectConstraint constraint) { + const SkPaint* paint, SrcRectConstraint constraint) { SkBitmap skiaBitmap; SkPixmap pixmap; if (image->peekPixels(&pixmap) && skiaBitmap.installPixels(pixmap)) { sk_sp<Bitmap> bitmap = Bitmap::createFrom(skiaBitmap.info(), *skiaBitmap.pixelRef()); SkRect src = (srcPtr) ? *srcPtr : SkRect::MakeWH(image->width(), image->height()); - mCanvas->drawBitmap(*bitmap, src.fLeft, src.fTop, src.fRight, src.fBottom, - dst.fLeft, dst.fTop, dst.fRight, dst.fBottom, paint); + mCanvas->drawBitmap(*bitmap, src.fLeft, src.fTop, src.fRight, src.fBottom, dst.fLeft, + dst.fTop, dst.fRight, dst.fBottom, paint); } } void SkiaCanvasProxy::onDrawImageNine(const SkImage*, const SkIRect& center, const SkRect& dst, - const SkPaint*) { + const SkPaint*) { SkDEBUGFAIL("SkiaCanvasProxy::onDrawImageNine is not yet supported"); } void SkiaCanvasProxy::onDrawImageLattice(const SkImage* image, const Lattice& lattice, - const SkRect& dst, const SkPaint* paint) { + const SkRect& dst, const SkPaint* paint) { SkLatticeIter iter(lattice, dst); SkRect srcR, dstR; while (iter.next(&srcR, &dstR)) { - onDrawImageRect(image, &srcR, dstR, paint, SkCanvas::kStrict_SrcRectConstraint); + onDrawImageRect(image, &srcR, dstR, paint, SkCanvas::kFast_SrcRectConstraint); } } void SkiaCanvasProxy::onDrawVerticesObject(const SkVertices* vertices, SkBlendMode bmode, - const SkPaint& paint) { + const SkPaint& paint) { if (mFilterHwuiCalls) { return; } @@ -214,7 +211,8 @@ static inline SaveFlags::Flags saveFlags(SkCanvas::SaveLayerFlags layerFlags) { return saveFlags; } -SkCanvas::SaveLayerStrategy SkiaCanvasProxy::getSaveLayerStrategy(const SaveLayerRec& saveLayerRec) { +SkCanvas::SaveLayerStrategy SkiaCanvasProxy::getSaveLayerStrategy( + const SaveLayerRec& saveLayerRec) { SkRect rect; if (saveLayerRec.fBounds) { rect = *saveLayerRec.fBounds; @@ -239,7 +237,7 @@ void SkiaCanvasProxy::didSetMatrix(const SkMatrix& matrix) { } void SkiaCanvasProxy::onDrawDRRect(const SkRRect& outer, const SkRRect& inner, - const SkPaint& paint) { + const SkPaint& paint) { SkPath path; path.addRRect(outer); path.addRRect(inner); @@ -259,7 +257,7 @@ public: glyphIDs = (uint16_t*)text; count = byteLength >> 1; } else { - // ensure space for one glyph per ID given UTF8 encoding. + // ensure space for one glyph per ID given UTF8 encoding. storage.reset(new uint16_t[byteLength]); glyphIDs = storage.get(); count = paint.textToGlyphs(text, byteLength, storage.get()); @@ -270,12 +268,13 @@ public: SkPaint paint; uint16_t* glyphIDs; int count; + private: std::unique_ptr<uint16_t[]> storage; }; void SkiaCanvasProxy::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y, - const SkPaint& origPaint) { + const SkPaint& origPaint) { // convert to glyphIDs if necessary GlyphIDConverter glyphs(text, byteLength, origPaint); @@ -309,14 +308,14 @@ void SkiaCanvasProxy::onDrawText(const void* text, size_t byteLength, SkScalar x int xBaseline = 0; int yBaseline = 0; if (mCanvas->drawTextAbsolutePos()) { - bounds.offset(x,y); + bounds.offset(x, y); xBaseline = x; yBaseline = y; } - static_assert(sizeof(SkPoint) == sizeof(float)*2, "SkPoint is no longer two floats"); - auto glyphFunc = [&] (uint16_t* text, float* positions) { - memcpy(text, glyphs.glyphIDs, glyphs.count*sizeof(uint16_t)); + static_assert(sizeof(SkPoint) == sizeof(float) * 2, "SkPoint is no longer two floats"); + auto glyphFunc = [&](uint16_t* text, float* positions) { + memcpy(text, glyphs.glyphIDs, glyphs.count * sizeof(uint16_t)); size_t posIndex = 0; // setup the first glyph position positions[posIndex++] = xBaseline; @@ -326,24 +325,24 @@ void SkiaCanvasProxy::onDrawText(const void* text, size_t byteLength, SkScalar x float yPosition = yBaseline; for (int i = 1; i < glyphs.count; i++) { positions[posIndex++] = xBaseline; - yPosition += glyphWidths[i-1]; + yPosition += glyphWidths[i - 1]; positions[posIndex++] = yPosition; } } else { float xPosition = xBaseline; for (int i = 1; i < glyphs.count; i++) { - xPosition += glyphWidths[i-1]; + xPosition += glyphWidths[i - 1]; positions[posIndex++] = xPosition; positions[posIndex++] = yBaseline; } } }; mCanvas->drawGlyphs(glyphFunc, glyphs.count, glyphs.paint, x, y, bounds.fLeft, bounds.fTop, - bounds.fRight, bounds.fBottom, 0); + bounds.fRight, bounds.fBottom, 0); } void SkiaCanvasProxy::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[], - const SkPaint& origPaint) { + const SkPaint& origPaint) { // convert to glyphIDs if necessary GlyphIDConverter glyphs(text, byteLength, origPaint); @@ -369,11 +368,11 @@ void SkiaCanvasProxy::onDrawPosText(const void* text, size_t byteLength, const S bounds.join(glyphBounds); } - static_assert(sizeof(SkPoint) == sizeof(float)*2, "SkPoint is no longer two floats"); - auto glyphFunc = [&] (uint16_t* text, float* positions) { - memcpy(text, glyphs.glyphIDs, glyphs.count*sizeof(uint16_t)); + static_assert(sizeof(SkPoint) == sizeof(float) * 2, "SkPoint is no longer two floats"); + auto glyphFunc = [&](uint16_t* text, float* positions) { + memcpy(text, glyphs.glyphIDs, glyphs.count * sizeof(uint16_t)); if (mCanvas->drawTextAbsolutePos()) { - memcpy(positions, pos, 2*glyphs.count*sizeof(float)); + memcpy(positions, pos, 2 * glyphs.count * sizeof(float)); } else { for (int i = 0, posIndex = 0; i < glyphs.count; i++) { positions[posIndex++] = pos[i].fX - x; @@ -382,11 +381,11 @@ void SkiaCanvasProxy::onDrawPosText(const void* text, size_t byteLength, const S } }; mCanvas->drawGlyphs(glyphFunc, glyphs.count, glyphs.paint, x, y, bounds.fLeft, bounds.fTop, - bounds.fRight, bounds.fBottom, 0); + bounds.fRight, bounds.fBottom, 0); } void SkiaCanvasProxy::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[], - SkScalar constY, const SkPaint& paint) { + SkScalar constY, const SkPaint& paint) { const size_t pointCount = byteLength >> 1; std::unique_ptr<SkPoint[]> pts(new SkPoint[pointCount]); for (size_t i = 0; i < pointCount; i++) { @@ -396,13 +395,14 @@ void SkiaCanvasProxy::onDrawPosTextH(const void* text, size_t byteLength, const } void SkiaCanvasProxy::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path, - const SkMatrix* matrix, const SkPaint& origPaint) { + const SkMatrix* matrix, const SkPaint& origPaint) { SkDEBUGFAIL("SkiaCanvasProxy::onDrawTextOnPath is not supported"); } void SkiaCanvasProxy::onDrawTextRSXform(const void* text, size_t byteLength, - const SkRSXform xform[], const SkRect* cullRect, const SkPaint& paint) { - GlyphIDConverter glyphs(text, byteLength, paint); // Just get count + const SkRSXform xform[], const SkRect* cullRect, + const SkPaint& paint) { + GlyphIDConverter glyphs(text, byteLength, paint); // Just get count SkMatrix localM, currM, origM; mCanvas->getMatrix(&currM); origM = currM; @@ -410,55 +410,56 @@ void SkiaCanvasProxy::onDrawTextRSXform(const void* text, size_t byteLength, localM.setRSXform(*xform++); currM.setConcat(origM, localM); mCanvas->setMatrix(currM); - this->onDrawText((char*)text + (byteLength / glyphs.count * i), - byteLength / glyphs.count, 0, 0, paint); + this->onDrawText((char*)text + (byteLength / glyphs.count * i), byteLength / glyphs.count, + 0, 0, paint); } mCanvas->setMatrix(origM); } - void SkiaCanvasProxy::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y, - const SkPaint& paint) { + const SkPaint& paint) { SkPaint runPaint = paint; - SkTextBlobRunIterator it(blob); - for (;!it.done(); it.next()) { - size_t textLen = it.glyphCount() * sizeof(uint16_t); - const SkPoint& offset = it.offset(); - // applyFontToPaint() always overwrites the exact same attributes, - // so it is safe to not re-seed the paint for this reason. - it.applyFontToPaint(&runPaint); - - switch (it.positioning()) { - case SkTextBlob::kDefault_Positioning: - this->drawText(it.glyphs(), textLen, x + offset.x(), y + offset.y(), runPaint); - break; - case SkTextBlob::kHorizontal_Positioning: { - std::unique_ptr<SkPoint[]> pts(new SkPoint[it.glyphCount()]); - for (size_t i = 0; i < it.glyphCount(); i++) { - pts[i].set(x + offset.x() + it.pos()[i], y + offset.y()); - } - this->drawPosText(it.glyphs(), textLen, pts.get(), runPaint); - break; - } - case SkTextBlob::kFull_Positioning: { - std::unique_ptr<SkPoint[]> pts(new SkPoint[it.glyphCount()]); - for (size_t i = 0; i < it.glyphCount(); i++) { - const size_t xIndex = i*2; - const size_t yIndex = xIndex + 1; - pts[i].set(x + offset.x() + it.pos()[xIndex], y + offset.y() + it.pos()[yIndex]); - } - this->drawPosText(it.glyphs(), textLen, pts.get(), runPaint); - break; - } - default: - SkFAIL("unhandled positioning mode"); - } - } + SkTextBlobRunIterator it(blob); + for (; !it.done(); it.next()) { + size_t textLen = it.glyphCount() * sizeof(uint16_t); + const SkPoint& offset = it.offset(); + // applyFontToPaint() always overwrites the exact same attributes, + // so it is safe to not re-seed the paint for this reason. + it.applyFontToPaint(&runPaint); + + switch (it.positioning()) { + case SkTextBlob::kDefault_Positioning: + this->drawText(it.glyphs(), textLen, x + offset.x(), y + offset.y(), runPaint); + break; + case SkTextBlob::kHorizontal_Positioning: { + std::unique_ptr<SkPoint[]> pts(new SkPoint[it.glyphCount()]); + for (size_t i = 0; i < it.glyphCount(); i++) { + pts[i].set(x + offset.x() + it.pos()[i], y + offset.y()); + } + this->drawPosText(it.glyphs(), textLen, pts.get(), runPaint); + break; + } + case SkTextBlob::kFull_Positioning: { + std::unique_ptr<SkPoint[]> pts(new SkPoint[it.glyphCount()]); + for (size_t i = 0; i < it.glyphCount(); i++) { + const size_t xIndex = i * 2; + const size_t yIndex = xIndex + 1; + pts[i].set(x + offset.x() + it.pos()[xIndex], + y + offset.y() + it.pos()[yIndex]); + } + this->drawPosText(it.glyphs(), textLen, pts.get(), runPaint); + break; + } + default: + SK_ABORT("unhandled positioning mode"); + } + } } void SkiaCanvasProxy::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4], - const SkPoint texCoords[4], SkBlendMode bmode, const SkPaint& paint) { + const SkPoint texCoords[4], SkBlendMode bmode, + const SkPaint& paint) { if (mFilterHwuiCalls) { return; } @@ -466,9 +467,9 @@ void SkiaCanvasProxy::onDrawPatch(const SkPoint cubics[12], const SkColor colors mCanvas->getMatrix(&matrix); SkISize lod = SkPatchUtils::GetLevelOfDetail(cubics, &matrix); - mCanvas->drawVertices(SkPatchUtils::MakeVertices(cubics, colors, texCoords, - lod.width(), lod.height()).get(), - bmode, paint); + mCanvas->drawVertices( + SkPatchUtils::MakeVertices(cubics, colors, texCoords, lod.width(), lod.height()).get(), + bmode, paint); } void SkiaCanvasProxy::onClipRect(const SkRect& rect, SkClipOp op, ClipEdgeStyle) { @@ -485,5 +486,5 @@ void SkiaCanvasProxy::onClipPath(const SkPath& path, SkClipOp op, ClipEdgeStyle) mCanvas->clipPath(&path, op); } -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/SkiaCanvasProxy.h b/libs/hwui/SkiaCanvasProxy.h index d11a779b3600..360d5a08b974 100644 --- a/libs/hwui/SkiaCanvasProxy.h +++ b/libs/hwui/SkiaCanvasProxy.h @@ -17,8 +17,8 @@ #ifndef SkiaCanvasProxy_DEFINED #define SkiaCanvasProxy_DEFINED -#include <cutils/compiler.h> #include <SkCanvas.h> +#include <cutils/compiler.h> #include "hwui/Canvas.h" @@ -43,7 +43,6 @@ public: virtual ~SkiaCanvasProxy() {} protected: - virtual sk_sp<SkSurface> onNewSurface(const SkImageInfo&, const SkSurfaceProps&) override; virtual void willSave() override; @@ -66,15 +65,15 @@ protected: const SkPaint*) override; virtual void onDrawBitmapRect(const SkBitmap&, const SkRect* src, const SkRect& dst, const SkPaint* paint, SrcRectConstraint) override; - virtual void onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, - const SkRect& dst, const SkPaint*) override; + virtual void onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst, + const SkPaint*) override; virtual void onDrawImage(const SkImage*, SkScalar dx, SkScalar dy, const SkPaint*); virtual void onDrawImageRect(const SkImage*, const SkRect*, const SkRect&, const SkPaint*, - SrcRectConstraint); + SrcRectConstraint); virtual void onDrawImageNine(const SkImage*, const SkIRect& center, const SkRect& dst, - const SkPaint*); + const SkPaint*); virtual void onDrawImageLattice(const SkImage*, const Lattice& lattice, const SkRect& dst, - const SkPaint*); + const SkPaint*); virtual void onDrawVerticesObject(const SkVertices*, SkBlendMode, const SkPaint&) override; virtual void onDrawDRRect(const SkRRect&, const SkRRect&, const SkPaint&) override; @@ -107,7 +106,7 @@ private: typedef SkCanvas INHERITED; }; -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android -#endif // SkiaCanvasProxy_DEFINED +#endif // SkiaCanvasProxy_DEFINED diff --git a/libs/hwui/SkiaShader.cpp b/libs/hwui/SkiaShader.cpp index 5c5378bf22ad..df746554f306 100644 --- a/libs/hwui/SkiaShader.cpp +++ b/libs/hwui/SkiaShader.cpp @@ -39,11 +39,11 @@ static constexpr GLenum gTileModes[] = { }; static_assert(gTileModes[SkShader::kClamp_TileMode] == GL_CLAMP_TO_EDGE, - "SkShader TileModes have changed"); + "SkShader TileModes have changed"); static_assert(gTileModes[SkShader::kRepeat_TileMode] == GL_REPEAT, - "SkShader TileModes have changed"); + "SkShader TileModes have changed"); static_assert(gTileModes[SkShader::kMirror_TileMode] == GL_MIRRORED_REPEAT, - "SkShader TileModes have changed"); + "SkShader TileModes have changed"); /** * This function does not work for n == 0. @@ -70,7 +70,7 @@ static inline void bindTexture(Caches* caches, Texture* texture, GLenum wrapS, G * @param modelViewMatrix Model view matrix, as supplied by the OpenGLRenderer. */ static void computeScreenSpaceMatrix(mat4& screenSpace, const SkMatrix& unitMatrix, - const SkMatrix& localMatrix, const mat4& modelViewMatrix) { + const SkMatrix& localMatrix, const mat4& modelViewMatrix) { mat4 shaderMatrix; // uses implicit construction shaderMatrix.loadInverse(localMatrix); @@ -95,7 +95,7 @@ static void toLinearUnitMatrix(const SkPoint pts[2], SkMatrix* matrix) { } static void toCircularUnitMatrix(const float x, const float y, const float radius, - SkMatrix* matrix) { + SkMatrix* matrix) { const float inv = 1.0f / radius; matrix->setTranslate(-x, -y); matrix->postScale(inv, inv); @@ -118,8 +118,8 @@ static bool isSimpleGradient(const SkShader::GradientInfo& gradInfo) { /////////////////////////////////////////////////////////////////////////////// bool tryStoreGradient(Caches& caches, const SkShader& shader, const Matrix4 modelViewMatrix, - GLuint* textureUnit, ProgramDescription* description, - SkiaShaderData::GradientShaderData* outData) { + GLuint* textureUnit, ProgramDescription* description, + SkiaShaderData::GradientShaderData* outData) { SkShader::GradientInfo gradInfo; gradInfo.fColorCount = 0; gradInfo.fColors = nullptr; @@ -135,8 +135,8 @@ bool tryStoreGradient(Caches& caches, const SkShader& shader, const Matrix4 mode case SkShader::kRadial_GradientType: description->gradientType = ProgramDescription::kGradientCircular; - toCircularUnitMatrix(gradInfo.fPoint[0].fX, gradInfo.fPoint[0].fY, - gradInfo.fRadius[0], &unitMatrix); + toCircularUnitMatrix(gradInfo.fPoint[0].fX, gradInfo.fPoint[0].fY, gradInfo.fRadius[0], + &unitMatrix); break; case SkShader::kSweep_GradientType: description->gradientType = ProgramDescription::kGradientSweep; @@ -150,8 +150,8 @@ bool tryStoreGradient(Caches& caches, const SkShader& shader, const Matrix4 mode description->hasGradient = true; description->isSimpleGradient = isSimpleGradient(gradInfo); - computeScreenSpaceMatrix(outData->screenSpace, unitMatrix, - shader.getLocalMatrix(), modelViewMatrix); + computeScreenSpaceMatrix(outData->screenSpace, unitMatrix, shader.getLocalMatrix(), + modelViewMatrix); // re-query shader to get full color / offset data std::unique_ptr<SkColor[]> colorStorage(new SkColor[gradInfo.fColorCount]); @@ -164,7 +164,7 @@ bool tryStoreGradient(Caches& caches, const SkShader& shader, const Matrix4 mode outData->gradientSampler = (*textureUnit)++; #ifndef SK_SCALAR_IS_FLOAT - #error Need to convert gradInfo.fColorOffsets to float! +#error Need to convert gradInfo.fColorOffsets to float! #endif outData->gradientTexture = caches.gradientCache.get( gradInfo.fColors, gradInfo.fColorOffsets, gradInfo.fColorCount); @@ -181,8 +181,7 @@ bool tryStoreGradient(Caches& caches, const SkShader& shader, const Matrix4 mode } void applyGradient(Caches& caches, const SkiaShaderData::GradientShaderData& data, - const GLsizei width, const GLsizei height) { - + const GLsizei width, const GLsizei height) { if (CC_UNLIKELY(data.gradientTexture)) { caches.textureState().activateTexture(data.gradientSampler); bindTexture(&caches, data.gradientTexture, data.wrapST, data.wrapST); @@ -193,13 +192,13 @@ void applyGradient(Caches& caches, const SkiaShaderData::GradientShaderData& dat } glUniform2f(caches.program().getUniform("screenSize"), 1.0f / width, 1.0f / height); - glUniformMatrix4fv(caches.program().getUniform("screenSpace"), 1, - GL_FALSE, &data.screenSpace.data[0]); + glUniformMatrix4fv(caches.program().getUniform("screenSpace"), 1, GL_FALSE, + &data.screenSpace.data[0]); } bool tryStoreBitmap(Caches& caches, const SkShader& shader, const Matrix4& modelViewMatrix, - GLuint* textureUnit, ProgramDescription* description, - SkiaShaderData::BitmapShaderData* outData) { + GLuint* textureUnit, ProgramDescription* description, + SkiaShaderData::BitmapShaderData* outData) { SkBitmap bitmap; SkShader::TileMode xy[2]; if (!shader.isABitmap(&bitmap, nullptr, xy)) { @@ -225,9 +224,9 @@ bool tryStoreBitmap(Caches& caches, const SkShader& shader, const Matrix4& model description->hasTranslucentConversion = texture->blend; description->isShaderBitmapExternal = hwuiBitmap->isHardware(); // gralloc doesn't support non-clamp modes - if (hwuiBitmap->isHardware() || (!caches.extensions().hasNPot() - && (!isPowerOfTwo(width) || !isPowerOfTwo(height)) - && (xy[0] != SkShader::kClamp_TileMode || xy[1] != SkShader::kClamp_TileMode))) { + if (hwuiBitmap->isHardware() || + (!caches.extensions().hasNPot() && (!isPowerOfTwo(width) || !isPowerOfTwo(height)) && + (xy[0] != SkShader::kClamp_TileMode || xy[1] != SkShader::kClamp_TileMode))) { // need non-clamp mode, but it's not supported for this draw, // so enable custom shader logic to mimic description->useShaderBasedWrap = true; @@ -242,7 +241,7 @@ bool tryStoreBitmap(Caches& caches, const SkShader& shader, const Matrix4& model } computeScreenSpaceMatrix(outData->textureTransform, SkMatrix::I(), shader.getLocalMatrix(), - modelViewMatrix); + modelViewMatrix); outData->textureDimension[0] = 1.0f / width; outData->textureDimension[1] = 1.0f / height; @@ -256,7 +255,7 @@ void applyBitmap(Caches& caches, const SkiaShaderData::BitmapShaderData& data) { glUniform1i(caches.program().getUniform("bitmapSampler"), data.bitmapSampler); glUniformMatrix4fv(caches.program().getUniform("textureTransform"), 1, GL_FALSE, - &data.textureTransform.data[0]); + &data.textureTransform.data[0]); glUniform2fv(caches.program().getUniform("textureDimension"), 1, &data.textureDimension[0]); } @@ -283,20 +282,19 @@ SkiaShaderType getComposeSubType(const SkShader& shader) { } void storeCompose(Caches& caches, const SkShader& bitmapShader, const SkShader& gradientShader, - const Matrix4& modelViewMatrix, GLuint* textureUnit, - ProgramDescription* description, SkiaShaderData* outData) { - LOG_ALWAYS_FATAL_IF(!tryStoreBitmap(caches, bitmapShader, modelViewMatrix, - textureUnit, description, &outData->bitmapData), - "failed storing bitmap shader data"); - LOG_ALWAYS_FATAL_IF(!tryStoreGradient(caches, gradientShader, modelViewMatrix, - textureUnit, description, &outData->gradientData), - "failing storing gradient shader data"); + const Matrix4& modelViewMatrix, GLuint* textureUnit, + ProgramDescription* description, SkiaShaderData* outData) { + LOG_ALWAYS_FATAL_IF(!tryStoreBitmap(caches, bitmapShader, modelViewMatrix, textureUnit, + description, &outData->bitmapData), + "failed storing bitmap shader data"); + LOG_ALWAYS_FATAL_IF(!tryStoreGradient(caches, gradientShader, modelViewMatrix, textureUnit, + description, &outData->gradientData), + "failing storing gradient shader data"); } bool tryStoreCompose(Caches& caches, const SkShader& shader, const Matrix4& modelViewMatrix, - GLuint* textureUnit, ProgramDescription* description, - SkiaShaderData* outData) { - + GLuint* textureUnit, ProgramDescription* description, + SkiaShaderData* outData) { SkShader::ComposeRec rec; if (!shader.asACompose(&rec)) return false; @@ -311,34 +309,33 @@ bool tryStoreCompose(Caches& caches, const SkShader& shader, const Matrix4& mode computeScreenSpaceMatrix(transform, SkMatrix::I(), shader.getLocalMatrix(), modelViewMatrix); if (shaderAType == kBitmap_SkiaShaderType) { description->isBitmapFirst = true; - storeCompose(caches, *rec.fShaderA, *rec.fShaderB, - transform, textureUnit, description, outData); + storeCompose(caches, *rec.fShaderA, *rec.fShaderB, transform, textureUnit, description, + outData); } else { description->isBitmapFirst = false; - storeCompose(caches, *rec.fShaderB, *rec.fShaderA, - transform, textureUnit, description, outData); + storeCompose(caches, *rec.fShaderB, *rec.fShaderA, transform, textureUnit, description, + outData); } description->shadersMode = rec.fBlendMode; return true; } void SkiaShader::store(Caches& caches, const SkShader& shader, const Matrix4& modelViewMatrix, - GLuint* textureUnit, ProgramDescription* description, - SkiaShaderData* outData) { - if (tryStoreGradient(caches, shader, modelViewMatrix, - textureUnit, description, &outData->gradientData)) { + GLuint* textureUnit, ProgramDescription* description, + SkiaShaderData* outData) { + if (tryStoreGradient(caches, shader, modelViewMatrix, textureUnit, description, + &outData->gradientData)) { outData->skiaShaderType = kGradient_SkiaShaderType; return; } - if (tryStoreBitmap(caches, shader, modelViewMatrix, - textureUnit, description, &outData->bitmapData)) { + if (tryStoreBitmap(caches, shader, modelViewMatrix, textureUnit, description, + &outData->bitmapData)) { outData->skiaShaderType = kBitmap_SkiaShaderType; return; } - if (tryStoreCompose(caches, shader, modelViewMatrix, - textureUnit, description, outData)) { + if (tryStoreCompose(caches, shader, modelViewMatrix, textureUnit, description, outData)) { outData->skiaShaderType = kCompose_SkiaShaderType; return; } @@ -347,8 +344,8 @@ void SkiaShader::store(Caches& caches, const SkShader& shader, const Matrix4& mo outData->skiaShaderType = kNone_SkiaShaderType; } -void SkiaShader::apply(Caches& caches, const SkiaShaderData& data, - const GLsizei width, const GLsizei height) { +void SkiaShader::apply(Caches& caches, const SkiaShaderData& data, const GLsizei width, + const GLsizei height) { if (!data.skiaShaderType) return; if (data.skiaShaderType & kGradient_SkiaShaderType) { @@ -359,5 +356,5 @@ void SkiaShader::apply(Caches& caches, const SkiaShaderData& data, } } -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/SkiaShader.h b/libs/hwui/SkiaShader.h index ab578d540774..e8e92bdc1d76 100644 --- a/libs/hwui/SkiaShader.h +++ b/libs/hwui/SkiaShader.h @@ -74,13 +74,13 @@ struct SkiaShaderData { class SkiaShader { public: static void store(Caches& caches, const SkShader& shader, const Matrix4& modelViewMatrix, - GLuint* textureUnit, ProgramDescription* description, - SkiaShaderData* outData); - static void apply(Caches& caches, const SkiaShaderData& data, - const GLsizei width, const GLsizei height); + GLuint* textureUnit, ProgramDescription* description, + SkiaShaderData* outData); + static void apply(Caches& caches, const SkiaShaderData& data, const GLsizei width, + const GLsizei height); }; -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android -#endif // ANDROID_HWUI_SKIA_SHADER_H +#endif // ANDROID_HWUI_SKIA_SHADER_H diff --git a/libs/hwui/Snapshot.cpp b/libs/hwui/Snapshot.cpp index 9d719bd1e997..f1a1bef7c94e 100644 --- a/libs/hwui/Snapshot.cpp +++ b/libs/hwui/Snapshot.cpp @@ -113,8 +113,8 @@ void Snapshot::resetClip(float left, float top, float right, float bottom) { // Clipping round rect /////////////////////////////////////////////////////////////////////////////// -void Snapshot::setClippingRoundRect(LinearAllocator& allocator, const Rect& bounds, - float radius, bool highPriority) { +void Snapshot::setClippingRoundRect(LinearAllocator& allocator, const Rect& bounds, float radius, + bool highPriority) { if (bounds.isEmpty()) { mClipArea->setEmpty(); return; @@ -135,7 +135,7 @@ void Snapshot::setClippingRoundRect(LinearAllocator& allocator, const Rect& boun state->matrix.loadInverse(roundRectDrawingMatrix); // compute area under rounded corners - only draws overlapping these rects need to be clipped - for (int i = 0 ; i < 4; i++) { + for (int i = 0; i < 4; i++) { state->dangerRects[i] = bounds; } state->dangerRects[0].bottom = state->dangerRects[1].bottom = bounds.top + radius; @@ -170,15 +170,16 @@ static Snapshot* getClipRoot(Snapshot* target) { } const ClipBase* Snapshot::serializeIntersectedClip(LinearAllocator& allocator, - const ClipBase* recordedClip, const Matrix4& recordedClipTransform) { + const ClipBase* recordedClip, + const Matrix4& recordedClipTransform) { auto target = this; if (CC_UNLIKELY(recordedClip && recordedClip->intersectWithRoot)) { // Clip must be intersected with root, instead of current clip. target = getClipRoot(this); } - return target->mClipArea->serializeIntersectedClip(allocator, - recordedClip, recordedClipTransform); + return target->mClipArea->serializeIntersectedClip(allocator, recordedClip, + recordedClipTransform); } void Snapshot::applyClip(const ClipBase* recordedClip, const Matrix4& transform) { @@ -194,15 +195,15 @@ void Snapshot::applyClip(const ClipBase* recordedClip, const Matrix4& transform) /////////////////////////////////////////////////////////////////////////////// void Snapshot::dump() const { - ALOGD("Snapshot %p, flags %x, prev %p, height %d, hasComplexClip %d", - this, flags, previous, getViewportHeight(), !mClipArea->isSimple()); + ALOGD("Snapshot %p, flags %x, prev %p, height %d, hasComplexClip %d", this, flags, previous, + getViewportHeight(), !mClipArea->isSimple()); const Rect& clipRect(mClipArea->getClipRect()); - ALOGD(" ClipRect %.1f %.1f %.1f %.1f, clip simple %d", - clipRect.left, clipRect.top, clipRect.right, clipRect.bottom, mClipArea->isSimple()); + ALOGD(" ClipRect %.1f %.1f %.1f %.1f, clip simple %d", clipRect.left, clipRect.top, + clipRect.right, clipRect.bottom, mClipArea->isSimple()); ALOGD(" Transform (at %p):", transform); transform->dump(); } -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/Snapshot.h b/libs/hwui/Snapshot.h index 8cd90a68a9bb..655f819ca41b 100644 --- a/libs/hwui/Snapshot.h +++ b/libs/hwui/Snapshot.h @@ -19,9 +19,9 @@ #include <GLES2/gl2.h> #include <GLES2/gl2ext.h> +#include <ui/Region.h> #include <utils/LinearAllocator.h> #include <utils/RefBase.h> -#include <ui/Region.h> #include <SkClipOp.h> #include <SkRegion.h> @@ -50,10 +50,8 @@ public: } bool areaRequiresRoundRectClip(const Rect& rect) const { - return rect.intersects(dangerRects[0]) - || rect.intersects(dangerRects[1]) - || rect.intersects(dangerRects[2]) - || rect.intersects(dangerRects[3]); + return rect.intersects(dangerRects[0]) || rect.intersects(dangerRects[1]) || + rect.intersects(dangerRects[2]) || rect.intersects(dangerRects[3]); } bool highPriority; @@ -74,7 +72,6 @@ public: */ class Snapshot { public: - Snapshot(); Snapshot(Snapshot* s, int saveFlags); @@ -147,8 +144,9 @@ public: const ClipArea& getClipArea() const { return *mClipArea; } ClipArea& mutateClipArea() { return *mClipArea; } - WARN_UNUSED_RESULT const ClipBase* serializeIntersectedClip(LinearAllocator& allocator, - const ClipBase* recordedClip, const Matrix4& recordedClipTransform); + WARN_UNUSED_RESULT const ClipBase* serializeIntersectedClip( + LinearAllocator& allocator, const ClipBase* recordedClip, + const Matrix4& recordedClipTransform); void applyClip(const ClipBase* clip, const Matrix4& transform); /** @@ -173,8 +171,8 @@ public: * * If the current round rect clip is high priority, the incoming clip is ignored. */ - void setClippingRoundRect(LinearAllocator& allocator, const Rect& bounds, - float radius, bool highPriority); + void setClippingRoundRect(LinearAllocator& allocator, const Rect& bounds, float radius, + bool highPriority); /** * Sets (and replaces) the current projection mask @@ -275,7 +273,7 @@ private: ViewportData mViewportData; Vector3 mRelativeLightCenter; -}; // class Snapshot +}; // class Snapshot -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/SpotShadow.cpp b/libs/hwui/SpotShadow.cpp index 7b0a1bc3e93e..e371ac8da1e5 100644 --- a/libs/hwui/SpotShadow.cpp +++ b/libs/hwui/SpotShadow.cpp @@ -37,7 +37,7 @@ // For the whole polygon, the sum of all the deltas b/t normals is 2 * M_PI, // therefore, the maximum number of extra vertices will be twice bigger. -#define SPOT_MAX_EXTRA_CORNER_VERTEX_NUMBER (2 * SPOT_EXTRA_CORNER_VERTEX_PER_PI) +#define SPOT_MAX_EXTRA_CORNER_VERTEX_NUMBER (2 * SPOT_EXTRA_CORNER_VERTEX_PER_PI) // For each RADIANS_DIVISOR, we would allocate one more vertex b/t the normals. #define SPOT_CORNER_RADIANS_DIVISOR (M_PI / SPOT_EXTRA_CORNER_VERTEX_PER_PI) @@ -52,10 +52,10 @@ #include "VertexBuffer.h" #include "utils/MathUtils.h" -#include <algorithm> #include <math.h> #include <stdlib.h> #include <utils/Log.h> +#include <algorithm> // TODO: After we settle down the new algorithm, we can remove the old one and // its utility functions. @@ -115,14 +115,14 @@ static float angle(const Vector2& point, const Vector2& center) { * @param p2 The second point defining the line segment * @return The distance along the ray if it intersects with the line segment, negative if otherwise */ -static float rayIntersectPoints(const Vector2& rayOrigin, float dx, float dy, - const Vector2& p1, const Vector2& p2) { +static float rayIntersectPoints(const Vector2& rayOrigin, float dx, float dy, const Vector2& p1, + const Vector2& p2) { // The math below is derived from solving this formula, basically the // intersection point should stay on both the ray and the edge of (p1, p2). // solve([p1x+t*(p2x-p1x)=dx*t2+px,p1y+t*(p2y-p1y)=dy*t2+py],[t,t2]); float divisor = (dx * (p1.y - p2.y) + dy * p2.x - dy * p1.x); - if (divisor == 0) return -1.0f; // error, invalid divisor + if (divisor == 0) return -1.0f; // error, invalid divisor #if DEBUG_SHADOW float interpVal = (dx * (p1.y - rayOrigin.y) + dy * rayOrigin.x - dy * p1.x) / divisor; @@ -132,9 +132,10 @@ static float rayIntersectPoints(const Vector2& rayOrigin, float dx, float dy, #endif float distance = (p1.x * (rayOrigin.y - p2.y) + p2.x * (p1.y - rayOrigin.y) + - rayOrigin.x * (p2.y - p1.y)) / divisor; + rayOrigin.x * (p2.y - p1.y)) / + divisor; - return distance; // may be negative in error cases + return distance; // may be negative in error cases } /** @@ -144,9 +145,7 @@ static float rayIntersectPoints(const Vector2& rayOrigin, float dx, float dy, * @param pointsLength the number of vertices of the polygon. */ void SpotShadow::xsort(Vector2* points, int pointsLength) { - auto cmp = [](const Vector2& a, const Vector2& b) -> bool { - return a.x < b.x; - }; + auto cmp = [](const Vector2& a, const Vector2& b) -> bool { return a.x < b.x; }; std::sort(points, points + pointsLength, cmp); } @@ -171,10 +170,9 @@ int SpotShadow::hull(Vector2* points, int pointsLength, Vector2* retPoly) { lUpper[lUpperSize] = points[i]; lUpperSize++; - while (lUpperSize > 2 && !ccw( - lUpper[lUpperSize - 3].x, lUpper[lUpperSize - 3].y, - lUpper[lUpperSize - 2].x, lUpper[lUpperSize - 2].y, - lUpper[lUpperSize - 1].x, lUpper[lUpperSize - 1].y)) { + while (lUpperSize > 2 && + !ccw(lUpper[lUpperSize - 3].x, lUpper[lUpperSize - 3].y, lUpper[lUpperSize - 2].x, + lUpper[lUpperSize - 2].y, lUpper[lUpperSize - 1].x, lUpper[lUpperSize - 1].y)) { // Remove the middle point of the three last lUpper[lUpperSize - 2].x = lUpper[lUpperSize - 1].x; lUpper[lUpperSize - 2].y = lUpper[lUpperSize - 1].y; @@ -192,10 +190,9 @@ int SpotShadow::hull(Vector2* points, int pointsLength, Vector2* retPoly) { lLower[lLowerSize] = points[i]; lLowerSize++; - while (lLowerSize > 2 && !ccw( - lLower[lLowerSize - 3].x, lLower[lLowerSize - 3].y, - lLower[lLowerSize - 2].x, lLower[lLowerSize - 2].y, - lLower[lLowerSize - 1].x, lLower[lLowerSize - 1].y)) { + while (lLowerSize > 2 && + !ccw(lLower[lLowerSize - 3].x, lLower[lLowerSize - 3].y, lLower[lLowerSize - 2].x, + lLower[lLowerSize - 2].y, lLower[lLowerSize - 1].x, lLower[lLowerSize - 1].y)) { // Remove the middle point of the three last lLower[lLowerSize - 2] = lLower[lLowerSize - 1]; lLowerSize--; @@ -223,8 +220,7 @@ int SpotShadow::hull(Vector2* points, int pointsLength, Vector2* retPoly) { * * @return true if a right hand turn */ -bool SpotShadow::ccw(float ax, float ay, float bx, float by, - float cx, float cy) { +bool SpotShadow::ccw(float ax, float ay, float bx, float by, float cx, float cy) { return (bx - ax) * (cy - ay) - (by - ay) * (cx - ax) > EPSILON; } @@ -251,8 +247,7 @@ void SpotShadow::swap(Vector2* points, int i, int j) { /** * quick sort implementation about the center. */ -void SpotShadow::quicksortCirc(Vector2* points, int low, int high, - const Vector2& center) { +void SpotShadow::quicksortCirc(Vector2* points, int low, int high, const Vector2& center) { int i = low, j = high; int p = low + (high - low) / 2; float pivot = angle(points[p], center); @@ -281,8 +276,7 @@ void SpotShadow::quicksortCirc(Vector2* points, int low, int high, * @param poly the polygon * @return true if the testPoint is inside the poly. */ -bool SpotShadow::testPointInsidePolygon(const Vector2 testPoint, - const Vector2* poly, int len) { +bool SpotShadow::testPointInsidePolygon(const Vector2 testPoint, const Vector2* poly, int len) { bool c = false; float testx = testPoint.x; float testy = testPoint.y; @@ -292,9 +286,8 @@ bool SpotShadow::testPointInsidePolygon(const Vector2 testPoint, float endX = poly[i].x; float endY = poly[i].y; - if (((endY > testy) != (startY > testy)) - && (testx < (startX - endX) * (testy - endY) - / (startY - endY) + endX)) { + if (((endY > testy) != (startY > testy)) && + (testx < (startX - endX) * (testy - endY) / (startY - endY) + endX)) { c = !c; } } @@ -326,8 +319,8 @@ void SpotShadow::reverse(Vector2* polygon, int len) { * @param size the light size. * @param ret result polygon. */ -void SpotShadow::computeLightPolygon(int points, const Vector3& lightCenter, - float size, Vector3* ret) { +void SpotShadow::computeLightPolygon(int points, const Vector3& lightCenter, float size, + Vector3* ret) { // TODO: Caching all the sin / cos values and store them in a look up table. for (int i = 0; i < points; i++) { float angle = 2 * i * M_PI / points; @@ -346,8 +339,8 @@ void SpotShadow::computeLightPolygon(int points, const Vector3& lightCenter, * * @return float The ratio of (polygon.z / light.z - polygon.z) */ -float SpotShadow::projectCasterToOutline(Vector2& outline, - const Vector3& lightCenter, const Vector3& polyVertex) { +float SpotShadow::projectCasterToOutline(Vector2& outline, const Vector3& lightCenter, + const Vector3& polyVertex) { float lightToPolyZ = lightCenter.z - polyVertex.z; float ratioZ = CASTER_Z_CAP_RATIO; if (lightToPolyZ != 0) { @@ -372,9 +365,9 @@ float SpotShadow::projectCasterToOutline(Vector2& outline, * @param shadowTriangleStrip return an (x,y,alpha) triangle strip representing the shadow. Return * empty strip if error. */ -void SpotShadow::createSpotShadow(bool isCasterOpaque, const Vector3& lightCenter, - float lightSize, const Vector3* poly, int polyLength, const Vector3& polyCentroid, - VertexBuffer& shadowTriangleStrip) { +void SpotShadow::createSpotShadow(bool isCasterOpaque, const Vector3& lightCenter, float lightSize, + const Vector3* poly, int polyLength, const Vector3& polyCentroid, + VertexBuffer& shadowTriangleStrip) { if (CC_UNLIKELY(lightCenter.z <= 0)) { ALOGW("Relative Light Z is not positive. No spot shadow!"); return; @@ -403,21 +396,18 @@ void SpotShadow::createSpotShadow(bool isCasterOpaque, const Vector3& lightCente // Compute the last outline vertex to make sure we can get the normal and outline // in one single loop. - projectCasterToOutline(outlineData[polyLength - 1].position, lightCenter, - poly[polyLength - 1]); + projectCasterToOutline(outlineData[polyLength - 1].position, lightCenter, poly[polyLength - 1]); // Take the outline's polygon, calculate the normal for each outline edge. int currentNormalIndex = polyLength - 1; int nextNormalIndex = 0; for (int i = 0; i < polyLength; i++) { - float ratioZ = projectCasterToOutline(outlineData[i].position, - lightCenter, poly[i]); + float ratioZ = projectCasterToOutline(outlineData[i].position, lightCenter, poly[i]); outlineData[i].radius = ratioZ * lightSize; outlineData[currentNormalIndex].normal = ShadowTessellator::calculateNormal( - outlineData[currentNormalIndex].position, - outlineData[nextNormalIndex].position); + outlineData[currentNormalIndex].position, outlineData[nextNormalIndex].position); currentNormalIndex = (currentNormalIndex + 1) % polyLength; nextNormalIndex++; } @@ -489,11 +479,9 @@ void SpotShadow::createSpotShadow(bool isCasterOpaque, const Vector3& lightCente (previousNormal * (currentCornerSliceNumber - k) + currentNormal * k) / currentCornerSliceNumber; avgNormal.normalize(); - penumbra[penumbraIndex++] = outlineData[i].position + - avgNormal * outlineData[i].radius; + penumbra[penumbraIndex++] = outlineData[i].position + avgNormal * outlineData[i].radius; } - // Compute the umbra by the intersection from the outline's centroid! // // (V) ------------------------------------ @@ -547,7 +535,7 @@ void SpotShadow::createSpotShadow(bool isCasterOpaque, const Vector3& lightCente #endif for (int i = 0; i < polyLength; i++) { umbra[i] = outlineData[i].position * FAKE_UMBRA_SIZE_RATIO + - outlineCentroid * (1 - FAKE_UMBRA_SIZE_RATIO); + outlineCentroid * (1 - FAKE_UMBRA_SIZE_RATIO); } shadowStrengthScale = 1.0 / minRaitoVI; } @@ -556,7 +544,8 @@ void SpotShadow::createSpotShadow(bool isCasterOpaque, const Vector3& lightCente int umbraLength = polyLength; #if DEBUG_SHADOW - ALOGD("penumbraLength is %d , allocatedPenumbraLength %d", penumbraLength, allocatedPenumbraLength); + ALOGD("penumbraLength is %d , allocatedPenumbraLength %d", penumbraLength, + allocatedPenumbraLength); dumpPolygon(poly, polyLength, "input poly"); dumpPolygon(penumbra, penumbraLength, "penumbra"); dumpPolygon(umbra, umbraLength, "umbra"); @@ -573,10 +562,9 @@ void SpotShadow::createSpotShadow(bool isCasterOpaque, const Vector3& lightCente int finalUmbraLength = hull(umbra, umbraLength, finalUmbra); int finalPenumbraLength = hull(penumbra, penumbraLength, finalPenumbra); - generateTriangleStrip(isCasterOpaque, shadowStrengthScale, finalPenumbra, - finalPenumbraLength, finalUmbra, finalUmbraLength, poly, polyLength, - shadowTriangleStrip, outlineCentroid); - + generateTriangleStrip(isCasterOpaque, shadowStrengthScale, finalPenumbra, finalPenumbraLength, + finalUmbra, finalUmbraLength, poly, polyLength, shadowTriangleStrip, + outlineCentroid); } /** @@ -632,7 +620,7 @@ inline int getClosestUmbraIndex(const Vector2& pivot, const Vector2* polygon, in break; } } - if(resultIndex == -1) { + if (resultIndex == -1) { ALOGE("resultIndex is -1, the polygon must be invalid!"); resultIndex = 0; } @@ -651,7 +639,7 @@ inline bool sameDirections(bool isPositiveCross, float a, float b) { // Find the right polygon edge to shoot the ray at. inline int findPolyIndex(bool isPositiveCross, int startPolyIndex, const Vector2& umbraDir, - const Vector2* polyToCentroid, int polyLength) { + const Vector2* polyToCentroid, int polyLength) { // Make sure we loop with a bound. for (int i = 0; i < polyLength; i++) { int currentIndex = (i + startPolyIndex) % polyLength; @@ -662,7 +650,7 @@ inline int findPolyIndex(bool isPositiveCross, int startPolyIndex, const Vector2 float umbraCrossNext = umbraDir.cross(nextToCentroid); if (sameDirections(isPositiveCross, currentCrossUmbra, umbraCrossNext)) { #if DEBUG_SHADOW - ALOGD("findPolyIndex loop %d times , index %d", i, currentIndex ); + ALOGD("findPolyIndex loop %d times , index %d", i, currentIndex); #endif return currentIndex; } @@ -674,12 +662,13 @@ inline int findPolyIndex(bool isPositiveCross, int startPolyIndex, const Vector2 // Generate the index pair for penumbra / umbra vertices, and more penumbra vertices // if needed. inline void genNewPenumbraAndPairWithUmbra(const Vector2* penumbra, int penumbraLength, - const Vector2* umbra, int umbraLength, Vector2* newPenumbra, int& newPenumbraIndex, - IndexPair* verticesPair, int& verticesPairIndex) { + const Vector2* umbra, int umbraLength, + Vector2* newPenumbra, int& newPenumbraIndex, + IndexPair* verticesPair, int& verticesPairIndex) { // In order to keep everything in just one loop, we need to pre-compute the // closest umbra vertex for the last penumbra vertex. - int previousClosestUmbraIndex = getClosestUmbraIndex(penumbra[penumbraLength - 1], - umbra, umbraLength); + int previousClosestUmbraIndex = + getClosestUmbraIndex(penumbra[penumbraLength - 1], umbra, umbraLength); for (int i = 0; i < penumbraLength; i++) { const Vector2& currentPenumbraVertex = penumbra[i]; // For current penumbra vertex, starting from previousClosestUmbraIndex, @@ -704,7 +693,8 @@ inline void genNewPenumbraAndPairWithUmbra(const Vector2* penumbra, int penumbra } if (indexDelta > 1) { - // For those umbra don't have penumbra, generate new penumbra vertices by interpolation. + // For those umbra don't have penumbra, generate new penumbra vertices by + // interpolation. // // Assuming Pi for penumbra vertices, and Ui for umbra vertices. // In the case like below P1 paired with U1 and P2 paired with U5. @@ -756,7 +746,7 @@ inline void genNewPenumbraAndPairWithUmbra(const Vector2* penumbra, int penumbra float weightForPreviousPenumbra = 1.0f - weightForCurrentPenumbra; Vector2 interpolatedPenumbra = currentPenumbraVertex * weightForCurrentPenumbra + - previousPenumbra * weightForPreviousPenumbra; + previousPenumbra * weightForPreviousPenumbra; int skippedUmbraIndex = (previousClosestUmbraIndex + k + 1) % umbraLength; verticesPair[verticesPairIndex].outerIndex = newPenumbraIndex; @@ -775,8 +765,8 @@ inline void genNewPenumbraAndPairWithUmbra(const Vector2* penumbra, int penumbra } // Precompute all the polygon's vector, return true if the reference cross product is positive. -inline bool genPolyToCentroid(const Vector2* poly2d, int polyLength, - const Vector2& centroid, Vector2* polyToCentroid) { +inline bool genPolyToCentroid(const Vector2* poly2d, int polyLength, const Vector2& centroid, + Vector2* polyToCentroid) { for (int j = 0; j < polyLength; j++) { polyToCentroid[j] = poly2d[j] - centroid; // Normalize these vectors such that we can use epsilon comparison after @@ -798,21 +788,22 @@ inline bool genPolyToCentroid(const Vector2* poly2d, int polyLength, // If the ray hit the polygon first, then return the intersection point as the // closer vertex. inline Vector2 getCloserVertex(const Vector2& umbraVertex, const Vector2& centroid, - const Vector2* poly2d, int polyLength, const Vector2* polyToCentroid, - bool isPositiveCross, int& previousPolyIndex) { + const Vector2* poly2d, int polyLength, const Vector2* polyToCentroid, + bool isPositiveCross, int& previousPolyIndex) { Vector2 umbraToCentroid = umbraVertex - centroid; float distanceToUmbra = umbraToCentroid.length(); umbraToCentroid = umbraToCentroid / distanceToUmbra; // previousPolyIndex is updated for each item such that we can minimize the // looping inside findPolyIndex(); - previousPolyIndex = findPolyIndex(isPositiveCross, previousPolyIndex, - umbraToCentroid, polyToCentroid, polyLength); + previousPolyIndex = findPolyIndex(isPositiveCross, previousPolyIndex, umbraToCentroid, + polyToCentroid, polyLength); float dx = umbraToCentroid.x; float dy = umbraToCentroid.y; - float distanceToIntersectPoly = rayIntersectPoints(centroid, dx, dy, - poly2d[previousPolyIndex], poly2d[(previousPolyIndex + 1) % polyLength]); + float distanceToIntersectPoly = + rayIntersectPoints(centroid, dx, dy, poly2d[previousPolyIndex], + poly2d[(previousPolyIndex + 1) % polyLength]); if (distanceToIntersectPoly < 0) { distanceToIntersectPoly = 0; } @@ -833,9 +824,9 @@ inline Vector2 getCloserVertex(const Vector2& umbraVertex, const Vector2& centro * Generate a triangle strip given two convex polygon **/ void SpotShadow::generateTriangleStrip(bool isCasterOpaque, float shadowStrengthScale, - Vector2* penumbra, int penumbraLength, Vector2* umbra, int umbraLength, - const Vector3* poly, int polyLength, VertexBuffer& shadowTriangleStrip, - const Vector2& centroid) { + Vector2* penumbra, int penumbraLength, Vector2* umbra, + int umbraLength, const Vector3* poly, int polyLength, + VertexBuffer& shadowTriangleStrip, const Vector2& centroid) { bool hasOccludedUmbraArea = false; Vector2 poly2d[polyLength]; @@ -891,7 +882,7 @@ void SpotShadow::generateTriangleStrip(bool isCasterOpaque, float shadowStrength // For each penumbra vertex, find its closet umbra vertex by comparing the // neighbor umbra vertices. genNewPenumbraAndPairWithUmbra(penumbra, penumbraLength, umbra, umbraLength, newPenumbra, - newPenumbraIndex, verticesPair, verticesPairIndex); + newPenumbraIndex, verticesPair, verticesPairIndex); ShadowTessellator::checkOverflow(verticesPairIndex, maxNewPenumbraLength, "Spot pair"); ShadowTessellator::checkOverflow(newPenumbraIndex, maxNewPenumbraLength, "Spot new penumbra"); #if DEBUG_SHADOW @@ -915,17 +906,15 @@ void SpotShadow::generateTriangleStrip(bool isCasterOpaque, float shadowStrength const int newPenumbraLength = newPenumbraIndex; const int totalVertexCount = newPenumbraLength + umbraLength * 2; const int totalIndexCount = 2 * umbraLength + 2 * verticesPairIndex + 6; - AlphaVertex* shadowVertices = - shadowTriangleStrip.alloc<AlphaVertex>(totalVertexCount); - uint16_t* indexBuffer = - shadowTriangleStrip.allocIndices<uint16_t>(totalIndexCount); + AlphaVertex* shadowVertices = shadowTriangleStrip.alloc<AlphaVertex>(totalVertexCount); + uint16_t* indexBuffer = shadowTriangleStrip.allocIndices<uint16_t>(totalIndexCount); int vertexBufferIndex = 0; int indexBufferIndex = 0; // Fill the IB and VB for the penumbra area. for (int i = 0; i < newPenumbraLength; i++) { - AlphaVertex::set(&shadowVertices[vertexBufferIndex++], newPenumbra[i].x, - newPenumbra[i].y, PENUMBRA_ALPHA); + AlphaVertex::set(&shadowVertices[vertexBufferIndex++], newPenumbra[i].x, newPenumbra[i].y, + PENUMBRA_ALPHA); } // Since the umbra can be a faked one when the occluder is too high, the umbra should be lighter // in this case. @@ -933,7 +922,7 @@ void SpotShadow::generateTriangleStrip(bool isCasterOpaque, float shadowStrength for (int i = 0; i < umbraLength; i++) { AlphaVertex::set(&shadowVertices[vertexBufferIndex++], umbra[i].x, umbra[i].y, - scaledUmbraAlpha); + scaledUmbraAlpha); } for (int i = 0; i < verticesPairIndex; i++) { @@ -966,21 +955,22 @@ void SpotShadow::generateTriangleStrip(bool isCasterOpaque, float shadowStrength for (int i = 0; i < umbraLength; i++) { // Shoot a ray from centroid to each umbra vertices and pick the one with // shorter distance to the centroid, b/t the umbra vertex or the intersection point. - Vector2 closerVertex = getCloserVertex(umbra[i], centroid, poly2d, polyLength, - polyToCentroid, isPositiveCross, previousPolyIndex); + Vector2 closerVertex = + getCloserVertex(umbra[i], centroid, poly2d, polyLength, polyToCentroid, + isPositiveCross, previousPolyIndex); // We already stored the umbra vertices, just need to add the occlued umbra's ones. indexBuffer[indexBufferIndex++] = newPenumbraLength + i; indexBuffer[indexBufferIndex++] = vertexBufferIndex; - AlphaVertex::set(&shadowVertices[vertexBufferIndex++], - closerVertex.x, closerVertex.y, scaledUmbraAlpha); + AlphaVertex::set(&shadowVertices[vertexBufferIndex++], closerVertex.x, closerVertex.y, + scaledUmbraAlpha); } } else { // If there is no occluded umbra at all, then draw the triangle fan // starting from the centroid to all umbra vertices. int lastCentroidIndex = vertexBufferIndex; - AlphaVertex::set(&shadowVertices[vertexBufferIndex++], centroid.x, - centroid.y, scaledUmbraAlpha); + AlphaVertex::set(&shadowVertices[vertexBufferIndex++], centroid.x, centroid.y, + scaledUmbraAlpha); for (int i = 0; i < umbraLength; i++) { indexBuffer[indexBufferIndex++] = newPenumbraLength + i; indexBuffer[indexBufferIndex++] = lastCentroidIndex; @@ -1006,8 +996,7 @@ void SpotShadow::generateTriangleStrip(bool isCasterOpaque, float shadowStrength /** * Calculate the bounds for generating random test points. */ -void SpotShadow::updateBound(const Vector2 inVector, Vector2& lowerBound, - Vector2& upperBound) { +void SpotShadow::updateBound(const Vector2 inVector, Vector2& lowerBound, Vector2& upperBound) { if (inVector.x < lowerBound.x) { lowerBound.x = inVector.x; } @@ -1046,8 +1035,7 @@ void SpotShadow::dumpPolygon(const Vector3* poly, int polyLength, const char* po /** * Test whether the polygon is convex. */ -bool SpotShadow::testConvex(const Vector2* polygon, int polygonLength, - const char* name) { +bool SpotShadow::testConvex(const Vector2* polygon, int polygonLength, const char* name) { bool isConvex = true; for (int i = 0; i < polygonLength; i++) { Vector2 start = polygon[i]; @@ -1055,13 +1043,13 @@ bool SpotShadow::testConvex(const Vector2* polygon, int polygonLength, Vector2 end = polygon[(i + 2) % polygonLength]; float delta = (float(middle.x) - start.x) * (float(end.y) - start.y) - - (float(middle.y) - start.y) * (float(end.x) - start.x); + (float(middle.y) - start.y) * (float(end.x) - start.x); bool isCCWOrCoLinear = (delta >= EPSILON); if (isCCWOrCoLinear) { ALOGW("(Error Type 2): polygon (%s) is not a convex b/c start (x %f, y %f)," - "middle (x %f, y %f) and end (x %f, y %f) , delta is %f !!!", - name, start.x, start.y, middle.x, middle.y, end.x, end.y, delta); + "middle (x %f, y %f) and end (x %f, y %f) , delta is %f !!!", + name, start.x, start.y, middle.x, middle.y, end.x, end.y, delta); isConvex = false; break; } @@ -1074,9 +1062,9 @@ bool SpotShadow::testConvex(const Vector2* polygon, int polygonLength, * Using Marte Carlo method, we generate a random point, and if it is inside the * intersection, then it must be inside both source polygons. */ -void SpotShadow::testIntersection(const Vector2* poly1, int poly1Length, - const Vector2* poly2, int poly2Length, - const Vector2* intersection, int intersectionLength) { +void SpotShadow::testIntersection(const Vector2* poly1, int poly1Length, const Vector2* poly2, + int poly2Length, const Vector2* intersection, + int intersectionLength) { // Find the min and max of x and y. Vector2 lowerBound = {FLT_MAX, FLT_MAX}; Vector2 upperBound = {-FLT_MAX, -FLT_MAX}; @@ -1102,15 +1090,15 @@ void SpotShadow::testIntersection(const Vector2* poly1, int poly1Length, if (!testPointInsidePolygon(testPoint, poly1, poly1Length)) { dumpPoly = true; ALOGW("(Error Type 1): one point (%f, %f) in the intersection is" - " not in the poly1", - testPoint.x, testPoint.y); + " not in the poly1", + testPoint.x, testPoint.y); } if (!testPointInsidePolygon(testPoint, poly2, poly2Length)) { dumpPoly = true; ALOGW("(Error Type 1): one point (%f, %f) in the intersection is" - " not in the poly2", - testPoint.x, testPoint.y); + " not in the poly2", + testPoint.x, testPoint.y); } } } @@ -1128,5 +1116,5 @@ void SpotShadow::testIntersection(const Vector2* poly1, int poly1Length, } #endif -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/SpotShadow.h b/libs/hwui/SpotShadow.h index 6108bb6bd585..8476be70318b 100644 --- a/libs/hwui/SpotShadow.h +++ b/libs/hwui/SpotShadow.h @@ -27,22 +27,22 @@ class VertexBuffer; class SpotShadow { public: - static void createSpotShadow(bool isCasterOpaque, const Vector3& lightCenter, - float lightSize, const Vector3* poly, int polyLength, - const Vector3& polyCentroid, VertexBuffer& retstrips); + static void createSpotShadow(bool isCasterOpaque, const Vector3& lightCenter, float lightSize, + const Vector3* poly, int polyLength, const Vector3& polyCentroid, + VertexBuffer& retstrips); private: struct VertexAngleData; - static float projectCasterToOutline(Vector2& outline, - const Vector3& lightCenter, const Vector3& polyVertex); + static float projectCasterToOutline(Vector2& outline, const Vector3& lightCenter, + const Vector3& polyVertex); - static void computeLightPolygon(int points, const Vector3& lightCenter, - float size, Vector3* ret); + static void computeLightPolygon(int points, const Vector3& lightCenter, float size, + Vector3* ret); static void smoothPolygon(int level, int rays, float* rayDist); - static float rayIntersectPoly(const Vector2* poly, int polyLength, - const Vector2& point, float dx, float dy); + static float rayIntersectPoly(const Vector2* poly, int polyLength, const Vector2& point, + float dx, float dy); static void xsort(Vector2* points, int pointsLength); static int hull(Vector2* points, int pointsLength, Vector2* retPoly); @@ -57,23 +57,23 @@ private: static void reverse(Vector2* polygon, int len); static void generateTriangleStrip(bool isCasterOpaque, float shadowStrengthScale, - Vector2* penumbra, int penumbraLength, Vector2* umbra, int umbraLength, - const Vector3* poly, int polyLength, VertexBuffer& retstrips, const Vector2& centroid); + Vector2* penumbra, int penumbraLength, Vector2* umbra, + int umbraLength, const Vector3* poly, int polyLength, + VertexBuffer& retstrips, const Vector2& centroid); #if DEBUG_SHADOW - static bool testConvex(const Vector2* polygon, int polygonLength, - const char* name); - static void testIntersection(const Vector2* poly1, int poly1Length, - const Vector2* poly2, int poly2Length, - const Vector2* intersection, int intersectionLength); - static void updateBound(const Vector2 inVector, Vector2& lowerBound, Vector2& upperBound ); + static bool testConvex(const Vector2* polygon, int polygonLength, const char* name); + static void testIntersection(const Vector2* poly1, int poly1Length, const Vector2* poly2, + int poly2Length, const Vector2* intersection, + int intersectionLength); + static void updateBound(const Vector2 inVector, Vector2& lowerBound, Vector2& upperBound); static void dumpPolygon(const Vector2* poly, int polyLength, const char* polyName); static void dumpPolygon(const Vector3* poly, int polyLength, const char* polyName); #endif -}; // SpotShadow +}; // SpotShadow -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android -#endif // ANDROID_HWUI_SPOT_SHADOW_H +#endif // ANDROID_HWUI_SPOT_SHADOW_H diff --git a/libs/hwui/SwapBehavior.h b/libs/hwui/SwapBehavior.h new file mode 100644 index 000000000000..3a176d7bfbcc --- /dev/null +++ b/libs/hwui/SwapBehavior.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef HWUI_SWAPBEHAVIOR_H +#define HWUI_SWAPBEHAVIOR_H + +namespace android { +namespace uirenderer { +namespace renderthread { + +enum class SwapBehavior { + kSwap_default, + kSwap_discardBuffer, +}; + +} // namespace renderthread +} // namespace uirenderer +} // namespace android + +#endif // HWUI_SWAPBEHAVIOR_H diff --git a/libs/hwui/TessellationCache.cpp b/libs/hwui/TessellationCache.cpp index 63bf7bc443e0..c7d93da718e7 100644 --- a/libs/hwui/TessellationCache.cpp +++ b/libs/hwui/TessellationCache.cpp @@ -45,7 +45,8 @@ TessellationCache::Description::Description() memset(&shape, 0, sizeof(Shape)); } -TessellationCache::Description::Description(Type type, const Matrix4& transform, const SkPaint& paint) +TessellationCache::Description::Description(Type type, const Matrix4& transform, + const SkPaint& paint) : type(type) , aa(paint.isAntiAlias()) , cap(paint.getStrokeCap()) @@ -82,7 +83,7 @@ hash_t TessellationCache::Description::hash() const { hash = JenkinsHashMix(hash, android::hash_type(strokeWidth)); hash = JenkinsHashMix(hash, android::hash_type(scaleX)); hash = JenkinsHashMix(hash, android::hash_type(scaleY)); - hash = JenkinsHashMixBytes(hash, (uint8_t*) &shape, sizeof(Shape)); + hash = JenkinsHashMixBytes(hash, (uint8_t*)&shape, sizeof(Shape)); return JenkinsHashWhiten(hash); } @@ -94,25 +95,24 @@ void TessellationCache::Description::setupMatrixAndPaint(Matrix4* matrix, SkPain paint->setStrokeWidth(strokeWidth); } -TessellationCache::ShadowDescription::ShadowDescription() - : nodeKey(nullptr) { +TessellationCache::ShadowDescription::ShadowDescription() : nodeKey(nullptr) { memset(&matrixData, 0, sizeof(matrixData)); } -TessellationCache::ShadowDescription::ShadowDescription(const SkPath* nodeKey, const Matrix4* drawTransform) +TessellationCache::ShadowDescription::ShadowDescription(const SkPath* nodeKey, + const Matrix4* drawTransform) : nodeKey(nodeKey) { memcpy(&matrixData, drawTransform->data, sizeof(matrixData)); } bool TessellationCache::ShadowDescription::operator==( const TessellationCache::ShadowDescription& rhs) const { - return nodeKey == rhs.nodeKey - && memcmp(&matrixData, &rhs.matrixData, sizeof(matrixData)) == 0; + return nodeKey == rhs.nodeKey && memcmp(&matrixData, &rhs.matrixData, sizeof(matrixData)) == 0; } hash_t TessellationCache::ShadowDescription::hash() const { - uint32_t hash = JenkinsHashMixBytes(0, (uint8_t*) &nodeKey, sizeof(const void*)); - hash = JenkinsHashMixBytes(hash, (uint8_t*) &matrixData, sizeof(matrixData)); + uint32_t hash = JenkinsHashMixBytes(0, (uint8_t*)&nodeKey, sizeof(const void*)); + hash = JenkinsHashMixBytes(hash, (uint8_t*)&matrixData, sizeof(matrixData)); return JenkinsHashWhiten(hash); } @@ -123,9 +123,7 @@ hash_t TessellationCache::ShadowDescription::hash() const { class TessellationCache::TessellationTask : public Task<VertexBuffer*> { public: TessellationTask(Tessellator tessellator, const Description& description) - : tessellator(tessellator) - , description(description) { - } + : tessellator(tessellator), description(description) {} ~TessellationTask() {} @@ -135,8 +133,7 @@ public: class TessellationCache::TessellationProcessor : public TaskProcessor<VertexBuffer*> { public: - explicit TessellationProcessor(Caches& caches) - : TaskProcessor<VertexBuffer*>(&caches.tasks) {} + explicit TessellationProcessor(Caches& caches) : TaskProcessor<VertexBuffer*>(&caches.tasks) {} ~TessellationProcessor() {} virtual void onProcess(const sp<Task<VertexBuffer*> >& task) override { @@ -149,10 +146,7 @@ public: class TessellationCache::Buffer { public: - explicit Buffer(const sp<Task<VertexBuffer*> >& task) - : mTask(task) - , mBuffer(nullptr) { - } + explicit Buffer(const sp<Task<VertexBuffer*> >& task) : mTask(task), mBuffer(nullptr) {} ~Buffer() { mTask.clear(); @@ -203,18 +197,15 @@ static void reverseVertexArray(Vertex* polygon, int len) { } } -void tessellateShadows( - const Matrix4* drawTransform, const Rect* localClip, - bool isCasterOpaque, const SkPath* casterPerimeter, - const Matrix4* casterTransformXY, const Matrix4* casterTransformZ, - const Vector3& lightCenter, float lightRadius, - VertexBuffer& ambientBuffer, VertexBuffer& spotBuffer) { - +void tessellateShadows(const Matrix4* drawTransform, const Rect* localClip, bool isCasterOpaque, + const SkPath* casterPerimeter, const Matrix4* casterTransformXY, + const Matrix4* casterTransformZ, const Vector3& lightCenter, + float lightRadius, VertexBuffer& ambientBuffer, VertexBuffer& spotBuffer) { // tessellate caster outline into a 2d polygon std::vector<Vertex> casterVertices2d; const float casterRefinementThreshold = 2.0f; - PathTessellator::approximatePathOutlineVertices(*casterPerimeter, - casterRefinementThreshold, casterVertices2d); + PathTessellator::approximatePathOutlineVertices(*casterPerimeter, casterRefinementThreshold, + casterVertices2d); // Shadow requires CCW for now. TODO: remove potential double-reverse reverseVertexArray(&casterVertices2d.front(), casterVertices2d.size()); @@ -235,9 +226,8 @@ void tessellateShadows( } // map the centroid of the caster into 3d - Vector2 centroid = ShadowTessellator::centroid2d( - reinterpret_cast<const Vector2*>(&casterVertices2d.front()), - casterVertexCount); + Vector2 centroid = ShadowTessellator::centroid2d( + reinterpret_cast<const Vector2*>(&casterVertices2d.front()), casterVertexCount); Vector3 centroid3d = {centroid.x, centroid.y, 0}; mapPointFakeZ(centroid3d, casterTransformXY, casterTransformZ); @@ -257,14 +247,13 @@ void tessellateShadows( casterTransformXY->mapRect(casterBounds); // actual tessellation of both shadows - ShadowTessellator::tessellateAmbientShadow( - isCasterOpaque, casterPolygon, casterVertexCount, centroid3d, - casterBounds, *localClip, maxZ, ambientBuffer); - - ShadowTessellator::tessellateSpotShadow( - isCasterOpaque, casterPolygon, casterVertexCount, centroid3d, - *drawTransform, lightCenter, lightRadius, casterBounds, *localClip, - spotBuffer); + ShadowTessellator::tessellateAmbientShadow(isCasterOpaque, casterPolygon, casterVertexCount, + centroid3d, casterBounds, *localClip, maxZ, + ambientBuffer); + + ShadowTessellator::tessellateSpotShadow(isCasterOpaque, casterPolygon, casterVertexCount, + centroid3d, *drawTransform, lightCenter, lightRadius, + casterBounds, *localClip, spotBuffer); } class ShadowProcessor : public TaskProcessor<TessellationCache::vertexBuffer_pair_t> { @@ -278,8 +267,8 @@ public: ATRACE_NAME("shadow tessellation"); tessellateShadows(&t->drawTransform, &t->localClip, t->opaque, &t->casterPerimeter, - &t->transformXY, &t->transformZ, t->lightCenter, t->lightRadius, - t->ambientBuffer, t->spotBuffer); + &t->transformXY, &t->transformZ, t->lightCenter, t->lightRadius, + t->ambientBuffer, t->spotBuffer); t->setResult(TessellationCache::vertexBuffer_pair_t(&t->ambientBuffer, &t->spotBuffer)); } @@ -292,7 +281,8 @@ public: TessellationCache::TessellationCache() : mMaxSize(MB(1)) , mCache(LruCache<Description, Buffer*>::kUnlimitedCapacity) - , mShadowCache(LruCache<ShadowDescription, Task<vertexBuffer_pair_t*>*>::kUnlimitedCapacity) { + , mShadowCache( + LruCache<ShadowDescription, Task<vertexBuffer_pair_t*>*>::kUnlimitedCapacity) { mCache.setOnEntryRemovedListener(&mBufferRemovedListener); mShadowCache.setOnEntryRemovedListener(&mBufferPairRemovedListener); mDebugEnabled = Properties::debugLevel & kDebugCaches; @@ -323,7 +313,6 @@ uint32_t TessellationCache::getMaxSize() { // Caching /////////////////////////////////////////////////////////////////////////////// - void TessellationCache::trim() { uint32_t size = getSize(); while (size > mMaxSize) { @@ -343,7 +332,7 @@ void TessellationCache::clear() { /////////////////////////////////////////////////////////////////////////////// void TessellationCache::BufferRemovedListener::operator()(Description& description, - Buffer*& buffer) { + Buffer*& buffer) { delete buffer; } @@ -352,32 +341,31 @@ void TessellationCache::BufferRemovedListener::operator()(Description& descripti /////////////////////////////////////////////////////////////////////////////// void TessellationCache::precacheShadows(const Matrix4* drawTransform, const Rect& localClip, - bool opaque, const SkPath* casterPerimeter, - const Matrix4* transformXY, const Matrix4* transformZ, - const Vector3& lightCenter, float lightRadius) { + bool opaque, const SkPath* casterPerimeter, + const Matrix4* transformXY, const Matrix4* transformZ, + const Vector3& lightCenter, float lightRadius) { ShadowDescription key(casterPerimeter, drawTransform); if (mShadowCache.get(key)) return; - sp<ShadowTask> task = new ShadowTask(drawTransform, localClip, opaque, - casterPerimeter, transformXY, transformZ, lightCenter, lightRadius); + sp<ShadowTask> task = new ShadowTask(drawTransform, localClip, opaque, casterPerimeter, + transformXY, transformZ, lightCenter, lightRadius); if (mShadowProcessor == nullptr) { mShadowProcessor = new ShadowProcessor(Caches::getInstance()); } mShadowProcessor->add(task); - task->incStrong(nullptr); // not using sp<>s, so manually ref while in the cache + task->incStrong(nullptr); // not using sp<>s, so manually ref while in the cache mShadowCache.put(key, task.get()); } sp<TessellationCache::ShadowTask> TessellationCache::getShadowTask( - const Matrix4* drawTransform, const Rect& localClip, - bool opaque, const SkPath* casterPerimeter, - const Matrix4* transformXY, const Matrix4* transformZ, + const Matrix4* drawTransform, const Rect& localClip, bool opaque, + const SkPath* casterPerimeter, const Matrix4* transformXY, const Matrix4* transformZ, const Vector3& lightCenter, float lightRadius) { ShadowDescription key(casterPerimeter, drawTransform); ShadowTask* task = static_cast<ShadowTask*>(mShadowCache.get(key)); if (!task) { - precacheShadows(drawTransform, localClip, opaque, casterPerimeter, - transformXY, transformZ, lightCenter, lightRadius); + precacheShadows(drawTransform, localClip, opaque, casterPerimeter, transformXY, transformZ, + lightCenter, lightRadius); task = static_cast<ShadowTask*>(mShadowCache.get(key)); } LOG_ALWAYS_FATAL_IF(task == nullptr, "shadow not precached"); @@ -388,8 +376,8 @@ sp<TessellationCache::ShadowTask> TessellationCache::getShadowTask( // Tessellation precaching /////////////////////////////////////////////////////////////////////////////// -TessellationCache::Buffer* TessellationCache::getOrCreateBuffer( - const Description& entry, Tessellator tessellator) { +TessellationCache::Buffer* TessellationCache::getOrCreateBuffer(const Description& entry, + Tessellator tessellator) { Buffer* buffer = mCache.get(entry); if (!buffer) { // not cached, enqueue a task to fill the buffer @@ -408,7 +396,7 @@ TessellationCache::Buffer* TessellationCache::getOrCreateBuffer( } static VertexBuffer* tessellatePath(const TessellationCache::Description& description, - const SkPath& path) { + const SkPath& path) { Matrix4 matrix; SkPaint paint; description.setupMatrixAndPaint(&matrix, &paint); @@ -422,8 +410,8 @@ static VertexBuffer* tessellatePath(const TessellationCache::Description& descri /////////////////////////////////////////////////////////////////////////////// static VertexBuffer* tessellateRoundRect(const TessellationCache::Description& description) { - SkRect rect = SkRect::MakeWH(description.shape.roundRect.width, - description.shape.roundRect.height); + SkRect rect = + SkRect::MakeWH(description.shape.roundRect.width, description.shape.roundRect.height); float rx = description.shape.roundRect.rx; float ry = description.shape.roundRect.ry; if (description.style == SkPaint::kStrokeAndFill_Style) { @@ -437,9 +425,9 @@ static VertexBuffer* tessellateRoundRect(const TessellationCache::Description& d return tessellatePath(description, path); } -TessellationCache::Buffer* TessellationCache::getRoundRectBuffer( - const Matrix4& transform, const SkPaint& paint, - float width, float height, float rx, float ry) { +TessellationCache::Buffer* TessellationCache::getRoundRectBuffer(const Matrix4& transform, + const SkPaint& paint, float width, + float height, float rx, float ry) { Description entry(Description::Type::RoundRect, transform, paint); entry.shape.roundRect.width = width; entry.shape.roundRect.height = height; @@ -448,9 +436,9 @@ TessellationCache::Buffer* TessellationCache::getRoundRectBuffer( return getOrCreateBuffer(entry, &tessellateRoundRect); } const VertexBuffer* TessellationCache::getRoundRect(const Matrix4& transform, const SkPaint& paint, - float width, float height, float rx, float ry) { + float width, float height, float rx, float ry) { return getRoundRectBuffer(transform, paint, width, height, rx, ry)->getVertexBuffer(); } -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/TessellationCache.h b/libs/hwui/TessellationCache.h index ccad1b7bd415..a0f0ed4653e0 100644 --- a/libs/hwui/TessellationCache.h +++ b/libs/hwui/TessellationCache.h @@ -90,17 +90,16 @@ public: class ShadowTask : public Task<vertexBuffer_pair_t> { public: ShadowTask(const Matrix4* drawTransform, const Rect& localClip, bool opaque, - const SkPath* casterPerimeter, const Matrix4* transformXY, const Matrix4* transformZ, - const Vector3& lightCenter, float lightRadius) - : drawTransform(*drawTransform) - , localClip(localClip) - , opaque(opaque) - , casterPerimeter(*casterPerimeter) - , transformXY(*transformXY) - , transformZ(*transformZ) - , lightCenter(lightCenter) - , lightRadius(lightRadius) { - } + const SkPath* casterPerimeter, const Matrix4* transformXY, + const Matrix4* transformZ, const Vector3& lightCenter, float lightRadius) + : drawTransform(*drawTransform) + , localClip(localClip) + , opaque(opaque) + , casterPerimeter(*casterPerimeter) + , transformXY(*transformXY) + , transformZ(*transformZ) + , lightCenter(lightCenter) + , lightRadius(lightRadius) {} /* Note - we deep copy all task parameters, because *even though* pointers into Allocator * controlled objects (like the SkPath and Matrix4s) should be safe for the entire frame, @@ -153,17 +152,17 @@ public: // TODO: precache/get for Oval, Lines, Points, etc. - void precacheRoundRect(const Matrix4& transform, const SkPaint& paint, - float width, float height, float rx, float ry) { + void precacheRoundRect(const Matrix4& transform, const SkPaint& paint, float width, + float height, float rx, float ry) { getRoundRectBuffer(transform, paint, width, height, rx, ry); } - const VertexBuffer* getRoundRect(const Matrix4& transform, const SkPaint& paint, - float width, float height, float rx, float ry); + const VertexBuffer* getRoundRect(const Matrix4& transform, const SkPaint& paint, float width, + float height, float rx, float ry); - sp<ShadowTask> getShadowTask(const Matrix4* drawTransform, const Rect& localClip, - bool opaque, const SkPath* casterPerimeter, - const Matrix4* transformXY, const Matrix4* transformZ, - const Vector3& lightCenter, float lightRadius); + sp<ShadowTask> getShadowTask(const Matrix4* drawTransform, const Rect& localClip, bool opaque, + const SkPath* casterPerimeter, const Matrix4* transformXY, + const Matrix4* transformZ, const Vector3& lightCenter, + float lightRadius); private: class Buffer; @@ -172,15 +171,14 @@ private: typedef VertexBuffer* (*Tessellator)(const Description&); - void precacheShadows(const Matrix4* drawTransform, const Rect& localClip, - bool opaque, const SkPath* casterPerimeter, - const Matrix4* transformXY, const Matrix4* transformZ, - const Vector3& lightCenter, float lightRadius); + void precacheShadows(const Matrix4* drawTransform, const Rect& localClip, bool opaque, + const SkPath* casterPerimeter, const Matrix4* transformXY, + const Matrix4* transformZ, const Vector3& lightCenter, float lightRadius); - Buffer* getRectBuffer(const Matrix4& transform, const SkPaint& paint, - float width, float height); - Buffer* getRoundRectBuffer(const Matrix4& transform, const SkPaint& paint, - float width, float height, float rx, float ry); + Buffer* getRectBuffer(const Matrix4& transform, const SkPaint& paint, float width, + float height); + Buffer* getRoundRectBuffer(const Matrix4& transform, const SkPaint& paint, float width, + float height, float rx, float ry); Buffer* getOrCreateBuffer(const Description& entry, Tessellator tessellator); @@ -207,21 +205,21 @@ private: // holds a pointer, and implicit strong ref to each shadow task of the frame LruCache<ShadowDescription, Task<vertexBuffer_pair_t>*> mShadowCache; - class BufferPairRemovedListener : public OnEntryRemoved<ShadowDescription, Task<vertexBuffer_pair_t>*> { - void operator()(ShadowDescription& description, Task<vertexBuffer_pair_t>*& bufferPairTask) override { + class BufferPairRemovedListener + : public OnEntryRemoved<ShadowDescription, Task<vertexBuffer_pair_t>*> { + void operator()(ShadowDescription& description, + Task<vertexBuffer_pair_t>*& bufferPairTask) override { bufferPairTask->decStrong(nullptr); } }; BufferPairRemovedListener mBufferPairRemovedListener; -}; // class TessellationCache +}; // class TessellationCache -void tessellateShadows( - const Matrix4* drawTransform, const Rect* localClip, - bool isCasterOpaque, const SkPath* casterPerimeter, - const Matrix4* casterTransformXY, const Matrix4* casterTransformZ, - const Vector3& lightCenter, float lightRadius, - VertexBuffer& ambientBuffer, VertexBuffer& spotBuffer); +void tessellateShadows(const Matrix4* drawTransform, const Rect* localClip, bool isCasterOpaque, + const SkPath* casterPerimeter, const Matrix4* casterTransformXY, + const Matrix4* casterTransformZ, const Vector3& lightCenter, + float lightRadius, VertexBuffer& ambientBuffer, VertexBuffer& spotBuffer); -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/TextDropShadowCache.cpp b/libs/hwui/TextDropShadowCache.cpp index c521892c69df..c892ceb3e14d 100644 --- a/libs/hwui/TextDropShadowCache.cpp +++ b/libs/hwui/TextDropShadowCache.cpp @@ -19,8 +19,8 @@ #include "Caches.h" #include "Debug.h" #include "FontRenderer.h" -#include "TextDropShadowCache.h" #include "Properties.h" +#include "TextDropShadowCache.h" namespace android { namespace uirenderer { @@ -38,8 +38,7 @@ hash_t ShadowText::hash() const { hash = JenkinsHashMix(hash, android::hash_type(italicStyle)); hash = JenkinsHashMix(hash, android::hash_type(scaleX)); if (glyphs) { - hash = JenkinsHashMixShorts( - hash, reinterpret_cast<const uint16_t*>(glyphs), glyphCount); + hash = JenkinsHashMixShorts(hash, reinterpret_cast<const uint16_t*>(glyphs), glyphCount); } if (positions) { for (uint32_t i = 0; i < glyphCount * 2; i++) { @@ -146,15 +145,15 @@ void TextDropShadowCache::clear() { } ShadowTexture* TextDropShadowCache::get(const SkPaint* paint, const glyph_t* glyphs, int numGlyphs, - float radius, const float* positions) { + float radius, const float* positions) { ShadowText entry(paint, radius, numGlyphs, glyphs, positions); ShadowTexture* texture = mCache.get(entry); if (!texture) { SkPaint paintCopy(*paint); paintCopy.setTextAlign(SkPaint::kLeft_Align); - FontRenderer::DropShadow shadow = mRenderer->renderDropShadow(&paintCopy, glyphs, numGlyphs, - radius, positions); + FontRenderer::DropShadow shadow = + mRenderer->renderDropShadow(&paintCopy, glyphs, numGlyphs, radius, positions); if (!shadow.image) { return nullptr; @@ -174,14 +173,15 @@ ShadowTexture* TextDropShadowCache::get(const SkPaint* paint, const glyph_t* gly if (size < mMaxSize) { while (mSize + size > mMaxSize) { LOG_ALWAYS_FATAL_IF(!mCache.removeOldest(), - "Failed to remove oldest from cache. mSize = %" - PRIu32 ", mCache.size() = %zu", mSize, mCache.size()); + "Failed to remove oldest from cache. mSize = %" PRIu32 + ", mCache.size() = %zu", + mSize, mCache.size()); } } // Textures are Alpha8 - texture->upload(GL_ALPHA, shadow.width, shadow.height, - GL_ALPHA, GL_UNSIGNED_BYTE, shadow.image); + texture->upload(GL_ALPHA, shadow.width, shadow.height, GL_ALPHA, GL_UNSIGNED_BYTE, + shadow.image); texture->setFilter(GL_LINEAR); texture->setWrap(GL_CLAMP_TO_EDGE); @@ -205,5 +205,5 @@ ShadowTexture* TextDropShadowCache::get(const SkPaint* paint, const glyph_t* gly return texture; } -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/TextDropShadowCache.h b/libs/hwui/TextDropShadowCache.h index 13e87749029a..86a012970f17 100644 --- a/libs/hwui/TextDropShadowCache.h +++ b/libs/hwui/TextDropShadowCache.h @@ -24,8 +24,8 @@ #include <utils/LruCache.h> #include <utils/String16.h> -#include "font/Font.h" #include "Texture.h" +#include "font/Font.h" namespace android { namespace uirenderer { @@ -34,13 +34,20 @@ class Caches; class FontRenderer; struct ShadowText { - ShadowText(): glyphCount(0), radius(0.0f), textSize(0.0f), typeface(nullptr), - flags(0), italicStyle(0.0f), scaleX(0), glyphs(nullptr), positions(nullptr) { - } + ShadowText() + : glyphCount(0) + , radius(0.0f) + , textSize(0.0f) + , typeface(nullptr) + , flags(0) + , italicStyle(0.0f) + , scaleX(0) + , glyphs(nullptr) + , positions(nullptr) {} // len is the number of bytes in text ShadowText(const SkPaint* paint, float radius, uint32_t glyphCount, const glyph_t* srcGlyphs, - const float* positions) + const float* positions) : glyphCount(glyphCount) , radius(radius) , textSize(paint->getTextSize()) @@ -49,23 +56,17 @@ struct ShadowText { , italicStyle(paint->getTextSkewX()) , scaleX(paint->getTextScaleX()) , glyphs(srcGlyphs) - , positions(positions) { - } + , positions(positions) {} - ~ShadowText() { - } + ~ShadowText() {} hash_t hash() const; static int compare(const ShadowText& lhs, const ShadowText& rhs); - bool operator==(const ShadowText& other) const { - return compare(*this, other) == 0; - } + bool operator==(const ShadowText& other) const { return compare(*this, other) == 0; } - bool operator!=(const ShadowText& other) const { - return compare(*this, other) != 0; - } + bool operator!=(const ShadowText& other) const { return compare(*this, other) != 0; } void copyTextLocally() { str.setTo(reinterpret_cast<const char16_t*>(glyphs), glyphCount); @@ -91,7 +92,7 @@ struct ShadowText { String16 str; Vector<float> positionsCopy; -}; // struct ShadowText +}; // struct ShadowText // Caching support @@ -110,15 +111,14 @@ inline hash_t hash_type(const ShadowText& entry) { /** * Alpha texture used to represent a shadow. */ -struct ShadowTexture: public Texture { - explicit ShadowTexture(Caches& caches): Texture(caches) { - } +struct ShadowTexture : public Texture { + explicit ShadowTexture(Caches& caches) : Texture(caches) {} float left; float top; -}; // struct ShadowTexture +}; // struct ShadowTexture -class TextDropShadowCache: public OnEntryRemoved<ShadowText, ShadowTexture*> { +class TextDropShadowCache : public OnEntryRemoved<ShadowText, ShadowTexture*> { public: TextDropShadowCache(); explicit TextDropShadowCache(uint32_t maxByteSize); @@ -130,17 +130,15 @@ public: */ void operator()(ShadowText& text, ShadowTexture*& texture) override; - ShadowTexture* get(const SkPaint* paint, const glyph_t* text, - int numGlyphs, float radius, const float* positions); + ShadowTexture* get(const SkPaint* paint, const glyph_t* text, int numGlyphs, float radius, + const float* positions); /** * Clears the cache. This causes all textures to be deleted. */ void clear(); - void setFontRenderer(FontRenderer& fontRenderer) { - mRenderer = &fontRenderer; - } + void setFontRenderer(FontRenderer& fontRenderer) { mRenderer = &fontRenderer; } /** * Returns the maximum size of the cache in bytes. @@ -158,9 +156,9 @@ private: const uint32_t mMaxSize; FontRenderer* mRenderer = nullptr; bool mDebugEnabled; -}; // class TextDropShadowCache +}; // class TextDropShadowCache -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android -#endif // ANDROID_HWUI_TEXT_DROP_SHADOW_CACHE_H +#endif // ANDROID_HWUI_TEXT_DROP_SHADOW_CACHE_H diff --git a/libs/hwui/Texture.cpp b/libs/hwui/Texture.cpp index b7c1e290370f..1e90eebe3bb8 100644 --- a/libs/hwui/Texture.cpp +++ b/libs/hwui/Texture.cpp @@ -14,8 +14,8 @@ * limitations under the License. */ -#include "Caches.h" #include "Texture.h" +#include "Caches.h" #include "utils/GLUtils.h" #include "utils/MathUtils.h" #include "utils/TraceUtils.h" @@ -32,22 +32,22 @@ namespace uirenderer { // Number of bytes used by a texture in the given format static int bytesPerPixel(GLint glFormat) { switch (glFormat) { - // The wrapped-texture case, usually means a SurfaceTexture - case 0: - return 0; - case GL_LUMINANCE: - case GL_ALPHA: - return 1; - case GL_SRGB8: - case GL_RGB: - return 3; - case GL_SRGB8_ALPHA8: - case GL_RGBA: - return 4; - case GL_RGBA16F: - return 8; - default: - LOG_ALWAYS_FATAL("UNKNOWN FORMAT 0x%x", glFormat); + // The wrapped-texture case, usually means a SurfaceTexture + case 0: + return 0; + case GL_LUMINANCE: + case GL_ALPHA: + return 1; + case GL_SRGB8: + case GL_RGB: + return 3; + case GL_SRGB8_ALPHA8: + case GL_RGBA: + return 4; + case GL_RGBA16F: + return 8; + default: + LOG_ALWAYS_FATAL("UNKNOWN FORMAT 0x%x", glFormat); } } @@ -92,13 +92,10 @@ void Texture::deleteTexture() { } } -bool Texture::updateLayout(uint32_t width, uint32_t height, GLint internalFormat, - GLint format, GLenum target) { - if (mWidth == width - && mHeight == height - && mFormat == format - && mInternalFormat == internalFormat - && mTarget == target) { +bool Texture::updateLayout(uint32_t width, uint32_t height, GLint internalFormat, GLint format, + GLenum target) { + if (mWidth == width && mHeight == height && mFormat == format && + mInternalFormat == internalFormat && mTarget == target) { return false; } mWidth = width; @@ -117,8 +114,8 @@ void Texture::resetCachedParams() { mMagFilter = GL_LINEAR; } -void Texture::upload(GLint internalFormat, uint32_t width, uint32_t height, - GLenum format, GLenum type, const void* pixels) { +void Texture::upload(GLint internalFormat, uint32_t width, uint32_t height, GLenum format, + GLenum type, const void* pixels) { GL_CHECKPOINT(MODERATE); // We don't have color space information, we assume the data is gamma encoded @@ -132,11 +129,9 @@ void Texture::upload(GLint internalFormat, uint32_t width, uint32_t height, } mCaches.textureState().bindTexture(GL_TEXTURE_2D, mId); if (needsAlloc) { - glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, mWidth, mHeight, 0, - format, type, pixels); + glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, mWidth, mHeight, 0, format, type, pixels); } else if (pixels) { - glTexSubImage2D(GL_TEXTURE_2D, 0, internalFormat, mWidth, mHeight, 0, - format, type, pixels); + glTexSubImage2D(GL_TEXTURE_2D, 0, internalFormat, mWidth, mHeight, 0, format, type, pixels); } GL_CHECKPOINT(MODERATE); } @@ -148,15 +143,15 @@ void Texture::uploadHardwareBitmapToTexture(GraphicBuffer* buffer) { mEglImageHandle = EGL_NO_IMAGE_KHR; } mEglImageHandle = eglCreateImageKHR(eglDisplayHandle, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, - buffer->getNativeBuffer(), 0); + buffer->getNativeBuffer(), 0); glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, mEglImageHandle); } static void uploadToTexture(bool resize, GLint internalFormat, GLenum format, GLenum type, - GLsizei stride, GLsizei bpp, GLsizei width, GLsizei height, const GLvoid * data) { - - const bool useStride = stride != width - && Caches::getInstance().extensions().hasUnpackRowLength(); + GLsizei stride, GLsizei bpp, GLsizei width, GLsizei height, + const GLvoid* data) { + const bool useStride = + stride != width && Caches::getInstance().extensions().hasUnpackRowLength(); if ((stride == width) || useStride) { if (useStride) { glPixelStorei(GL_UNPACK_ROW_LENGTH, stride); @@ -175,11 +170,11 @@ static void uploadToTexture(bool resize, GLint internalFormat, GLenum format, GL // With OpenGL ES 2.0 we need to copy the bitmap in a temporary buffer // if the stride doesn't match the width - GLvoid * temp = (GLvoid *) malloc(width * height * bpp); + GLvoid* temp = (GLvoid*)malloc(width * height * bpp); if (!temp) return; - uint8_t * pDst = (uint8_t *)temp; - uint8_t * pSrc = (uint8_t *)data; + uint8_t* pDst = (uint8_t*)temp; + uint8_t* pSrc = (uint8_t*)data; for (GLsizei i = 0; i < height; i++) { memcpy(pDst, pSrc, width * bpp); pDst += width * bpp; @@ -196,69 +191,70 @@ static void uploadToTexture(bool resize, GLint internalFormat, GLenum format, GL } } -void Texture::colorTypeToGlFormatAndType(const Caches& caches, SkColorType colorType, - bool needSRGB, GLint* outInternalFormat, GLint* outFormat, GLint* outType) { +void Texture::colorTypeToGlFormatAndType(const Caches& caches, SkColorType colorType, bool needSRGB, + GLint* outInternalFormat, GLint* outFormat, + GLint* outType) { switch (colorType) { - case kAlpha_8_SkColorType: - *outFormat = GL_ALPHA; - *outInternalFormat = GL_ALPHA; - *outType = GL_UNSIGNED_BYTE; - break; - case kRGB_565_SkColorType: - if (needSRGB) { - // We would ideally use a GL_RGB/GL_SRGB8 texture but the - // intermediate Skia bitmap needs to be ARGB_8888 - *outFormat = GL_RGBA; - *outInternalFormat = caches.rgbaInternalFormat(); + case kAlpha_8_SkColorType: + *outFormat = GL_ALPHA; + *outInternalFormat = GL_ALPHA; *outType = GL_UNSIGNED_BYTE; - } else { - *outFormat = GL_RGB; - *outInternalFormat = GL_RGB; - *outType = GL_UNSIGNED_SHORT_5_6_5; - } - break; - // ARGB_4444 is upconverted to RGBA_8888 - case kARGB_4444_SkColorType: - case kN32_SkColorType: - *outFormat = GL_RGBA; - *outInternalFormat = caches.rgbaInternalFormat(needSRGB); - *outType = GL_UNSIGNED_BYTE; - break; - case kGray_8_SkColorType: - *outFormat = GL_LUMINANCE; - *outInternalFormat = GL_LUMINANCE; - *outType = GL_UNSIGNED_BYTE; - break; - case kRGBA_F16_SkColorType: - if (caches.extensions().getMajorGlVersion() >= 3) { - // This format is always linear - *outFormat = GL_RGBA; - *outInternalFormat = GL_RGBA16F; - *outType = GL_HALF_FLOAT; - } else { + break; + case kRGB_565_SkColorType: + if (needSRGB) { + // We would ideally use a GL_RGB/GL_SRGB8 texture but the + // intermediate Skia bitmap needs to be ARGB_8888 + *outFormat = GL_RGBA; + *outInternalFormat = caches.rgbaInternalFormat(); + *outType = GL_UNSIGNED_BYTE; + } else { + *outFormat = GL_RGB; + *outInternalFormat = GL_RGB; + *outType = GL_UNSIGNED_SHORT_5_6_5; + } + break; + // ARGB_4444 is upconverted to RGBA_8888 + case kARGB_4444_SkColorType: + case kN32_SkColorType: *outFormat = GL_RGBA; - *outInternalFormat = caches.rgbaInternalFormat(true); + *outInternalFormat = caches.rgbaInternalFormat(needSRGB); *outType = GL_UNSIGNED_BYTE; - } - break; - default: - LOG_ALWAYS_FATAL("Unsupported bitmap colorType: %d", colorType); - break; + break; + case kGray_8_SkColorType: + *outFormat = GL_LUMINANCE; + *outInternalFormat = GL_LUMINANCE; + *outType = GL_UNSIGNED_BYTE; + break; + case kRGBA_F16_SkColorType: + if (caches.extensions().getMajorGlVersion() >= 3) { + // This format is always linear + *outFormat = GL_RGBA; + *outInternalFormat = GL_RGBA16F; + *outType = GL_HALF_FLOAT; + } else { + *outFormat = GL_RGBA; + *outInternalFormat = caches.rgbaInternalFormat(true); + *outType = GL_UNSIGNED_BYTE; + } + break; + default: + LOG_ALWAYS_FATAL("Unsupported bitmap colorType: %d", colorType); + break; } } SkBitmap Texture::uploadToN32(const SkBitmap& bitmap, bool hasLinearBlending, - sk_sp<SkColorSpace> sRGB) { + sk_sp<SkColorSpace> sRGB) { SkBitmap rgbaBitmap; rgbaBitmap.allocPixels(SkImageInfo::MakeN32(bitmap.width(), bitmap.height(), - bitmap.info().alphaType(), hasLinearBlending ? sRGB : nullptr)); + bitmap.info().alphaType(), + hasLinearBlending ? sRGB : nullptr)); rgbaBitmap.eraseColor(0); if (bitmap.colorType() == kRGBA_F16_SkColorType) { // Drawing RGBA_F16 onto ARGB_8888 is not supported - bitmap.readPixels(rgbaBitmap.info() - .makeColorSpace(SkColorSpace::MakeSRGB()), - rgbaBitmap.getPixels(), rgbaBitmap.rowBytes(), 0, 0); + bitmap.readPixels(rgbaBitmap.info().makeColorSpace(SkColorSpace::MakeSRGB()), + rgbaBitmap.getPixels(), rgbaBitmap.rowBytes(), 0, 0); } else { SkCanvas canvas(rgbaBitmap); canvas.drawBitmap(bitmap, 0.0f, 0.0f, nullptr); @@ -268,12 +264,11 @@ SkBitmap Texture::uploadToN32(const SkBitmap& bitmap, bool hasLinearBlending, } bool Texture::hasUnsupportedColorType(const SkImageInfo& info, bool hasLinearBlending) { - return info.colorType() == kARGB_4444_SkColorType - || (info.colorType() == kRGB_565_SkColorType - && hasLinearBlending - && info.colorSpace()->isSRGB()) - || (info.colorType() == kRGBA_F16_SkColorType - && Caches::getInstance().extensions().getMajorGlVersion() < 3); + return info.colorType() == kARGB_4444_SkColorType || + (info.colorType() == kRGB_565_SkColorType && hasLinearBlending && + info.colorSpace()->isSRGB()) || + (info.colorType() == kRGBA_F16_SkColorType && + Caches::getInstance().extensions().getMajorGlVersion() < 3); } void Texture::upload(Bitmap& bitmap) { @@ -298,13 +293,13 @@ void Texture::upload(Bitmap& bitmap) { bool needSRGB = transferFunctionCloseToSRGB(bitmap.info().colorSpace()); GLint internalFormat, format, type; - colorTypeToGlFormatAndType(mCaches, bitmap.colorType(), - needSRGB && hasLinearBlending, &internalFormat, &format, &type); + colorTypeToGlFormatAndType(mCaches, bitmap.colorType(), needSRGB && hasLinearBlending, + &internalFormat, &format, &type); // Some devices don't support GL_RGBA16F, so we need to compare the color type // and internal GL format to decide what to do with 16 bit bitmaps - bool rgba16fNeedsConversion = bitmap.colorType() == kRGBA_F16_SkColorType - && internalFormat != GL_RGBA16F; + bool rgba16fNeedsConversion = + bitmap.colorType() == kRGBA_F16_SkColorType && internalFormat != GL_RGBA16F; // RGBA16F is always linear extended sRGB if (internalFormat == GL_RGBA16F) { @@ -330,16 +325,16 @@ void Texture::upload(Bitmap& bitmap) { float data[16]; xyzMatrix.asColMajorf(data); - ColorSpace::TransferParameters p = - {fn.fG, fn.fA, fn.fB, fn.fC, fn.fD, fn.fE, fn.fF}; - ColorSpace src("Unnamed", mat4f((const float*) &data[0]).upperLeft(), p); + ColorSpace::TransferParameters p = {fn.fG, fn.fA, fn.fB, fn.fC, + fn.fD, fn.fE, fn.fF}; + ColorSpace src("Unnamed", mat4f((const float*)&data[0]).upperLeft(), p); mConnector.reset(new ColorSpaceConnector(src, ColorSpace::sRGB())); // A non-sRGB color space might have a transfer function close enough to sRGB // that we can save shader instructions by using an sRGB sampler // This is only possible if we have hardware support for sRGB textures - if (needSRGB && internalFormat == GL_RGBA - && mCaches.extensions().hasSRGB() && !bitmap.isHardware()) { + if (needSRGB && internalFormat == GL_RGBA && mCaches.extensions().hasSRGB() && + !bitmap.isHardware()) { internalFormat = GL_SRGB8_ALPHA8; } } @@ -360,13 +355,14 @@ void Texture::upload(Bitmap& bitmap) { sk_sp<SkColorSpace> sRGB = SkColorSpace::MakeSRGB(); SkBitmap rgbaBitmap = uploadToN32(skBitmap, hasLinearBlending, std::move(sRGB)); uploadToTexture(needsAlloc, internalFormat, format, type, rgbaBitmap.rowBytesAsPixels(), - rgbaBitmap.bytesPerPixel(), rgbaBitmap.width(), - rgbaBitmap.height(), rgbaBitmap.getPixels()); + rgbaBitmap.bytesPerPixel(), rgbaBitmap.width(), rgbaBitmap.height(), + rgbaBitmap.getPixels()); } else if (bitmap.isHardware()) { uploadHardwareBitmapToTexture(bitmap.graphicBuffer()); } else { uploadToTexture(needsAlloc, internalFormat, format, type, bitmap.rowBytesAsPixels(), - bitmap.info().bytesPerPixel(), bitmap.width(), bitmap.height(), bitmap.pixels()); + bitmap.info().bytesPerPixel(), bitmap.width(), bitmap.height(), + bitmap.pixels()); } if (canMipMap) { @@ -382,8 +378,8 @@ void Texture::upload(Bitmap& bitmap) { } } -void Texture::wrap(GLuint id, uint32_t width, uint32_t height, - GLint internalFormat, GLint format, GLenum target) { +void Texture::wrap(GLuint id, uint32_t width, uint32_t height, GLint internalFormat, GLint format, + GLenum target) { mId = id; mWidth = width; mHeight = height; @@ -399,8 +395,8 @@ TransferFunctionType Texture::getTransferFunctionType() const { if (mConnector.get() != nullptr && mInternalFormat != GL_SRGB8_ALPHA8) { const ColorSpace::TransferParameters& p = mConnector->getSource().getTransferParameters(); if (MathUtils::isZero(p.e) && MathUtils::isZero(p.f)) { - if (MathUtils::areEqual(p.a, 1.0f) && MathUtils::isZero(p.b) - && MathUtils::isZero(p.c) && MathUtils::isZero(p.d)) { + if (MathUtils::areEqual(p.a, 1.0f) && MathUtils::isZero(p.b) && + MathUtils::isZero(p.c) && MathUtils::isZero(p.d)) { if (MathUtils::areEqual(p.g, 1.0f)) { return TransferFunctionType::None; } @@ -413,5 +409,5 @@ TransferFunctionType Texture::getTransferFunctionType() const { return TransferFunctionType::None; } -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/Texture.h b/libs/hwui/Texture.h index 7f742e604838..5b7e4e261f30 100644 --- a/libs/hwui/Texture.h +++ b/libs/hwui/Texture.h @@ -27,10 +27,10 @@ #include <ui/ColorSpace.h> -#include <GLES2/gl2.h> -#include <GLES3/gl3.h> #include <EGL/egl.h> #include <EGL/eglext.h> +#include <GLES2/gl2.h> +#include <GLES3/gl3.h> #include <SkBitmap.h> namespace android { @@ -48,32 +48,30 @@ class Layer; */ class Texture : public GpuMemoryTracker { public: - static SkBitmap uploadToN32(const SkBitmap& bitmap, - bool hasLinearBlending, sk_sp<SkColorSpace> sRGB); + static SkBitmap uploadToN32(const SkBitmap& bitmap, bool hasLinearBlending, + sk_sp<SkColorSpace> sRGB); static bool hasUnsupportedColorType(const SkImageInfo& info, bool hasLinearBlending); static void colorTypeToGlFormatAndType(const Caches& caches, SkColorType colorType, - bool needSRGB, GLint* outInternalFormat, GLint* outFormat, GLint* outType); + bool needSRGB, GLint* outInternalFormat, + GLint* outFormat, GLint* outType); - explicit Texture(Caches& caches) - : GpuMemoryTracker(GpuObjectType::Texture) - , mCaches(caches) - { } + explicit Texture(Caches& caches) : GpuMemoryTracker(GpuObjectType::Texture), mCaches(caches) {} - virtual ~Texture() { } + virtual ~Texture() {} inline void setWrap(GLenum wrap, bool bindTexture = false, bool force = false) { setWrapST(wrap, wrap, bindTexture, force); } virtual void setWrapST(GLenum wrapS, GLenum wrapT, bool bindTexture = false, - bool force = false); + bool force = false); inline void setFilter(GLenum filter, bool bindTexture = false, bool force = false) { setFilterMinMag(filter, filter, bindTexture, force); } virtual void setFilterMinMag(GLenum min, GLenum mag, bool bindTexture = false, - bool force = false); + bool force = false); /** * Convenience method to call glDeleteTextures() on this texture's id. @@ -89,7 +87,7 @@ public: */ void resize(uint32_t width, uint32_t height, GLint internalFormat, GLint format) { upload(internalFormat, width, height, format, - internalFormat == GL_RGBA16F ? GL_HALF_FLOAT : GL_UNSIGNED_BYTE, nullptr); + internalFormat == GL_RGBA16F ? GL_HALF_FLOAT : GL_UNSIGNED_BYTE, nullptr); } /** @@ -104,60 +102,42 @@ public: /** * Basically glTexImage2D/glTexSubImage2D. */ - void upload(GLint internalFormat, uint32_t width, uint32_t height, - GLenum format, GLenum type, const void* pixels); + void upload(GLint internalFormat, uint32_t width, uint32_t height, GLenum format, GLenum type, + const void* pixels); /** * Wraps an existing texture. */ - void wrap(GLuint id, uint32_t width, uint32_t height, GLint internalFormat, - GLint format, GLenum target); + void wrap(GLuint id, uint32_t width, uint32_t height, GLint internalFormat, GLint format, + GLenum target); - GLuint id() const { - return mId; - } + GLuint id() const { return mId; } - uint32_t width() const { - return mWidth; - } + uint32_t width() const { return mWidth; } - uint32_t height() const { - return mHeight; - } + uint32_t height() const { return mHeight; } - GLint format() const { - return mFormat; - } + GLint format() const { return mFormat; } - GLint internalFormat() const { - return mInternalFormat; - } + GLint internalFormat() const { return mInternalFormat; } - GLenum target() const { - return mTarget; - } + GLenum target() const { return mTarget; } /** * Returns nullptr if this texture does not require color space conversion * to sRGB, or a valid pointer to a ColorSpaceConnector if a conversion * is required. */ - constexpr const ColorSpaceConnector* getColorSpaceConnector() const { - return mConnector.get(); - } + constexpr const ColorSpaceConnector* getColorSpaceConnector() const { return mConnector.get(); } - constexpr bool hasColorSpaceConversion() const { - return mConnector.get() != nullptr; - } + constexpr bool hasColorSpaceConversion() const { return mConnector.get() != nullptr; } TransferFunctionType getTransferFunctionType() const; /** * Returns true if this texture uses a linear encoding format. */ - constexpr bool isLinear() const { - return mIsLinear; - } + constexpr bool isLinear() const { return mIsLinear; } /** * Generation of the backing bitmap, @@ -190,6 +170,7 @@ public: * the current frame. This is reset at the start of a new frame. */ void* isInUse = nullptr; + private: // TODO: Temporarily grant private access to GlLayer, remove once // GlLayer can be de-tangled from being a dual-purpose render target @@ -197,8 +178,8 @@ private: friend class GlLayer; // Returns true if the texture layout (size, format, etc.) changed, false if it was the same - bool updateLayout(uint32_t width, uint32_t height, GLint internalFormat, - GLint format, GLenum target); + bool updateLayout(uint32_t width, uint32_t height, GLint internalFormat, GLint format, + GLenum target); void uploadHardwareBitmapToTexture(GraphicBuffer* buffer); void resetCachedParams(); @@ -226,12 +207,11 @@ private: Caches& mCaches; std::unique_ptr<ColorSpaceConnector> mConnector; -}; // struct Texture +}; // struct Texture class AutoTexture { public: - explicit AutoTexture(Texture* texture) - : texture(texture) {} + explicit AutoTexture(Texture* texture) : texture(texture) {} ~AutoTexture() { if (texture && texture->cleanup) { texture->deleteTexture(); @@ -240,9 +220,9 @@ public: } Texture* const texture; -}; // class AutoTexture +}; // class AutoTexture -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android -#endif // ANDROID_HWUI_TEXTURE_H +#endif // ANDROID_HWUI_TEXTURE_H diff --git a/libs/hwui/TextureCache.cpp b/libs/hwui/TextureCache.cpp index 6fe3606a7576..9d365fb29ebe 100644 --- a/libs/hwui/TextureCache.cpp +++ b/libs/hwui/TextureCache.cpp @@ -19,12 +19,12 @@ #include <utils/Mutex.h> #include "Caches.h" +#include "DeviceInfo.h" +#include "Properties.h" #include "Texture.h" #include "TextureCache.h" -#include "Properties.h" -#include "utils/TraceUtils.h" #include "hwui/Bitmap.h" -#include "DeviceInfo.h" +#include "utils/TraceUtils.h" namespace android { namespace uirenderer { @@ -36,7 +36,7 @@ namespace uirenderer { TextureCache::TextureCache() : mCache(LruCache<uint32_t, Texture*>::kUnlimitedCapacity) , mSize(0) - , mMaxSize(DeviceInfo::multiplyByResolution(4 * 6)) // 6 screen-sized RGBA_8888 bitmaps + , mMaxSize(DeviceInfo::multiplyByResolution(4 * 6)) // 6 screen-sized RGBA_8888 bitmaps , mFlushRate(.4f) { mCache.setOnEntryRemovedListener(this); mMaxTextureSize = DeviceInfo::get()->maxTextureSize(); @@ -67,8 +67,8 @@ void TextureCache::operator()(uint32_t&, Texture*& texture) { // This will be called already locked if (texture) { mSize -= texture->bitmapSize; - TEXTURE_LOGD("TextureCache::callback: name, removed size, mSize = %d, %d, %d", - texture->id, texture->bitmapSize, mSize); + TEXTURE_LOGD("TextureCache::callback: name, removed size, mSize = %d, %d, %d", texture->id, + texture->bitmapSize, mSize); if (mDebugEnabled) { ALOGD("Texture deleted, size = %d", texture->bitmapSize); } @@ -92,19 +92,19 @@ void TextureCache::resetMarkInUse(void* ownerToken) { bool TextureCache::canMakeTextureFromBitmap(Bitmap* bitmap) { if (bitmap->width() > mMaxTextureSize || bitmap->height() > mMaxTextureSize) { - ALOGW("Bitmap too large to be uploaded into a texture (%dx%d, max=%dx%d)", - bitmap->width(), bitmap->height(), mMaxTextureSize, mMaxTextureSize); + ALOGW("Bitmap too large to be uploaded into a texture (%dx%d, max=%dx%d)", bitmap->width(), + bitmap->height(), mMaxTextureSize, mMaxTextureSize); return false; } return true; } Texture* TextureCache::createTexture(Bitmap* bitmap) { - Texture* texture = new Texture(Caches::getInstance()); - texture->bitmapSize = bitmap->rowBytes() * bitmap->height(); - texture->generation = bitmap->getGenerationID(); - texture->upload(*bitmap); - return texture; + Texture* texture = new Texture(Caches::getInstance()); + texture->bitmapSize = bitmap->rowBytes() * bitmap->height(); + texture->generation = bitmap->getGenerationID(); + texture->upload(*bitmap); + return texture; } // Returns a prepared Texture* that either is already in the cache or can fit @@ -113,9 +113,9 @@ Texture* TextureCache::getCachedTexture(Bitmap* bitmap) { if (bitmap->isHardware()) { auto textureIterator = mHardwareTextures.find(bitmap->getStableID()); if (textureIterator == mHardwareTextures.end()) { - Texture* texture = createTexture(bitmap); - mHardwareTextures.insert(std::make_pair(bitmap->getStableID(), - std::unique_ptr<Texture>(texture))); + Texture* texture = createTexture(bitmap); + mHardwareTextures.insert( + std::make_pair(bitmap->getStableID(), std::unique_ptr<Texture>(texture))); if (mDebugEnabled) { ALOGD("Texture created for hw bitmap size = %d", texture->bitmapSize); } @@ -147,7 +147,7 @@ Texture* TextureCache::getCachedTexture(Bitmap* bitmap) { texture = createTexture(bitmap); mSize += size; TEXTURE_LOGD("TextureCache::get: create texture(%p): name, size, mSize = %d, %d, %d", - bitmap, texture->id, size, mSize); + bitmap, texture->id, size, mSize); if (mDebugEnabled) { ALOGD("Texture created, size = %d", size); } @@ -201,7 +201,7 @@ bool TextureCache::destroyTexture(uint32_t pixelRefStableID) { void TextureCache::clear() { mCache.clear(); - for(auto& iter: mHardwareTextures) { + for (auto& iter : mHardwareTextures) { iter.second->deleteTexture(); } mHardwareTextures.clear(); @@ -223,5 +223,5 @@ void TextureCache::flush() { } } -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/TextureCache.h b/libs/hwui/TextureCache.h index 776ff8a03fd1..19e7bea99669 100644 --- a/libs/hwui/TextureCache.h +++ b/libs/hwui/TextureCache.h @@ -26,8 +26,8 @@ #include "Debug.h" -#include <vector> #include <unordered_map> +#include <vector> namespace android { @@ -43,9 +43,9 @@ class Texture; // Debug #if DEBUG_TEXTURES - #define TEXTURE_LOGD(...) ALOGD(__VA_ARGS__) +#define TEXTURE_LOGD(...) ALOGD(__VA_ARGS__) #else - #define TEXTURE_LOGD(...) +#define TEXTURE_LOGD(...) #endif /////////////////////////////////////////////////////////////////////////////// @@ -136,9 +136,9 @@ private: bool mDebugEnabled; std::unordered_map<uint32_t, std::unique_ptr<Texture>> mHardwareTextures; -}; // class TextureCache +}; // class TextureCache -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android -#endif // ANDROID_HWUI_TEXTURE_CACHE_H +#endif // ANDROID_HWUI_TEXTURE_CACHE_H diff --git a/libs/hwui/TreeInfo.h b/libs/hwui/TreeInfo.h index e39614b6a5ea..d7012697a91e 100644 --- a/libs/hwui/TreeInfo.h +++ b/libs/hwui/TreeInfo.h @@ -37,6 +37,7 @@ class RenderState; class ErrorHandler { public: virtual void onError(const std::string& message) = 0; + protected: ~ErrorHandler() {} }; @@ -48,6 +49,7 @@ public: // is finished it is possible that the node was "resurrected" and has // a non-zero parent count. virtual void onMaybeRemovedFromTree(RenderNode* node) = 0; + protected: virtual ~TreeObserver() {} }; @@ -55,6 +57,7 @@ protected: // This would be a struct, but we want to PREVENT_COPY_AND_ASSIGN class TreeInfo { PREVENT_COPY_AND_ASSIGN(TreeInfo); + public: enum TraversalMode { // The full monty - sync, push, run animators, etc... Used by DrawFrameTask @@ -68,10 +71,7 @@ public: }; TreeInfo(TraversalMode mode, renderthread::CanvasContext& canvasContext) - : mode(mode) - , prepareTextures(mode == MODE_FULL) - , canvasContext(canvasContext) - {} + : mode(mode), prepareTextures(mode == MODE_FULL), canvasContext(canvasContext) {} TraversalMode mode; // TODO: Remove this? Currently this is used to signal to stop preparing diff --git a/libs/hwui/UvMapper.h b/libs/hwui/UvMapper.h index 70428d2efd51..b495e3394bc9 100644 --- a/libs/hwui/UvMapper.h +++ b/libs/hwui/UvMapper.h @@ -33,15 +33,14 @@ public: * Using this constructor is equivalent to not using any mapping at all. * UV coordinates in the [0..1] range remain in the [0..1] range. */ - UvMapper(): mIdentity(true), mMinU(0.0f), mMaxU(1.0f), mMinV(0.0f), mMaxV(1.0f) { - } + UvMapper() : mIdentity(true), mMinU(0.0f), mMaxU(1.0f), mMinV(0.0f), mMaxV(1.0f) {} /** * Creates a new mapper with the specified ranges for U and V coordinates. * The parameter minU must be < maxU and minV must be < maxV. */ - UvMapper(float minU, float maxU, float minV, float maxV): - mMinU(minU), mMaxU(maxU), mMinV(minV), mMaxV(maxV) { + UvMapper(float minU, float maxU, float minV, float maxV) + : mMinU(minU), mMaxU(maxU), mMinV(minV), mMaxV(maxV) { checkIdentity(); } @@ -49,9 +48,7 @@ public: * Returns true if calling the map*() methods has no effect (that is, * texture coordinates remain in the 0..1 range.) */ - bool isIdentity() const { - return mIdentity; - } + bool isIdentity() const { return mIdentity; } /** * Changes the U and V mapping ranges. @@ -127,7 +124,7 @@ private: float mMaxV; }; -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android -#endif // ANDROID_HWUI_UV_MAPPER_H +#endif // ANDROID_HWUI_UV_MAPPER_H diff --git a/libs/hwui/Vector.h b/libs/hwui/Vector.h index 6367dbd7b660..d2c15ad872a5 100644 --- a/libs/hwui/Vector.h +++ b/libs/hwui/Vector.h @@ -32,13 +32,9 @@ struct Vector2 { float x; float y; - float lengthSquared() const { - return x * x + y * y; - } + float lengthSquared() const { return x * x + y * y; } - float length() const { - return sqrt(x * x + y * y); - } + float length() const { return sqrt(x * x + y * y); } void operator+=(const Vector2& v) { x += v.x; @@ -70,21 +66,13 @@ struct Vector2 { y *= s; } - Vector2 operator+(const Vector2& v) const { - return (Vector2){x + v.x, y + v.y}; - } + Vector2 operator+(const Vector2& v) const { return (Vector2){x + v.x, y + v.y}; } - Vector2 operator-(const Vector2& v) const { - return (Vector2){x - v.x, y - v.y}; - } + Vector2 operator-(const Vector2& v) const { return (Vector2){x - v.x, y - v.y}; } - Vector2 operator/(float s) const { - return (Vector2){x / s, y / s}; - } + Vector2 operator/(float s) const { return (Vector2){x / s, y / s}; } - Vector2 operator*(float s) const { - return (Vector2){x * s, y * s}; - } + Vector2 operator*(float s) const { return (Vector2){x * s, y * s}; } void normalize() { float s = 1.0f / length(); @@ -98,18 +86,12 @@ struct Vector2 { return v; } - float dot(const Vector2& v) const { - return x * v.x + y * v.y; - } + float dot(const Vector2& v) const { return x * v.x + y * v.y; } - float cross(const Vector2& v) const { - return x * v.y - y * v.x; - } + float cross(const Vector2& v) const { return x * v.y - y * v.x; } - void dump() { - ALOGD("Vector2[%.2f, %.2f]", x, y); - } -}; // class Vector2 + void dump() { ALOGD("Vector2[%.2f, %.2f]", x, y); } +}; // class Vector2 // MUST BE A POD - this means no ctor or dtor! class Vector3 { @@ -118,29 +100,20 @@ public: float y; float z; - Vector3 operator+(const Vector3& v) const { - return (Vector3){x + v.x, y + v.y, z + v.z}; - } + Vector3 operator+(const Vector3& v) const { return (Vector3){x + v.x, y + v.y, z + v.z}; } - Vector3 operator-(const Vector3& v) const { - return (Vector3){x - v.x, y - v.y, z - v.z}; - } + Vector3 operator-(const Vector3& v) const { return (Vector3){x - v.x, y - v.y, z - v.z}; } - Vector3 operator/(float s) const { - return (Vector3){x / s, y / s, z / s}; - } - - Vector3 operator*(float s) const { - return (Vector3){x * s, y * s, z * s}; - } + Vector3 operator/(float s) const { return (Vector3){x / s, y / s, z / s}; } + Vector3 operator*(float s) const { return (Vector3){x * s, y * s, z * s}; } void dump(const char* label = "Vector3") const { ALOGD("%s[%.2f, %.2f, %.2f]", label, x, y, z); } }; -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android -#endif // ANDROID_HWUI_VECTOR_H +#endif // ANDROID_HWUI_VECTOR_H diff --git a/libs/hwui/VectorDrawable.cpp b/libs/hwui/VectorDrawable.cpp index ca179c9a25d2..f118e8d0a53e 100644 --- a/libs/hwui/VectorDrawable.cpp +++ b/libs/hwui/VectorDrawable.cpp @@ -16,11 +16,11 @@ #include "VectorDrawable.h" +#include <utils/Log.h> #include "PathParser.h" #include "SkColorFilter.h" #include "SkImageInfo.h" #include "SkShader.h" -#include <utils/Log.h> #include "utils/Macros.h" #include "utils/TraceUtils.h" #include "utils/VectorDrawableUtils.h" @@ -79,7 +79,7 @@ FullPath::FullPath(const FullPath& path) : Path(path) { } static void applyTrim(SkPath* outPath, const SkPath& inPath, float trimPathStart, float trimPathEnd, - float trimPathOffset) { + float trimPathOffset) { if (trimPathStart == 0.0f && trimPathEnd == 1.0f) { *outPath = inPath; return; @@ -109,25 +109,25 @@ const SkPath& FullPath::getUpdatedPath(bool useStagingData, SkPath* tempStagingP return mTrimmedSkPath; } Path::getUpdatedPath(useStagingData, tempStagingPath); - SkPath *outPath; + SkPath* outPath; if (useStagingData) { SkPath inPath = *tempStagingPath; applyTrim(tempStagingPath, inPath, mStagingProperties.getTrimPathStart(), - mStagingProperties.getTrimPathEnd(), mStagingProperties.getTrimPathOffset()); + mStagingProperties.getTrimPathEnd(), mStagingProperties.getTrimPathOffset()); outPath = tempStagingPath; } else { if (mProperties.getTrimPathStart() != 0.0f || mProperties.getTrimPathEnd() != 1.0f) { mProperties.mTrimDirty = false; applyTrim(&mTrimmedSkPath, mSkPath, mProperties.getTrimPathStart(), - mProperties.getTrimPathEnd(), mProperties.getTrimPathOffset()); + mProperties.getTrimPathEnd(), mProperties.getTrimPathOffset()); outPath = &mTrimmedSkPath; } else { outPath = &mSkPath; } } const FullPathProperties& properties = useStagingData ? mStagingProperties : mProperties; - bool setFillPath = properties.getFillGradient() != nullptr - || properties.getFillColor() != SK_ColorTRANSPARENT; + bool setFillPath = properties.getFillGradient() != nullptr || + properties.getFillColor() != SK_ColorTRANSPARENT; if (setFillPath) { SkPath::FillType ft = static_cast<SkPath::FillType>(properties.getFillType()); outPath->setFillType(ft); @@ -138,11 +138,10 @@ const SkPath& FullPath::getUpdatedPath(bool useStagingData, SkPath* tempStagingP void FullPath::dump() { Path::dump(); ALOGD("stroke width, color, alpha: %f, %d, %f, fill color, alpha: %d, %f", - mProperties.getStrokeWidth(), mProperties.getStrokeColor(), mProperties.getStrokeAlpha(), - mProperties.getFillColor(), mProperties.getFillAlpha()); + mProperties.getStrokeWidth(), mProperties.getStrokeColor(), mProperties.getStrokeAlpha(), + mProperties.getFillColor(), mProperties.getFillAlpha()); } - inline SkColor applyAlpha(SkColor color, float alpha) { int alphaBytes = SkColorGetA(color); return SkColorSetA(color, alphaBytes * alpha); @@ -167,7 +166,7 @@ void FullPath::draw(SkCanvas* outCanvas, bool useStagingData) { if (needsFill) { paint.setStyle(SkPaint::Style::kFill_Style); - paint.setAntiAlias(true); + paint.setAntiAlias(mAntiAlias); outCanvas->drawPath(renderPath, paint); } @@ -183,7 +182,7 @@ void FullPath::draw(SkCanvas* outCanvas, bool useStagingData) { } if (needsStroke) { paint.setStyle(SkPaint::Style::kStroke_Style); - paint.setAntiAlias(true); + paint.setAntiAlias(mAntiAlias); paint.setStrokeJoin(SkPaint::Join(properties.getStrokeLineJoin())); paint.setStrokeCap(SkPaint::Cap(properties.getStrokeLineCap())); paint.setStrokeMiter(properties.getStrokeMiterLimit()); @@ -213,7 +212,7 @@ bool FullPath::FullPathProperties::copyProperties(int8_t* outProperties, int len int propertyDataSize = sizeof(FullPathProperties::PrimitiveFields); if (length != propertyDataSize) { LOG_ALWAYS_FATAL("Properties needs exactly %d bytes, a byte array of size %d is provided", - propertyDataSize, length); + propertyDataSize, length); return false; } @@ -229,35 +228,37 @@ void FullPath::FullPathProperties::setColorPropertyValue(int propertyId, int32_t } else if (currentProperty == Property::fillColor) { setFillColor(value); } else { - LOG_ALWAYS_FATAL("Error setting color property on FullPath: No valid property" - " with id: %d", propertyId); + LOG_ALWAYS_FATAL( + "Error setting color property on FullPath: No valid property" + " with id: %d", + propertyId); } } void FullPath::FullPathProperties::setPropertyValue(int propertyId, float value) { Property property = static_cast<Property>(propertyId); switch (property) { - case Property::strokeWidth: - setStrokeWidth(value); - break; - case Property::strokeAlpha: - setStrokeAlpha(value); - break; - case Property::fillAlpha: - setFillAlpha(value); - break; - case Property::trimPathStart: - setTrimPathStart(value); - break; - case Property::trimPathEnd: - setTrimPathEnd(value); - break; - case Property::trimPathOffset: - setTrimPathOffset(value); - break; - default: - LOG_ALWAYS_FATAL("Invalid property id: %d for animation", propertyId); - break; + case Property::strokeWidth: + setStrokeWidth(value); + break; + case Property::strokeAlpha: + setStrokeAlpha(value); + break; + case Property::fillAlpha: + setFillAlpha(value); + break; + case Property::trimPathStart: + setTrimPathStart(value); + break; + case Property::trimPathEnd: + setTrimPathEnd(value); + break; + case Property::trimPathOffset: + setTrimPathOffset(value); + break; + default: + LOG_ALWAYS_FATAL("Invalid property id: %d for animation", propertyId); + break; } } @@ -288,7 +289,7 @@ void Group::draw(SkCanvas* outCanvas, bool useStagingData) { void Group::dump() { ALOGD("Group %s has %zu children: ", mName.c_str(), mChildren.size()); ALOGD("Group translateX, Y : %f, %f, scaleX, Y: %f, %f", mProperties.getTranslateX(), - mProperties.getTranslateY(), mProperties.getScaleX(), mProperties.getScaleY()); + mProperties.getTranslateY(), mProperties.getScaleX(), mProperties.getScaleY()); for (size_t i = 0; i < mChildren.size(); i++) { mChildren[i]->dump(); } @@ -315,7 +316,7 @@ void Group::getLocalMatrix(SkMatrix* outMatrix, const GroupProperties& propertie outMatrix->postScale(properties.getScaleX(), properties.getScaleY()); outMatrix->postRotate(properties.getRotation(), 0, 0); outMatrix->postTranslate(properties.getTranslateX() + properties.getPivotX(), - properties.getTranslateY() + properties.getPivotY()); + properties.getTranslateY() + properties.getPivotY()); } void Group::addChild(Node* child) { @@ -329,7 +330,7 @@ bool Group::GroupProperties::copyProperties(float* outProperties, int length) co int propertyCount = static_cast<int>(Property::count); if (length != propertyCount) { LOG_ALWAYS_FATAL("Properties needs exactly %d bytes, a byte array of size %d is provided", - propertyCount, length); + propertyCount, length); return false; } @@ -343,23 +344,23 @@ bool Group::GroupProperties::copyProperties(float* outProperties, int length) co float Group::GroupProperties::getPropertyValue(int propertyId) const { Property currentProperty = static_cast<Property>(propertyId); switch (currentProperty) { - case Property::rotate: - return getRotation(); - case Property::pivotX: - return getPivotX(); - case Property::pivotY: - return getPivotY(); - case Property::scaleX: - return getScaleX(); - case Property::scaleY: - return getScaleY(); - case Property::translateX: - return getTranslateX(); - case Property::translateY: - return getTranslateY(); - default: - LOG_ALWAYS_FATAL("Invalid property index: %d", propertyId); - return 0; + case Property::rotate: + return getRotation(); + case Property::pivotX: + return getPivotX(); + case Property::pivotY: + return getPivotY(); + case Property::scaleX: + return getScaleX(); + case Property::scaleY: + return getScaleY(); + case Property::translateX: + return getTranslateX(); + case Property::translateY: + return getTranslateY(); + default: + LOG_ALWAYS_FATAL("Invalid property index: %d", propertyId); + return 0; } } @@ -367,29 +368,29 @@ float Group::GroupProperties::getPropertyValue(int propertyId) const { void Group::GroupProperties::setPropertyValue(int propertyId, float value) { Property currentProperty = static_cast<Property>(propertyId); switch (currentProperty) { - case Property::rotate: - setRotation(value); - break; - case Property::pivotX: - setPivotX(value); - break; - case Property::pivotY: - setPivotY(value); - break; - case Property::scaleX: - setScaleX(value); - break; - case Property::scaleY: - setScaleY(value); - break; - case Property::translateX: - setTranslateX(value); - break; - case Property::translateY: - setTranslateY(value); - break; - default: - LOG_ALWAYS_FATAL("Invalid property index: %d", propertyId); + case Property::rotate: + setRotation(value); + break; + case Property::pivotX: + setPivotX(value); + break; + case Property::pivotY: + setPivotY(value); + break; + case Property::scaleX: + setScaleX(value); + break; + case Property::scaleY: + setScaleY(value); + break; + case Property::translateX: + setTranslateX(value); + break; + case Property::translateY: + setTranslateY(value); + break; + default: + LOG_ALWAYS_FATAL("Invalid property index: %d", propertyId); } } @@ -401,8 +402,8 @@ bool Group::GroupProperties::isValidProperty(int propertyId) { return propertyId >= 0 && propertyId < static_cast<int>(Property::count); } -int Tree::draw(Canvas* outCanvas, SkColorFilter* colorFilter, - const SkRect& bounds, bool needsMirroring, bool canReuseCache) { +int Tree::draw(Canvas* outCanvas, SkColorFilter* colorFilter, const SkRect& bounds, + bool needsMirroring, bool canReuseCache) { // The imageView can scale the canvas in different ways, in order to // avoid blurry scaling, we have to draw into a bitmap with exact pixel // size first. This bitmap size is determined by the bounds and the @@ -417,8 +418,8 @@ int Tree::draw(Canvas* outCanvas, SkColorFilter* colorFilter, canvasScaleX = fabs(canvasMatrix.getScaleX()); canvasScaleY = fabs(canvasMatrix.getScaleY()); } - int scaledWidth = (int) (bounds.width() * canvasScaleX); - int scaledHeight = (int) (bounds.height() * canvasScaleY); + int scaledWidth = (int)(bounds.width() * canvasScaleX); + int scaledHeight = (int)(bounds.height() * canvasScaleY); scaledWidth = std::min(Tree::MAX_CACHED_BITMAP_SIZE, scaledWidth); scaledHeight = std::min(Tree::MAX_CACHED_BITMAP_SIZE, scaledHeight); @@ -449,8 +450,8 @@ int Tree::draw(Canvas* outCanvas, SkColorFilter* colorFilter, } void Tree::drawStaging(Canvas* outCanvas) { - bool redrawNeeded = allocateBitmapIfNeeded(mStagingCache, - mStagingProperties.getScaledWidth(), mStagingProperties.getScaledHeight()); + bool redrawNeeded = allocateBitmapIfNeeded(mStagingCache, mStagingProperties.getScaledWidth(), + mStagingProperties.getScaledHeight()); // draw bitmap cache if (redrawNeeded || mStagingCache.dirty) { updateBitmapCache(*mStagingCache.bitmap, true); @@ -459,10 +460,11 @@ void Tree::drawStaging(Canvas* outCanvas) { SkPaint tmpPaint; SkPaint* paint = updatePaint(&tmpPaint, &mStagingProperties); - outCanvas->drawBitmap(*mStagingCache.bitmap, 0, 0, - mStagingCache.bitmap->width(), mStagingCache.bitmap->height(), - mStagingProperties.getBounds().left(), mStagingProperties.getBounds().top(), - mStagingProperties.getBounds().right(), mStagingProperties.getBounds().bottom(), paint); + outCanvas->drawBitmap(*mStagingCache.bitmap, 0, 0, mStagingCache.bitmap->width(), + mStagingCache.bitmap->height(), mStagingProperties.getBounds().left(), + mStagingProperties.getBounds().top(), + mStagingProperties.getBounds().right(), + mStagingProperties.getBounds().bottom(), paint); } SkPaint* Tree::getPaint() { @@ -484,7 +486,7 @@ SkPaint* Tree::updatePaint(SkPaint* outPaint, TreeProperties* prop) { Bitmap& Tree::getBitmapUpdateIfDirty() { bool redrawNeeded = allocateBitmapIfNeeded(mCache, mProperties.getScaledWidth(), - mProperties.getScaledHeight()); + mProperties.getScaledHeight()); if (redrawNeeded || mCache.dirty) { updateBitmapCache(*mCache.bitmap, false); mCache.dirty = false; @@ -495,8 +497,8 @@ Bitmap& Tree::getBitmapUpdateIfDirty() { void Tree::updateCache(sp<skiapipeline::VectorDrawableAtlas>& atlas, GrContext* context) { SkRect dst; sk_sp<SkSurface> surface = mCache.getSurface(&dst); - bool canReuseSurface = surface && dst.width() >= mProperties.getScaledWidth() - && dst.height() >= mProperties.getScaledHeight(); + bool canReuseSurface = surface && dst.width() >= mProperties.getScaledWidth() && + dst.height() >= mProperties.getScaledHeight(); if (!canReuseSurface) { int scaledWidth = SkScalarCeilToInt(mProperties.getScaledWidth()); int scaledHeight = SkScalarCeilToInt(mProperties.getScaledHeight()); @@ -506,33 +508,27 @@ void Tree::updateCache(sp<skiapipeline::VectorDrawableAtlas>& atlas, GrContext* surface = atlasEntry.surface; mCache.setAtlas(atlas, atlasEntry.key); } else { - //don't draw, if we failed to allocate an offscreen buffer + // don't draw, if we failed to allocate an offscreen buffer mCache.clear(); surface.reset(); } } if (!canReuseSurface || mCache.dirty) { - draw(surface.get(), dst); + if (surface) { + Bitmap& bitmap = getBitmapUpdateIfDirty(); + SkBitmap skiaBitmap; + bitmap.getSkBitmap(&skiaBitmap); + if (!surface->getCanvas()->writePixels(skiaBitmap, dst.fLeft, dst.fTop)) { + ALOGD("VectorDrawable caching failed to efficiently upload"); + surface->getCanvas()->drawBitmap(skiaBitmap, dst.fLeft, dst.fTop); + } + } mCache.dirty = false; } } -void Tree::draw(SkSurface* surface, const SkRect& dst) { - if (surface) { - SkCanvas* canvas = surface->getCanvas(); - float scaleX = dst.width() / mProperties.getViewportWidth(); - float scaleY = dst.height() / mProperties.getViewportHeight(); - SkAutoCanvasRestore acr(canvas, true); - canvas->translate(dst.fLeft, dst.fTop); - canvas->clipRect(SkRect::MakeWH(dst.width(), dst.height())); - canvas->clear(SK_ColorTRANSPARENT); - canvas->scale(scaleX, scaleY); - mRootNode->draw(canvas, false); - } -} - void Tree::Cache::setAtlas(sp<skiapipeline::VectorDrawableAtlas> newAtlas, - skiapipeline::AtlasKey newAtlasKey) { + skiapipeline::AtlasKey newAtlasKey) { LOG_ALWAYS_FATAL_IF(newAtlasKey == INVALID_ATLAS_KEY); clear(); mAtlas = newAtlas; @@ -566,27 +562,22 @@ void Tree::draw(SkCanvas* canvas) { sk_sp<SkSurface> vdSurface = mCache.getSurface(&src); if (vdSurface) { canvas->drawImageRect(vdSurface->makeImageSnapshot().get(), src, - mutateProperties()->getBounds(), getPaint()); + mutateProperties()->getBounds(), getPaint(), + SkCanvas::kFast_SrcRectConstraint); } else { // Handle the case when VectorDrawableAtlas has been destroyed, because of memory pressure. // We render the VD into a temporary standalone buffer and mark the frame as dirty. Next // frame will be cached into the atlas. + Bitmap& bitmap = getBitmapUpdateIfDirty(); + SkBitmap skiaBitmap; + bitmap.getSkBitmap(&skiaBitmap); + int scaledWidth = SkScalarCeilToInt(mProperties.getScaledWidth()); int scaledHeight = SkScalarCeilToInt(mProperties.getScaledHeight()); - SkRect src = SkRect::MakeWH(scaledWidth, scaledHeight); -#ifndef ANDROID_ENABLE_LINEAR_BLENDING - sk_sp<SkColorSpace> colorSpace = nullptr; -#else - sk_sp<SkColorSpace> colorSpace = SkColorSpace::MakeSRGB(); -#endif - SkImageInfo info = SkImageInfo::MakeN32(scaledWidth, scaledHeight, kPremul_SkAlphaType, - colorSpace); - sk_sp<SkSurface> surface = SkSurface::MakeRenderTarget(canvas->getGrContext(), - SkBudgeted::kYes, info); - draw(surface.get(), src); + canvas->drawBitmapRect(skiaBitmap, SkRect::MakeWH(scaledWidth, scaledHeight), + mutateProperties()->getBounds(), getPaint(), + SkCanvas::kFast_SrcRectConstraint); mCache.clear(); - canvas->drawImageRect(surface->makeImageSnapshot().get(), mutateProperties()->getBounds(), - getPaint()); markDirty(); } } @@ -599,10 +590,10 @@ void Tree::updateBitmapCache(Bitmap& bitmap, bool useStagingData) { ATRACE_FORMAT("VectorDrawable repaint %dx%d", cacheWidth, cacheHeight); outCache.eraseColor(SK_ColorTRANSPARENT); SkCanvas outCanvas(outCache); - float viewportWidth = useStagingData ? - mStagingProperties.getViewportWidth() : mProperties.getViewportWidth(); - float viewportHeight = useStagingData ? - mStagingProperties.getViewportHeight() : mProperties.getViewportHeight(); + float viewportWidth = + useStagingData ? mStagingProperties.getViewportWidth() : mProperties.getViewportWidth(); + float viewportHeight = useStagingData ? mStagingProperties.getViewportHeight() + : mProperties.getViewportHeight(); float scaleX = cacheWidth / viewportWidth; float scaleY = cacheHeight / viewportHeight; outCanvas.scale(scaleX, scaleY); @@ -635,7 +626,7 @@ void Tree::onPropertyChanged(TreeProperties* prop) { } } -}; // namespace VectorDrawable +}; // namespace VectorDrawable -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/VectorDrawable.h b/libs/hwui/VectorDrawable.h index efbb695a14dd..d9cf8ab7eedf 100644 --- a/libs/hwui/VectorDrawable.h +++ b/libs/hwui/VectorDrawable.h @@ -17,15 +17,15 @@ #ifndef ANDROID_HWUI_VPATH_H #define ANDROID_HWUI_VPATH_H -#include "hwui/Canvas.h" +#include "DisplayList.h" #include "hwui/Bitmap.h" +#include "hwui/Canvas.h" #include "renderthread/CacheManager.h" -#include "DisplayList.h" #include <SkBitmap.h> +#include <SkCanvas.h> #include <SkColor.h> #include <SkColorFilter.h> -#include <SkCanvas.h> #include <SkMatrix.h> #include <SkPaint.h> #include <SkPath.h> @@ -36,25 +36,35 @@ #include <cutils/compiler.h> #include <stddef.h> -#include <vector> #include <string> +#include <vector> namespace android { namespace uirenderer { // Debug #if DEBUG_VECTOR_DRAWABLE - #define VECTOR_DRAWABLE_LOGD(...) ALOGD(__VA_ARGS__) +#define VECTOR_DRAWABLE_LOGD(...) ALOGD(__VA_ARGS__) #else - #define VECTOR_DRAWABLE_LOGD(...) +#define VECTOR_DRAWABLE_LOGD(...) #endif namespace VectorDrawable { -#define VD_SET_PRIMITIVE_FIELD_WITH_FLAG(field, value, flag) (VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(field, (value)) ? ((flag) = true, true) : false) +#define VD_SET_PRIMITIVE_FIELD_WITH_FLAG(field, value, flag) \ + (VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(field, (value)) ? ((flag) = true, true) : false) #define VD_SET_PROP(field, value) ((value) != (field) ? ((field) = (value), true) : false) -#define VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(field, value) ({ bool retVal = VD_SET_PROP((mPrimitiveFields.field), (value));\ - onPropertyChanged(); retVal;}) -#define UPDATE_SKPROP(field, value) ({bool retVal = ((field) != (value)); if ((field) != (value)) SkRefCnt_SafeAssign((field), (value)); retVal;}) +#define VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(field, value) \ + ({ \ + bool retVal = VD_SET_PROP((mPrimitiveFields.field), (value)); \ + onPropertyChanged(); \ + retVal; \ + }) +#define UPDATE_SKPROP(field, value) \ + ({ \ + bool retVal = ((field) != (value)); \ + if ((field) != (value)) SkRefCnt_SafeAssign((field), (value)); \ + retVal; \ + }) /* A VectorDrawable is composed of a tree of nodes. * Each node can be a group node, or a path. @@ -85,12 +95,9 @@ class PropertyChangedListener { public: PropertyChangedListener(bool* dirty, bool* stagingDirty) : mDirty(dirty), mStagingDirty(stagingDirty) {} - void onPropertyChanged() { - *mDirty = true; - } - void onStagingPropertyChanged() { - *mStagingDirty = true; - } + void onPropertyChanged() { *mDirty = true; } + void onStagingPropertyChanged() { *mStagingDirty = true; } + private: bool* mDirty; bool* mStagingDirty; @@ -101,27 +108,24 @@ public: class Properties { public: explicit Properties(Node* node) : mNode(node) {} - inline void onPropertyChanged() { - mNode->onPropertyChanged(this); - } + inline void onPropertyChanged() { mNode->onPropertyChanged(this); } + private: Node* mNode; }; - Node(const Node& node) { - mName = node.mName; - } + Node(const Node& node) { mName = node.mName; } Node() {} virtual void draw(SkCanvas* outCanvas, bool useStagingData) = 0; virtual void dump() = 0; - void setName(const char* name) { - mName = name; - } + void setName(const char* name) { mName = name; } virtual void setPropertyChangedListener(PropertyChangedListener* listener) { mPropertyChangedListener = listener; } virtual void onPropertyChanged(Properties* properties) = 0; - virtual ~Node(){} + virtual ~Node() {} virtual void syncProperties() = 0; + virtual void setAntiAlias(bool aa) = 0; + protected: std::string mName; PropertyChangedListener* mPropertyChangedListener = nullptr; @@ -134,8 +138,7 @@ public: std::vector<size_t> verbSizes; std::vector<float> points; bool operator==(const Data& data) const { - return verbs == data.verbs && verbSizes == data.verbSizes - && points == data.points; + return verbs == data.verbs && verbSizes == data.verbSizes && points == data.points; } }; @@ -156,11 +159,9 @@ public: } mData = data; onPropertyChanged(); - - } - const Data& getData() const { - return mData; } + const Data& getData() const { return mData; } + private: Data mData; }; @@ -177,7 +178,7 @@ public: if (mPropertyChangedListener) { mPropertyChangedListener->onStagingPropertyChanged(); } - } else if (prop == &mProperties){ + } else if (prop == &mProperties) { mSkPathDirty = true; if (mPropertyChangedListener) { mPropertyChangedListener->onPropertyChanged(); @@ -203,7 +204,7 @@ private: bool mStagingPropertiesDirty = true; }; -class ANDROID_API FullPath: public Path { +class ANDROID_API FullPath : public Path { public: class FullPathProperties : public Properties { public: @@ -234,87 +235,59 @@ public: onPropertyChanged(); } void setFillGradient(SkShader* gradient) { - if(UPDATE_SKPROP(fillGradient, gradient)) { + if (UPDATE_SKPROP(fillGradient, gradient)) { onPropertyChanged(); } } void setStrokeGradient(SkShader* gradient) { - if(UPDATE_SKPROP(strokeGradient, gradient)) { + if (UPDATE_SKPROP(strokeGradient, gradient)) { onPropertyChanged(); } } - SkShader* getFillGradient() const { - return fillGradient; - } - SkShader* getStrokeGradient() const { - return strokeGradient; - } - float getStrokeWidth() const{ - return mPrimitiveFields.strokeWidth; - } + SkShader* getFillGradient() const { return fillGradient; } + SkShader* getStrokeGradient() const { return strokeGradient; } + float getStrokeWidth() const { return mPrimitiveFields.strokeWidth; } void setStrokeWidth(float strokeWidth) { VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(strokeWidth, strokeWidth); } - SkColor getStrokeColor() const{ - return mPrimitiveFields.strokeColor; - } + SkColor getStrokeColor() const { return mPrimitiveFields.strokeColor; } void setStrokeColor(SkColor strokeColor) { VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(strokeColor, strokeColor); } - float getStrokeAlpha() const{ - return mPrimitiveFields.strokeAlpha; - } + float getStrokeAlpha() const { return mPrimitiveFields.strokeAlpha; } void setStrokeAlpha(float strokeAlpha) { VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(strokeAlpha, strokeAlpha); } - SkColor getFillColor() const { - return mPrimitiveFields.fillColor; - } + SkColor getFillColor() const { return mPrimitiveFields.fillColor; } void setFillColor(SkColor fillColor) { VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(fillColor, fillColor); } - float getFillAlpha() const{ - return mPrimitiveFields.fillAlpha; - } + float getFillAlpha() const { return mPrimitiveFields.fillAlpha; } void setFillAlpha(float fillAlpha) { VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(fillAlpha, fillAlpha); } - float getTrimPathStart() const{ - return mPrimitiveFields.trimPathStart; - } + float getTrimPathStart() const { return mPrimitiveFields.trimPathStart; } void setTrimPathStart(float trimPathStart) { VD_SET_PRIMITIVE_FIELD_WITH_FLAG(trimPathStart, trimPathStart, mTrimDirty); } - float getTrimPathEnd() const{ - return mPrimitiveFields.trimPathEnd; - } + float getTrimPathEnd() const { return mPrimitiveFields.trimPathEnd; } void setTrimPathEnd(float trimPathEnd) { VD_SET_PRIMITIVE_FIELD_WITH_FLAG(trimPathEnd, trimPathEnd, mTrimDirty); } - float getTrimPathOffset() const{ - return mPrimitiveFields.trimPathOffset; - } + float getTrimPathOffset() const { return mPrimitiveFields.trimPathOffset; } void setTrimPathOffset(float trimPathOffset) { VD_SET_PRIMITIVE_FIELD_WITH_FLAG(trimPathOffset, trimPathOffset, mTrimDirty); } - float getStrokeMiterLimit() const { - return mPrimitiveFields.strokeMiterLimit; - } - float getStrokeLineCap() const { - return mPrimitiveFields.strokeLineCap; - } - float getStrokeLineJoin() const { - return mPrimitiveFields.strokeLineJoin; - } - float getFillType() const { - return mPrimitiveFields.fillType; - } + float getStrokeMiterLimit() const { return mPrimitiveFields.strokeMiterLimit; } + float getStrokeLineCap() const { return mPrimitiveFields.strokeLineCap; } + float getStrokeLineJoin() const { return mPrimitiveFields.strokeLineJoin; } + float getFillType() const { return mPrimitiveFields.fillType; } bool copyProperties(int8_t* outProperties, int length) const; void updateProperties(float strokeWidth, SkColor strokeColor, float strokeAlpha, - SkColor fillColor, float fillAlpha, float trimPathStart, float trimPathEnd, - float trimPathOffset, float strokeMiterLimit, int strokeLineCap, int strokeLineJoin, - int fillType) { + SkColor fillColor, float fillAlpha, float trimPathStart, + float trimPathEnd, float trimPathOffset, float strokeMiterLimit, + int strokeLineCap, int strokeLineJoin, int fillType) { mPrimitiveFields.strokeWidth = strokeWidth; mPrimitiveFields.strokeColor = strokeColor; mPrimitiveFields.strokeAlpha = strokeAlpha; @@ -334,6 +307,7 @@ public: void setColorPropertyValue(int propertyId, int32_t value); void setPropertyValue(int propertyId, float value); bool mTrimDirty; + private: enum class Property { strokeWidth = 0, @@ -356,7 +330,7 @@ public: }; // Called from UI thread - FullPath(const FullPath& path); // for cloning + FullPath(const FullPath& path); // for cloning FullPath(const char* path, size_t strLength) : Path(path, strLength) {} FullPath() : Path() {} void draw(SkCanvas* outCanvas, bool useStagingData) override; @@ -381,29 +355,32 @@ public: } } } + virtual void setAntiAlias(bool aa) { mAntiAlias = aa; } protected: const SkPath& getUpdatedPath(bool useStagingData, SkPath* tempStagingPath) override; -private: +private: FullPathProperties mProperties = FullPathProperties(this); FullPathProperties mStagingProperties = FullPathProperties(this); bool mStagingPropertiesDirty = true; // Intermediate data for drawing, render thread only SkPath mTrimmedSkPath; - + // Default to use AntiAlias + bool mAntiAlias = true; }; -class ANDROID_API ClipPath: public Path { +class ANDROID_API ClipPath : public Path { public: ClipPath(const ClipPath& path) : Path(path) {} ClipPath(const char* path, size_t strLength) : Path(path, strLength) {} ClipPath() : Path() {} void draw(SkCanvas* outCanvas, bool useStagingData) override; + virtual void setAntiAlias(bool aa) {} }; -class ANDROID_API Group: public Node { +class ANDROID_API Group : public Node { public: class GroupProperties : public Properties { public: @@ -421,50 +398,26 @@ public: mPrimitiveFields = prop.mPrimitiveFields; onPropertyChanged(); } - float getRotation() const { - return mPrimitiveFields.rotate; - } - void setRotation(float rotation) { - VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(rotate, rotation); - } - float getPivotX() const { - return mPrimitiveFields.pivotX; - } - void setPivotX(float pivotX) { - VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(pivotX, pivotX); - } - float getPivotY() const { - return mPrimitiveFields.pivotY; - } - void setPivotY(float pivotY) { - VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(pivotY, pivotY); - } - float getScaleX() const { - return mPrimitiveFields.scaleX; - } - void setScaleX(float scaleX) { - VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(scaleX, scaleX); - } - float getScaleY() const { - return mPrimitiveFields.scaleY; - } - void setScaleY(float scaleY) { - VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(scaleY, scaleY); - } - float getTranslateX() const { - return mPrimitiveFields.translateX; - } + float getRotation() const { return mPrimitiveFields.rotate; } + void setRotation(float rotation) { VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(rotate, rotation); } + float getPivotX() const { return mPrimitiveFields.pivotX; } + void setPivotX(float pivotX) { VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(pivotX, pivotX); } + float getPivotY() const { return mPrimitiveFields.pivotY; } + void setPivotY(float pivotY) { VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(pivotY, pivotY); } + float getScaleX() const { return mPrimitiveFields.scaleX; } + void setScaleX(float scaleX) { VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(scaleX, scaleX); } + float getScaleY() const { return mPrimitiveFields.scaleY; } + void setScaleY(float scaleY) { VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(scaleY, scaleY); } + float getTranslateX() const { return mPrimitiveFields.translateX; } void setTranslateX(float translateX) { VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(translateX, translateX); } - float getTranslateY() const { - return mPrimitiveFields.translateY; - } + float getTranslateY() const { return mPrimitiveFields.translateY; } void setTranslateY(float translateY) { VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(translateY, translateY); } - void updateProperties(float rotate, float pivotX, float pivotY, - float scaleX, float scaleY, float translateX, float translateY) { + void updateProperties(float rotate, float pivotX, float pivotY, float scaleX, float scaleY, + float translateX, float translateY) { mPrimitiveFields.rotate = rotate; mPrimitiveFields.pivotX = pivotX; mPrimitiveFields.pivotY = pivotY; @@ -478,6 +431,7 @@ public: float getPropertyValue(int propertyId) const; bool copyProperties(float* outProperties, int length) const; static bool isValidProperty(int propertyId); + private: enum class Property { rotate = 0, @@ -498,7 +452,7 @@ public: virtual void setPropertyChangedListener(PropertyChangedListener* listener) override { Node::setPropertyChangedListener(listener); for (auto& child : mChildren) { - child->setPropertyChangedListener(listener); + child->setPropertyChangedListener(listener); } } virtual void syncProperties() override; @@ -527,11 +481,17 @@ public: } } + virtual void setAntiAlias(bool aa) { + for (auto& child : mChildren) { + child->setAntiAlias(aa); + } + } + private: GroupProperties mProperties = GroupProperties(this); GroupProperties mStagingProperties = GroupProperties(this); bool mStagingPropertiesDirty = true; - std::vector< std::unique_ptr<Node> > mChildren; + std::vector<std::unique_ptr<Node> > mChildren; }; class ANDROID_API Tree : public VirtualLightRefBase { @@ -547,17 +507,25 @@ public: } // Draws the VD onto a bitmap cache, then the bitmap cache will be rendered onto the input // canvas. Returns the number of pixels needed for the bitmap cache. - int draw(Canvas* outCanvas, SkColorFilter* colorFilter, - const SkRect& bounds, bool needsMirroring, bool canReuseCache); + int draw(Canvas* outCanvas, SkColorFilter* colorFilter, const SkRect& bounds, + bool needsMirroring, bool canReuseCache); void drawStaging(Canvas* canvas); Bitmap& getBitmapUpdateIfDirty(); - void setAllowCaching(bool allowCaching) { - mAllowCaching = allowCaching; - } + void setAllowCaching(bool allowCaching) { mAllowCaching = allowCaching; } SkPaint* getPaint(); void syncProperties() { if (mStagingProperties.mNonAnimatablePropertiesDirty) { + mCache.dirty |= (mProperties.mNonAnimatableProperties.viewportWidth != + mStagingProperties.mNonAnimatableProperties.viewportWidth) || + (mProperties.mNonAnimatableProperties.viewportHeight != + mStagingProperties.mNonAnimatableProperties.viewportHeight) || + (mProperties.mNonAnimatableProperties.scaledWidth != + mStagingProperties.mNonAnimatableProperties.scaledWidth) || + (mProperties.mNonAnimatableProperties.scaledHeight != + mStagingProperties.mNonAnimatableProperties.scaledHeight) || + (mProperties.mNonAnimatableProperties.bounds != + mStagingProperties.mNonAnimatableProperties.bounds); mProperties.syncNonAnimatableProperties(mStagingProperties); mStagingProperties.mNonAnimatablePropertiesDirty = false; } @@ -583,9 +551,7 @@ public: int scaledWidth = 0; int scaledHeight = 0; SkColorFilter* colorFilter = nullptr; - ~NonAnimatableProperties() { - SkSafeUnref(colorFilter); - } + ~NonAnimatableProperties() { SkSafeUnref(colorFilter); } } mNonAnimatableProperties; bool mNonAnimatablePropertiesDirty = true; @@ -596,14 +562,14 @@ public: // Copy over the data that can only be changed in UI thread if (mNonAnimatableProperties.colorFilter != prop.mNonAnimatableProperties.colorFilter) { SkRefCnt_SafeAssign(mNonAnimatableProperties.colorFilter, - prop.mNonAnimatableProperties.colorFilter); + prop.mNonAnimatableProperties.colorFilter); } mNonAnimatableProperties = prop.mNonAnimatableProperties; } void setViewportSize(float width, float height) { - if (mNonAnimatableProperties.viewportWidth != width - || mNonAnimatableProperties.viewportHeight != height) { + if (mNonAnimatableProperties.viewportWidth != width || + mNonAnimatableProperties.viewportHeight != height) { mNonAnimatablePropertiesDirty = true; mNonAnimatableProperties.viewportWidth = width; mNonAnimatableProperties.viewportHeight = height; @@ -622,12 +588,12 @@ public: // If the requested size is bigger than what the bitmap was, then // we increase the bitmap size to match. The width and height // are bound by MAX_CACHED_BITMAP_SIZE. - if (mNonAnimatableProperties.scaledWidth < width - || mNonAnimatableProperties.scaledHeight < height) { - mNonAnimatableProperties.scaledWidth = std::max(width, - mNonAnimatableProperties.scaledWidth); - mNonAnimatableProperties.scaledHeight = std::max(height, - mNonAnimatableProperties.scaledHeight); + if (mNonAnimatableProperties.scaledWidth < width || + mNonAnimatableProperties.scaledHeight < height) { + mNonAnimatableProperties.scaledWidth = + std::max(width, mNonAnimatableProperties.scaledWidth); + mNonAnimatableProperties.scaledHeight = + std::max(height, mNonAnimatableProperties.scaledHeight); mNonAnimatablePropertiesDirty = true; mTree->onPropertyChanged(this); } @@ -638,25 +604,13 @@ public: mTree->onPropertyChanged(this); } } - SkColorFilter* getColorFilter() const{ - return mNonAnimatableProperties.colorFilter; - } + SkColorFilter* getColorFilter() const { return mNonAnimatableProperties.colorFilter; } - float getViewportWidth() const { - return mNonAnimatableProperties.viewportWidth; - } - float getViewportHeight() const { - return mNonAnimatableProperties.viewportHeight; - } - float getScaledWidth() const { - return mNonAnimatableProperties.scaledWidth; - } - float getScaledHeight() const { - return mNonAnimatableProperties.scaledHeight; - } - void syncAnimatableProperties(const TreeProperties& prop) { - mRootAlpha = prop.mRootAlpha; - } + float getViewportWidth() const { return mNonAnimatableProperties.viewportWidth; } + float getViewportHeight() const { return mNonAnimatableProperties.viewportHeight; } + float getScaledWidth() const { return mNonAnimatableProperties.scaledWidth; } + float getScaledHeight() const { return mNonAnimatableProperties.scaledHeight; } + void syncAnimatableProperties(const TreeProperties& prop) { mRootAlpha = prop.mRootAlpha; } bool setRootAlpha(float rootAlpha) { if (rootAlpha != mRootAlpha) { mAnimatablePropertiesDirty = true; @@ -666,10 +620,8 @@ public: } return false; } - float getRootAlpha() const { return mRootAlpha;} - const SkRect& getBounds() const { - return mNonAnimatableProperties.bounds; - } + float getRootAlpha() const { return mRootAlpha; } + const SkRect& getBounds() const { return mNonAnimatableProperties.bounds; } Tree* mTree; }; void onPropertyChanged(TreeProperties* prop); @@ -700,11 +652,13 @@ public: */ void updateCache(sp<skiapipeline::VectorDrawableAtlas>& atlas, GrContext* context); + void setAntiAlias(bool aa) { mRootNode->setAntiAlias(aa); } + private: class Cache { public: - sk_sp<Bitmap> bitmap; //used by HWUI pipeline and software - //TODO: use surface instead of bitmap when drawing in software canvas + sk_sp<Bitmap> bitmap; // used by HWUI pipeline and software + // TODO: use surface instead of bitmap when drawing in software canvas bool dirty = true; // the rest of the code in Cache is used by Skia pipelines only @@ -715,7 +669,7 @@ private: * Stores a weak pointer to the atlas and a key. */ void setAtlas(sp<skiapipeline::VectorDrawableAtlas> atlas, - skiapipeline::AtlasKey newAtlasKey); + skiapipeline::AtlasKey newAtlasKey); /** * Gets a surface and bounds from the atlas. @@ -728,6 +682,7 @@ private: * Releases atlas key from the atlas, which makes it available for reuse. */ void clear(); + private: wp<skiapipeline::VectorDrawableAtlas> mAtlas; skiapipeline::AtlasKey mAtlasKey = INVALID_ATLAS_KEY; @@ -738,11 +693,6 @@ private: bool canReuseBitmap(Bitmap*, int width, int height); void updateBitmapCache(Bitmap& outCache, bool useStagingData); - /** - * Draws the root node into "surface" at a given "dst" position. - */ - void draw(SkSurface* surface, const SkRect& dst); - // Cap the bitmap size, such that it won't hurt the performance too much // and it won't crash due to a very large scale. // The drawable will look blurry above this size. @@ -759,16 +709,16 @@ private: Cache mStagingCache; Cache mCache; - PropertyChangedListener mPropertyChangedListener - = PropertyChangedListener(&mCache.dirty, &mStagingCache.dirty); + PropertyChangedListener mPropertyChangedListener = + PropertyChangedListener(&mCache.dirty, &mStagingCache.dirty); mutable bool mWillBeConsumed = false; }; -} // namespace VectorDrawable +} // namespace VectorDrawable typedef VectorDrawable::Path::Data PathData; -} // namespace uirenderer -} // namespace android +} // namespace uirenderer +} // namespace android -#endif // ANDROID_HWUI_VPATH_H +#endif // ANDROID_HWUI_VPATH_H diff --git a/libs/hwui/Vertex.h b/libs/hwui/Vertex.h index db982ad0c8f4..186d0a88a33e 100644 --- a/libs/hwui/Vertex.h +++ b/libs/hwui/Vertex.h @@ -45,15 +45,13 @@ struct Vertex { vertex->y = y; } - static inline void set(Vertex* vertex, Vector2 val) { - set(vertex, val.x, val.y); - } + static inline void set(Vertex* vertex, Vector2 val) { set(vertex, val.x, val.y); } static inline void copyWithOffset(Vertex* vertex, const Vertex& src, float x, float y) { set(vertex, src.x + x, src.y + y); } -}; // struct Vertex +}; // struct Vertex REQUIRE_COMPATIBLE_LAYOUT(Vertex); @@ -65,14 +63,14 @@ struct TextureVertex { float u, v; static inline void set(TextureVertex* vertex, float x, float y, float u, float v) { - *vertex = { x, y, u, v }; + *vertex = {x, y, u, v}; } static inline void setUV(TextureVertex* vertex, float u, float v) { vertex[0].u = u; vertex[0].v = v; } -}; // struct TextureVertex +}; // struct TextureVertex REQUIRE_COMPATIBLE_LAYOUT(TextureVertex); @@ -83,15 +81,15 @@ REQUIRE_COMPATIBLE_LAYOUT(TextureVertex); struct ColorTextureVertex { float x, y; float u, v; - float r, g, b, a; // pre-multiplied linear + float r, g, b, a; // pre-multiplied linear - static inline void set(ColorTextureVertex* vertex, float x, float y, - float u, float v, uint32_t color) { + static inline void set(ColorTextureVertex* vertex, float x, float y, float u, float v, + uint32_t color) { FloatColor c; c.set(color); - *vertex = { x, y, u, v, c.r, c.g, c.b, c.a }; + *vertex = {x, y, u, v, c.r, c.g, c.b, c.a}; } -}; // struct ColorTextureVertex +}; // struct ColorTextureVertex REQUIRE_COMPATIBLE_LAYOUT(ColorTextureVertex); @@ -103,22 +101,20 @@ struct AlphaVertex { float alpha; static inline void set(AlphaVertex* vertex, float x, float y, float alpha) { - *vertex = { x, y, alpha }; + *vertex = {x, y, alpha}; } - static inline void copyWithOffset(AlphaVertex* vertex, const AlphaVertex& src, - float x, float y) { + static inline void copyWithOffset(AlphaVertex* vertex, const AlphaVertex& src, float x, + float y) { AlphaVertex::set(vertex, src.x + x, src.y + y, src.alpha); } - static inline void setColor(AlphaVertex* vertex, float alpha) { - vertex[0].alpha = alpha; - } -}; // struct AlphaVertex + static inline void setColor(AlphaVertex* vertex, float alpha) { vertex[0].alpha = alpha; } +}; // struct AlphaVertex REQUIRE_COMPATIBLE_LAYOUT(AlphaVertex); -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android -#endif // ANDROID_HWUI_VERTEX_H +#endif // ANDROID_HWUI_VERTEX_H diff --git a/libs/hwui/VertexBuffer.h b/libs/hwui/VertexBuffer.h index bdb5b7b381bf..613cf4af64b2 100644 --- a/libs/hwui/VertexBuffer.h +++ b/libs/hwui/VertexBuffer.h @@ -41,8 +41,7 @@ public: , mMeshFeatureFlags(kNone) , mReallocBuffer(nullptr) , mCleanupMethod(nullptr) - , mCleanupIndexMethod(nullptr) - {} + , mCleanupIndexMethod(nullptr) {} ~VertexBuffer() { if (mCleanupMethod) mCleanupMethod(mBuffer); @@ -128,10 +127,10 @@ public: unsigned int getVertexCount() const { return mVertexCount; } unsigned int getSize() const { return mByteCount; } unsigned int getIndexCount() const { return mIndexCount; } - void updateIndexCount(unsigned int newCount) { + void updateIndexCount(unsigned int newCount) { mIndexCount = std::min(newCount, mAllocatedIndexCount); } - void updateVertexCount(unsigned int newCount) { + void updateVertexCount(unsigned int newCount) { mVertexCount = std::min(newCount, mAllocatedVertexCount); } MeshFeatureFlags getMeshFeatureFlags() const { return mMeshFeatureFlags; } @@ -153,7 +152,7 @@ public: private: template <class TYPE> static void cleanup(void* buffer) { - delete[] (TYPE*)buffer; + delete[](TYPE*) buffer; } Rect mBounds; @@ -169,13 +168,13 @@ private: MeshFeatureFlags mMeshFeatureFlags; - void* mReallocBuffer; // used for multi-allocation + void* mReallocBuffer; // used for multi-allocation void (*mCleanupMethod)(void*); void (*mCleanupIndexMethod)(void*); }; -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android -#endif // ANDROID_HWUI_VERTEX_BUFFER_H +#endif // ANDROID_HWUI_VERTEX_BUFFER_H diff --git a/libs/hwui/VkLayer.cpp b/libs/hwui/VkLayer.cpp index ef4784b3d65b..30fba7ae7d9b 100644 --- a/libs/hwui/VkLayer.cpp +++ b/libs/hwui/VkLayer.cpp @@ -36,5 +36,5 @@ void VkLayer::onVkContextDestroyed() { mImage = nullptr; } -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/VkLayer.h b/libs/hwui/VkLayer.h index 7e41ad1d0e9c..f23f4726017e 100644 --- a/libs/hwui/VkLayer.h +++ b/libs/hwui/VkLayer.h @@ -36,30 +36,20 @@ public: virtual ~VkLayer() {} - uint32_t getWidth() const override { - return mWidth; - } + uint32_t getWidth() const override { return mWidth; } - uint32_t getHeight() const override { - return mHeight; - } + uint32_t getHeight() const override { return mHeight; } void setSize(uint32_t width, uint32_t height) override { mWidth = width; mHeight = height; } - void setBlend(bool blend) override { - mBlend = blend; - } + void setBlend(bool blend) override { mBlend = blend; } - bool isBlend() const override { - return mBlend; - } + bool isBlend() const override { return mBlend; } - sk_sp<SkImage> getImage() { - return mImage; - } + sk_sp<SkImage> getImage() { return mImage; } void updateTexture(); @@ -72,9 +62,9 @@ private: int mHeight; bool mBlend; - sk_sp<SkImage> mImage; + sk_sp<SkImage> mImage; -}; // struct VkLayer +}; // struct VkLayer -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/debug/DefaultGlesDriver.cpp b/libs/hwui/debug/DefaultGlesDriver.cpp index 4515ec1f25a5..46ab20081d17 100644 --- a/libs/hwui/debug/DefaultGlesDriver.cpp +++ b/libs/hwui/debug/DefaultGlesDriver.cpp @@ -35,6 +35,6 @@ namespace debug { #undef CALL_GL_API #undef CALL_GL_API_RETURN -} // namespace debug -} // namespace uirenderer -} // namespace android +} // namespace debug +} // namespace uirenderer +} // namespace android diff --git a/libs/hwui/debug/DefaultGlesDriver.h b/libs/hwui/debug/DefaultGlesDriver.h index 3eab97077004..8027ea284aaa 100644 --- a/libs/hwui/debug/DefaultGlesDriver.h +++ b/libs/hwui/debug/DefaultGlesDriver.h @@ -25,11 +25,10 @@ namespace debug { class DefaultGlesDriver : public GlesDriver { public: #define GL_ENTRY(ret, api, ...) virtual ret api##_(__VA_ARGS__) override; - #include "gles_decls.in" +#include "gles_decls.in" #undef GL_ENTRY - }; -} // namespace debug -} // namespace uirenderer -} // namespace android +} // namespace debug +} // namespace uirenderer +} // namespace android diff --git a/libs/hwui/debug/FatalBaseDriver.cpp b/libs/hwui/debug/FatalBaseDriver.cpp index 4c38fac33521..ed0f8316d208 100644 --- a/libs/hwui/debug/FatalBaseDriver.cpp +++ b/libs/hwui/debug/FatalBaseDriver.cpp @@ -25,9 +25,9 @@ namespace debug { // Generate the proxy #define API_ENTRY(x) FatalBaseDriver::x##_ #define CALL_GL_API(x, ...) LOG_ALWAYS_FATAL("Not Implemented"); -#define CALL_GL_API_RETURN(x, ...) \ - LOG_ALWAYS_FATAL("Not Implemented"); \ - return static_cast<decltype(x(__VA_ARGS__))>(0); +#define CALL_GL_API_RETURN(x, ...) \ + LOG_ALWAYS_FATAL("Not Implemented"); \ + return static_cast<decltype(x(__VA_ARGS__))>(0); #include "gles_stubs.in" @@ -35,6 +35,6 @@ namespace debug { #undef CALL_GL_API #undef CALL_GL_API_RETURN -} // namespace debug -} // namespace uirenderer -} // namespace android +} // namespace debug +} // namespace uirenderer +} // namespace android diff --git a/libs/hwui/debug/FatalBaseDriver.h b/libs/hwui/debug/FatalBaseDriver.h index 76c30e90bd39..45353d0568aa 100644 --- a/libs/hwui/debug/FatalBaseDriver.h +++ b/libs/hwui/debug/FatalBaseDriver.h @@ -28,10 +28,10 @@ namespace debug { class FatalBaseDriver : public GlesDriver { public: #define GL_ENTRY(ret, api, ...) virtual ret api##_(__VA_ARGS__) override; - #include "gles_decls.in" +#include "gles_decls.in" #undef GL_ENTRY }; -} // namespace debug -} // namespace uirenderer -} // namespace android +} // namespace debug +} // namespace uirenderer +} // namespace android diff --git a/libs/hwui/debug/GlesDriver.cpp b/libs/hwui/debug/GlesDriver.cpp index 97e8f3a81b74..98f06b0cb4f0 100644 --- a/libs/hwui/debug/GlesDriver.cpp +++ b/libs/hwui/debug/GlesDriver.cpp @@ -41,6 +41,6 @@ sk_sp<const GrGLInterface> GlesDriver::getSkiaInterface() { return skiaInterface; } -} // namespace debug -} // namespace uirenderer -} // namespace android +} // namespace debug +} // namespace uirenderer +} // namespace android diff --git a/libs/hwui/debug/GlesDriver.h b/libs/hwui/debug/GlesDriver.h index 3c36487f72cf..1c77c1a45b82 100644 --- a/libs/hwui/debug/GlesDriver.h +++ b/libs/hwui/debug/GlesDriver.h @@ -43,13 +43,13 @@ public: virtual sk_sp<const GrGLInterface> getSkiaInterface(); #define GL_ENTRY(ret, api, ...) virtual ret api##_(__VA_ARGS__) = 0; - #include "gles_decls.in" +#include "gles_decls.in" #undef GL_ENTRY static GlesDriver* get(); static std::unique_ptr<GlesDriver> replace(std::unique_ptr<GlesDriver>&& driver); }; -} // namespace debug -} // namespace uirenderer -} // namespace android +} // namespace debug +} // namespace uirenderer +} // namespace android diff --git a/libs/hwui/debug/GlesErrorCheckWrapper.cpp b/libs/hwui/debug/GlesErrorCheckWrapper.cpp index 7ededaaa7fc2..8d11c1905da3 100644 --- a/libs/hwui/debug/GlesErrorCheckWrapper.cpp +++ b/libs/hwui/debug/GlesErrorCheckWrapper.cpp @@ -29,38 +29,39 @@ void GlesErrorCheckWrapper::assertNoErrors(const char* apicall) { while ((status = mBase.glGetError_()) != GL_NO_ERROR) { lastError = status; switch (status) { - case GL_INVALID_ENUM: - ALOGE("GL error: GL_INVALID_ENUM"); - lastErrorName = "GL_INVALID_ENUM"; - break; - case GL_INVALID_VALUE: - ALOGE("GL error: GL_INVALID_VALUE"); - lastErrorName = "GL_INVALID_VALUE"; - break; - case GL_INVALID_OPERATION: - ALOGE("GL error: GL_INVALID_OPERATION"); - lastErrorName = "GL_INVALID_OPERATION"; - break; - case GL_OUT_OF_MEMORY: - ALOGE("GL error: Out of memory!"); - lastErrorName = "GL_OUT_OF_MEMORY"; - break; - default: - ALOGE("GL error: 0x%x", status); - lastErrorName = "UNKNOWN"; + case GL_INVALID_ENUM: + ALOGE("GL error: GL_INVALID_ENUM"); + lastErrorName = "GL_INVALID_ENUM"; + break; + case GL_INVALID_VALUE: + ALOGE("GL error: GL_INVALID_VALUE"); + lastErrorName = "GL_INVALID_VALUE"; + break; + case GL_INVALID_OPERATION: + ALOGE("GL error: GL_INVALID_OPERATION"); + lastErrorName = "GL_INVALID_OPERATION"; + break; + case GL_OUT_OF_MEMORY: + ALOGE("GL error: Out of memory!"); + lastErrorName = "GL_OUT_OF_MEMORY"; + break; + default: + ALOGE("GL error: 0x%x", status); + lastErrorName = "UNKNOWN"; } } - LOG_ALWAYS_FATAL_IF(lastError != GL_NO_ERROR, - "%s error! %s (0x%x)", apicall, lastErrorName, lastError); + LOG_ALWAYS_FATAL_IF(lastError != GL_NO_ERROR, "%s error! %s (0x%x)", apicall, lastErrorName, + lastError); } #define API_ENTRY(x) GlesErrorCheckWrapper::x##_ -#define CALL_GL_API(x, ...) \ - mBase.x##_(__VA_ARGS__); assertNoErrors(#x) +#define CALL_GL_API(x, ...) \ + mBase.x##_(__VA_ARGS__); \ + assertNoErrors(#x) -#define CALL_GL_API_RETURN(x, ...) \ +#define CALL_GL_API_RETURN(x, ...) \ auto ret = mBase.x##_(__VA_ARGS__); \ - assertNoErrors(#x); \ + assertNoErrors(#x); \ return ret #include "gles_stubs.in" @@ -69,6 +70,6 @@ void GlesErrorCheckWrapper::assertNoErrors(const char* apicall) { #undef CALL_GL_API #undef CALL_GL_API_RETURN -} // namespace debug -} // namespace uirenderer -} // namespace android +} // namespace debug +} // namespace uirenderer +} // namespace android diff --git a/libs/hwui/debug/GlesErrorCheckWrapper.h b/libs/hwui/debug/GlesErrorCheckWrapper.h index fd45fc0184a1..ee5cc1f46de8 100644 --- a/libs/hwui/debug/GlesErrorCheckWrapper.h +++ b/libs/hwui/debug/GlesErrorCheckWrapper.h @@ -27,7 +27,7 @@ public: GlesErrorCheckWrapper(GlesDriver& base) : mBase(base) {} #define GL_ENTRY(ret, api, ...) virtual ret api##_(__VA_ARGS__) override; - #include "gles_decls.in" +#include "gles_decls.in" #undef GL_ENTRY private: @@ -36,6 +36,6 @@ private: GlesDriver& mBase; }; -} // namespace debug -} // namespace uirenderer -} // namespace android +} // namespace debug +} // namespace uirenderer +} // namespace android diff --git a/libs/hwui/debug/MockGlesDriver.h b/libs/hwui/debug/MockGlesDriver.h index e0bfc5780b21..e48ca193d48f 100644 --- a/libs/hwui/debug/MockGlesDriver.h +++ b/libs/hwui/debug/MockGlesDriver.h @@ -27,10 +27,11 @@ namespace debug { class MockGlesDriver : public FatalBaseDriver { public: MOCK_METHOD2(glBindBuffer_, void(GLenum target, GLuint buffer)); - MOCK_METHOD4(glBufferData_, void(GLenum target, GLsizeiptr size, const void *data, GLenum usage)); - MOCK_METHOD2(glGenBuffers_, void(GLsizei n, GLuint *buffers)); + MOCK_METHOD4(glBufferData_, + void(GLenum target, GLsizeiptr size, const void* data, GLenum usage)); + MOCK_METHOD2(glGenBuffers_, void(GLsizei n, GLuint* buffers)); }; -} // namespace debug -} // namespace uirenderer -} // namespace android +} // namespace debug +} // namespace uirenderer +} // namespace android diff --git a/libs/hwui/debug/NullGlesDriver.cpp b/libs/hwui/debug/NullGlesDriver.cpp index 8fbe4bfe7033..212b24290e22 100644 --- a/libs/hwui/debug/NullGlesDriver.cpp +++ b/libs/hwui/debug/NullGlesDriver.cpp @@ -29,27 +29,27 @@ struct { GLboolean scissorEnabled; } gState; -static void nullglGenCommon(GLsizei n, GLuint *buffers) { +static void nullglGenCommon(GLsizei n, GLuint* buffers) { static GLuint nextId = 0; int i; - for(i = 0; i < n; i++) { + for (i = 0; i < n; i++) { buffers[i] = ++nextId; } } -void NullGlesDriver::glGenBuffers_(GLsizei n, GLuint *buffers) { +void NullGlesDriver::glGenBuffers_(GLsizei n, GLuint* buffers) { nullglGenCommon(n, buffers); } -void NullGlesDriver::glGenFramebuffers_(GLsizei n, GLuint *framebuffers) { +void NullGlesDriver::glGenFramebuffers_(GLsizei n, GLuint* framebuffers) { nullglGenCommon(n, framebuffers); } -void NullGlesDriver::glGenRenderbuffers_(GLsizei n, GLuint *renderbuffers) { +void NullGlesDriver::glGenRenderbuffers_(GLsizei n, GLuint* renderbuffers) { nullglGenCommon(n, renderbuffers); } -void NullGlesDriver::glGenTextures_(GLsizei n, GLuint *textures) { +void NullGlesDriver::glGenTextures_(GLsizei n, GLuint* textures) { nullglGenCommon(n, textures); } @@ -63,35 +63,37 @@ GLuint NullGlesDriver::glCreateShader_(GLenum type) { return ++nextShader; } -void NullGlesDriver::glGetProgramiv_(GLuint program, GLenum pname, GLint *params) { +void NullGlesDriver::glGetProgramiv_(GLuint program, GLenum pname, GLint* params) { switch (pname) { - case GL_DELETE_STATUS: - case GL_LINK_STATUS: - case GL_VALIDATE_STATUS: - *params = GL_TRUE; - break; - case GL_INFO_LOG_LENGTH: - *params = 16; - break; + case GL_DELETE_STATUS: + case GL_LINK_STATUS: + case GL_VALIDATE_STATUS: + *params = GL_TRUE; + break; + case GL_INFO_LOG_LENGTH: + *params = 16; + break; } } -void NullGlesDriver::glGetProgramInfoLog_(GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog) { +void NullGlesDriver::glGetProgramInfoLog_(GLuint program, GLsizei bufSize, GLsizei* length, + GLchar* infoLog) { *length = snprintf(infoLog, bufSize, "success"); if (*length >= bufSize) { *length = bufSize - 1; } } -void NullGlesDriver::glGetShaderiv_(GLuint shader, GLenum pname, GLint *params) { +void NullGlesDriver::glGetShaderiv_(GLuint shader, GLenum pname, GLint* params) { switch (pname) { - case GL_COMPILE_STATUS: - case GL_DELETE_STATUS: - *params = GL_TRUE; + case GL_COMPILE_STATUS: + case GL_DELETE_STATUS: + *params = GL_TRUE; } } -void NullGlesDriver::glGetShaderInfoLog_(GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog) { +void NullGlesDriver::glGetShaderInfoLog_(GLuint shader, GLsizei bufSize, GLsizei* length, + GLchar* infoLog) { *length = snprintf(infoLog, bufSize, "success"); if (*length >= bufSize) { *length = bufSize - 1; @@ -100,9 +102,9 @@ void NullGlesDriver::glGetShaderInfoLog_(GLuint shader, GLsizei bufSize, GLsizei void setBooleanState(GLenum cap, GLboolean value) { switch (cap) { - case GL_SCISSOR_TEST: - gState.scissorEnabled = value; - break; + case GL_SCISSOR_TEST: + gState.scissorEnabled = value; + break; } } @@ -116,55 +118,55 @@ void NullGlesDriver::glDisable_(GLenum cap) { GLboolean NullGlesDriver::glIsEnabled_(GLenum cap) { switch (cap) { - case GL_SCISSOR_TEST: - return gState.scissorEnabled; - default: - return GL_FALSE; + case GL_SCISSOR_TEST: + return gState.scissorEnabled; + default: + return GL_FALSE; } } -void NullGlesDriver::glGetIntegerv_(GLenum pname, GLint *data) { +void NullGlesDriver::glGetIntegerv_(GLenum pname, GLint* data) { switch (pname) { - case GL_MAX_TEXTURE_SIZE: - *data = 2048; - break; - case GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS: - *data = 4; - break; - default: - *data = 0; + case GL_MAX_TEXTURE_SIZE: + *data = 2048; + break; + case GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS: + *data = 4; + break; + default: + *data = 0; } } GLenum NullGlesDriver::glCheckFramebufferStatus_(GLenum target) { switch (target) { - case GL_FRAMEBUFFER: - return GL_FRAMEBUFFER_COMPLETE; - default: - return 0; // error case + case GL_FRAMEBUFFER: + return GL_FRAMEBUFFER_COMPLETE; + default: + return 0; // error case } } static const char* getString(GLenum name) { switch (name) { - case GL_VENDOR: - return "android"; - case GL_RENDERER: - return "null"; - case GL_VERSION: - return "OpenGL ES 2.0 rev1"; - case GL_SHADING_LANGUAGE_VERSION: - return "OpenGL ES GLSL ES 2.0 rev1"; - case GL_EXTENSIONS: - default: - return ""; + case GL_VENDOR: + return "android"; + case GL_RENDERER: + return "null"; + case GL_VERSION: + return "OpenGL ES 2.0 rev1"; + case GL_SHADING_LANGUAGE_VERSION: + return "OpenGL ES GLSL ES 2.0 rev1"; + case GL_EXTENSIONS: + default: + return ""; } } const GLubyte* NullGlesDriver::glGetString_(GLenum name) { - return (GLubyte*) getString(name); + return (GLubyte*)getString(name); } -} // namespace debug -} // namespace uirenderer -} // namespace android +} // namespace debug +} // namespace uirenderer +} // namespace android diff --git a/libs/hwui/debug/NullGlesDriver.h b/libs/hwui/debug/NullGlesDriver.h index 37ca8f34f87b..1a27dbc35299 100644 --- a/libs/hwui/debug/NullGlesDriver.h +++ b/libs/hwui/debug/NullGlesDriver.h @@ -26,26 +26,28 @@ class NullGlesDriver : public FatalBaseDriver { public: virtual sk_sp<const GrGLInterface> getSkiaInterface() override; - virtual void glGenBuffers_(GLsizei n, GLuint *buffers) override; - virtual void glGenFramebuffers_(GLsizei n, GLuint *framebuffers) override; - virtual void glGenRenderbuffers_(GLsizei n, GLuint *renderbuffers) override; - virtual void glGenTextures_(GLsizei n, GLuint *textures) override; + virtual void glGenBuffers_(GLsizei n, GLuint* buffers) override; + virtual void glGenFramebuffers_(GLsizei n, GLuint* framebuffers) override; + virtual void glGenRenderbuffers_(GLsizei n, GLuint* renderbuffers) override; + virtual void glGenTextures_(GLsizei n, GLuint* textures) override; virtual GLuint glCreateProgram_(void) override; virtual GLuint glCreateShader_(GLenum type) override; - virtual void glGetProgramiv_(GLuint program, GLenum pname, GLint *params) override; - virtual void glGetProgramInfoLog_(GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog) override; - virtual void glGetShaderiv_(GLuint shader, GLenum pname, GLint *params) override; - virtual void glGetShaderInfoLog_(GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog) override; + virtual void glGetProgramiv_(GLuint program, GLenum pname, GLint* params) override; + virtual void glGetProgramInfoLog_(GLuint program, GLsizei bufSize, GLsizei* length, + GLchar* infoLog) override; + virtual void glGetShaderiv_(GLuint shader, GLenum pname, GLint* params) override; + virtual void glGetShaderInfoLog_(GLuint shader, GLsizei bufSize, GLsizei* length, + GLchar* infoLog) override; virtual void glEnable_(GLenum cap) override; virtual void glDisable_(GLenum cap) override; virtual GLboolean glIsEnabled_(GLenum cap) override; - virtual void glGetIntegerv_(GLenum pname, GLint *data) override; + virtual void glGetIntegerv_(GLenum pname, GLint* data) override; virtual const GLubyte* glGetString_(GLenum name) override; virtual GLenum glCheckFramebufferStatus_(GLenum target) override; virtual void glActiveTexture_(GLenum texture) override {} virtual void glAttachShader_(GLuint program, GLuint shader) override {} - virtual void glBindAttribLocation_(GLuint program, GLuint index, const GLchar *name) override {} + virtual void glBindAttribLocation_(GLuint program, GLuint index, const GLchar* name) override {} virtual void glBindBuffer_(GLenum target, GLuint buffer) override {} virtual void glBindFramebuffer_(GLenum target, GLuint framebuffer) override {} virtual void glBindRenderbuffer_(GLenum target, GLuint renderbuffer) override {} @@ -54,116 +56,147 @@ public: virtual void glBlendEquation_(GLenum mode) override {} virtual void glBlendEquationSeparate_(GLenum modeRGB, GLenum modeAlpha) override {} virtual void glBlendFunc_(GLenum sfactor, GLenum dfactor) override {} - virtual void glBlendFuncSeparate_(GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha) override {} - virtual void glBufferData_(GLenum target, GLsizeiptr size, const void *data, GLenum usage) override {} - virtual void glBufferSubData_(GLenum target, GLintptr offset, GLsizeiptr size, const void *data) override {} + virtual void glBlendFuncSeparate_(GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, + GLenum dfactorAlpha) override {} + virtual void glBufferData_(GLenum target, GLsizeiptr size, const void* data, + GLenum usage) override {} + virtual void glBufferSubData_(GLenum target, GLintptr offset, GLsizeiptr size, + const void* data) override {} virtual void glClear_(GLbitfield mask) override {} virtual void glClearColor_(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha) override {} virtual void glClearDepthf_(GLfloat d) override {} virtual void glClearStencil_(GLint s) override {} - virtual void glColorMask_(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha) override {} + virtual void glColorMask_(GLboolean red, GLboolean green, GLboolean blue, + GLboolean alpha) override {} virtual void glCompileShader_(GLuint shader) override {} - virtual void glCompressedTexImage2D_(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *data) override {} - virtual void glCompressedTexSubImage2D_(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data) override {} - virtual void glCopyTexImage2D_(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border) override {} - virtual void glCopyTexSubImage2D_(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height) override {} + virtual void glCompressedTexImage2D_(GLenum target, GLint level, GLenum internalformat, + GLsizei width, GLsizei height, GLint border, + GLsizei imageSize, const void* data) override {} + virtual void glCompressedTexSubImage2D_(GLenum target, GLint level, GLint xoffset, + GLint yoffset, GLsizei width, GLsizei height, + GLenum format, GLsizei imageSize, + const void* data) override {} + virtual void glCopyTexImage2D_(GLenum target, GLint level, GLenum internalformat, GLint x, + GLint y, GLsizei width, GLsizei height, GLint border) override {} + virtual void glCopyTexSubImage2D_(GLenum target, GLint level, GLint xoffset, GLint yoffset, + GLint x, GLint y, GLsizei width, GLsizei height) override {} virtual void glCullFace_(GLenum mode) override {} - virtual void glDeleteBuffers_(GLsizei n, const GLuint *buffers) override {} - virtual void glDeleteFramebuffers_(GLsizei n, const GLuint *framebuffers) override {} + virtual void glDeleteBuffers_(GLsizei n, const GLuint* buffers) override {} + virtual void glDeleteFramebuffers_(GLsizei n, const GLuint* framebuffers) override {} virtual void glDeleteProgram_(GLuint program) override {} - virtual void glDeleteRenderbuffers_(GLsizei n, const GLuint *renderbuffers) override {} + virtual void glDeleteRenderbuffers_(GLsizei n, const GLuint* renderbuffers) override {} virtual void glDeleteShader_(GLuint shader) override {} - virtual void glDeleteTextures_(GLsizei n, const GLuint *textures) override {} + virtual void glDeleteTextures_(GLsizei n, const GLuint* textures) override {} virtual void glDepthFunc_(GLenum func) override {} virtual void glDepthMask_(GLboolean flag) override {} virtual void glDepthRangef_(GLfloat n, GLfloat f) override {} virtual void glDetachShader_(GLuint program, GLuint shader) override {} virtual void glDisableVertexAttribArray_(GLuint index) override {} virtual void glDrawArrays_(GLenum mode, GLint first, GLsizei count) override {} - virtual void glDrawElements_(GLenum mode, GLsizei count, GLenum type, const void *indices) override {} + virtual void glDrawElements_(GLenum mode, GLsizei count, GLenum type, + const void* indices) override {} virtual void glEnableVertexAttribArray_(GLuint index) override {} virtual void glFinish_(void) override {} virtual void glFlush_(void) override {} - virtual void glFramebufferRenderbuffer_(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer) override {} - virtual void glFramebufferTexture2D_(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level) override {} + virtual void glFramebufferRenderbuffer_(GLenum target, GLenum attachment, + GLenum renderbuffertarget, + GLuint renderbuffer) override {} + virtual void glFramebufferTexture2D_(GLenum target, GLenum attachment, GLenum textarget, + GLuint texture, GLint level) override {} virtual void glFrontFace_(GLenum mode) override {} virtual void glGenerateMipmap_(GLenum target) override {} - virtual GLint glGetAttribLocation_(GLuint program, const GLchar *name) override { return 1; } + virtual GLint glGetAttribLocation_(GLuint program, const GLchar* name) override { return 1; } virtual GLenum glGetError_(void) override { return GL_NO_ERROR; } - virtual GLint glGetUniformLocation_(GLuint program, const GLchar *name) override { return 2; } + virtual GLint glGetUniformLocation_(GLuint program, const GLchar* name) override { return 2; } virtual void glHint_(GLenum target, GLenum mode) override {} virtual void glLineWidth_(GLfloat width) override {} virtual void glLinkProgram_(GLuint program) override {} virtual void glPixelStorei_(GLenum pname, GLint param) override {} virtual void glPolygonOffset_(GLfloat factor, GLfloat units) override {} - virtual void glReadPixels_(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void *pixels) override {} + virtual void glReadPixels_(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, + GLenum type, void* pixels) override {} virtual void glReleaseShaderCompiler_(void) override {} - virtual void glRenderbufferStorage_(GLenum target, GLenum internalformat, GLsizei width, GLsizei height) override {} + virtual void glRenderbufferStorage_(GLenum target, GLenum internalformat, GLsizei width, + GLsizei height) override {} virtual void glSampleCoverage_(GLfloat value, GLboolean invert) override {} virtual void glScissor_(GLint x, GLint y, GLsizei width, GLsizei height) override {} - virtual void glShaderBinary_(GLsizei count, const GLuint *shaders, GLenum binaryformat, const void *binary, GLsizei length) override {} - virtual void glShaderSource_(GLuint shader, GLsizei count, const GLchar *const*string, const GLint *length) override {} + virtual void glShaderBinary_(GLsizei count, const GLuint* shaders, GLenum binaryformat, + const void* binary, GLsizei length) override {} + virtual void glShaderSource_(GLuint shader, GLsizei count, const GLchar* const* string, + const GLint* length) override {} virtual void glStencilFunc_(GLenum func, GLint ref, GLuint mask) override {} - virtual void glStencilFuncSeparate_(GLenum face, GLenum func, GLint ref, GLuint mask) override {} + virtual void glStencilFuncSeparate_(GLenum face, GLenum func, GLint ref, GLuint mask) override { + } virtual void glStencilMask_(GLuint mask) override {} virtual void glStencilMaskSeparate_(GLenum face, GLuint mask) override {} virtual void glStencilOp_(GLenum fail, GLenum zfail, GLenum zpass) override {} - virtual void glStencilOpSeparate_(GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass) override {} - virtual void glTexImage2D_(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels) override {} + virtual void glStencilOpSeparate_(GLenum face, GLenum sfail, GLenum dpfail, + GLenum dppass) override {} + virtual void glTexImage2D_(GLenum target, GLint level, GLint internalformat, GLsizei width, + GLsizei height, GLint border, GLenum format, GLenum type, + const void* pixels) override {} virtual void glTexParameterf_(GLenum target, GLenum pname, GLfloat param) override {} - virtual void glTexParameterfv_(GLenum target, GLenum pname, const GLfloat *params) override {} + virtual void glTexParameterfv_(GLenum target, GLenum pname, const GLfloat* params) override {} virtual void glTexParameteri_(GLenum target, GLenum pname, GLint param) override {} - virtual void glTexParameteriv_(GLenum target, GLenum pname, const GLint *params) override {} - virtual void glTexSubImage2D_(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels) override {} + virtual void glTexParameteriv_(GLenum target, GLenum pname, const GLint* params) override {} + virtual void glTexSubImage2D_(GLenum target, GLint level, GLint xoffset, GLint yoffset, + GLsizei width, GLsizei height, GLenum format, GLenum type, + const void* pixels) override {} virtual void glUniform1f_(GLint location, GLfloat v0) override {} - virtual void glUniform1fv_(GLint location, GLsizei count, const GLfloat *value) override {} + virtual void glUniform1fv_(GLint location, GLsizei count, const GLfloat* value) override {} virtual void glUniform1i_(GLint location, GLint v0) override {} - virtual void glUniform1iv_(GLint location, GLsizei count, const GLint *value) override {} + virtual void glUniform1iv_(GLint location, GLsizei count, const GLint* value) override {} virtual void glUniform2f_(GLint location, GLfloat v0, GLfloat v1) override {} - virtual void glUniform2fv_(GLint location, GLsizei count, const GLfloat *value) override {} + virtual void glUniform2fv_(GLint location, GLsizei count, const GLfloat* value) override {} virtual void glUniform2i_(GLint location, GLint v0, GLint v1) override {} - virtual void glUniform2iv_(GLint location, GLsizei count, const GLint *value) override {} + virtual void glUniform2iv_(GLint location, GLsizei count, const GLint* value) override {} virtual void glUniform3f_(GLint location, GLfloat v0, GLfloat v1, GLfloat v2) override {} - virtual void glUniform3fv_(GLint location, GLsizei count, const GLfloat *value) override {} + virtual void glUniform3fv_(GLint location, GLsizei count, const GLfloat* value) override {} virtual void glUniform3i_(GLint location, GLint v0, GLint v1, GLint v2) override {} - virtual void glUniform3iv_(GLint location, GLsizei count, const GLint *value) override {} - virtual void glUniform4f_(GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3) override {} - virtual void glUniform4fv_(GLint location, GLsizei count, const GLfloat *value) override {} + virtual void glUniform3iv_(GLint location, GLsizei count, const GLint* value) override {} + virtual void glUniform4f_(GLint location, GLfloat v0, GLfloat v1, GLfloat v2, + GLfloat v3) override {} + virtual void glUniform4fv_(GLint location, GLsizei count, const GLfloat* value) override {} virtual void glUniform4i_(GLint location, GLint v0, GLint v1, GLint v2, GLint v3) override {} - virtual void glUniform4iv_(GLint location, GLsizei count, const GLint *value) override {} - virtual void glUniformMatrix2fv_(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) override {} - virtual void glUniformMatrix3fv_(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) override {} - virtual void glUniformMatrix4fv_(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) override {} + virtual void glUniform4iv_(GLint location, GLsizei count, const GLint* value) override {} + virtual void glUniformMatrix2fv_(GLint location, GLsizei count, GLboolean transpose, + const GLfloat* value) override {} + virtual void glUniformMatrix3fv_(GLint location, GLsizei count, GLboolean transpose, + const GLfloat* value) override {} + virtual void glUniformMatrix4fv_(GLint location, GLsizei count, GLboolean transpose, + const GLfloat* value) override {} virtual void glUseProgram_(GLuint program) override {} virtual void glValidateProgram_(GLuint program) override {} virtual void glVertexAttrib1f_(GLuint index, GLfloat x) override {} - virtual void glVertexAttrib1fv_(GLuint index, const GLfloat *v) override {} + virtual void glVertexAttrib1fv_(GLuint index, const GLfloat* v) override {} virtual void glVertexAttrib2f_(GLuint index, GLfloat x, GLfloat y) override {} - virtual void glVertexAttrib2fv_(GLuint index, const GLfloat *v) override {} + virtual void glVertexAttrib2fv_(GLuint index, const GLfloat* v) override {} virtual void glVertexAttrib3f_(GLuint index, GLfloat x, GLfloat y, GLfloat z) override {} - virtual void glVertexAttrib3fv_(GLuint index, const GLfloat *v) override {} - virtual void glVertexAttrib4f_(GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w) override {} - virtual void glVertexAttrib4fv_(GLuint index, const GLfloat *v) override {} - virtual void glVertexAttribPointer_(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer) override {} + virtual void glVertexAttrib3fv_(GLuint index, const GLfloat* v) override {} + virtual void glVertexAttrib4f_(GLuint index, GLfloat x, GLfloat y, GLfloat z, + GLfloat w) override {} + virtual void glVertexAttrib4fv_(GLuint index, const GLfloat* v) override {} + virtual void glVertexAttribPointer_(GLuint index, GLint size, GLenum type, GLboolean normalized, + GLsizei stride, const void* pointer) override {} virtual void glViewport_(GLint x, GLint y, GLsizei width, GLsizei height) override {} // gles2 ext - virtual void glInsertEventMarkerEXT_(GLsizei length, const GLchar *marker) override {} - virtual void glPushGroupMarkerEXT_(GLsizei length, const GLchar *marker) override {} + virtual void glInsertEventMarkerEXT_(GLsizei length, const GLchar* marker) override {} + virtual void glPushGroupMarkerEXT_(GLsizei length, const GLchar* marker) override {} virtual void glPopGroupMarkerEXT_(void) override {} - virtual void glDiscardFramebufferEXT_(GLenum target, GLsizei numAttachments, const GLenum *attachments) override {} + virtual void glDiscardFramebufferEXT_(GLenum target, GLsizei numAttachments, + const GLenum* attachments) override {} virtual void glEGLImageTargetTexture2DOES_(GLenum target, GLeglImageOES image) override {} // GLES3 - virtual void* glMapBufferRange_(GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access) override { + virtual void* glMapBufferRange_(GLenum target, GLintptr offset, GLsizeiptr length, + GLbitfield access) override { return 0; } - virtual GLboolean glUnmapBuffer_(GLenum target) override { - return GL_FALSE; - } + virtual GLboolean glUnmapBuffer_(GLenum target) override { return GL_FALSE; } }; -} // namespace debug -} // namespace uirenderer -} // namespace android +} // namespace debug +} // namespace uirenderer +} // namespace android diff --git a/libs/hwui/debug/ScopedReplaceDriver.h b/libs/hwui/debug/ScopedReplaceDriver.h index 342c9d223727..110196fb2a01 100644 --- a/libs/hwui/debug/ScopedReplaceDriver.h +++ b/libs/hwui/debug/ScopedReplaceDriver.h @@ -33,14 +33,13 @@ public: Driver& get() { return *mCurrentDriver; } - ~ScopedReplaceDriver() { - GlesDriver::replace(std::move(mOldDriver)); - } + ~ScopedReplaceDriver() { GlesDriver::replace(std::move(mOldDriver)); } + private: std::unique_ptr<GlesDriver> mOldDriver; Driver* mCurrentDriver; }; -} // namespace debug -} // namespace uirenderer -} // namespace android +} // namespace debug +} // namespace uirenderer +} // namespace android diff --git a/libs/hwui/debug/gles_redefine.h b/libs/hwui/debug/gles_redefine.h index 201f0a945209..49b506918372 100644 --- a/libs/hwui/debug/gles_redefine.h +++ b/libs/hwui/debug/gles_redefine.h @@ -212,7 +212,8 @@ #define glDrawElementsInstancedANGLE wrap_glDrawElementsInstancedANGLE #define glDrawElementsInstancedBaseInstanceEXT wrap_glDrawElementsInstancedBaseInstanceEXT #define glDrawElementsInstancedBaseVertex wrap_glDrawElementsInstancedBaseVertex -#define glDrawElementsInstancedBaseVertexBaseInstanceEXT wrap_glDrawElementsInstancedBaseVertexBaseInstanceEXT +#define glDrawElementsInstancedBaseVertexBaseInstanceEXT \ + wrap_glDrawElementsInstancedBaseVertexBaseInstanceEXT #define glDrawElementsInstancedBaseVertexEXT wrap_glDrawElementsInstancedBaseVertexEXT #define glDrawElementsInstancedBaseVertexOES wrap_glDrawElementsInstancedBaseVertexOES #define glDrawElementsInstancedEXT wrap_glDrawElementsInstancedEXT diff --git a/libs/hwui/debug/nullegl.cpp b/libs/hwui/debug/nullegl.cpp index 2ae71df5c22c..ca47f8fd22ef 100644 --- a/libs/hwui/debug/nullegl.cpp +++ b/libs/hwui/debug/nullegl.cpp @@ -21,8 +21,8 @@ #include <stdlib.h> #include <string.h> -static EGLDisplay gDisplay = (EGLDisplay) 1; -static EGLSyncKHR gFence = (EGLSyncKHR) 1; +static EGLDisplay gDisplay = (EGLDisplay)1; +static EGLSyncKHR gFence = (EGLSyncKHR)1; typedef struct { EGLSurface surface; @@ -43,8 +43,8 @@ static void makeThreadState() { ThreadState* getThreadState() { ThreadState* ptr; pthread_once(&ThreadStateSetupOnce, makeThreadState); - if ((ptr = (ThreadState*) pthread_getspecific(ThreadStateKey)) == NULL) { - ptr = (ThreadState*) calloc(1, sizeof(ThreadState)); + if ((ptr = (ThreadState*)pthread_getspecific(ThreadStateKey)) == NULL) { + ptr = (ThreadState*)calloc(1, sizeof(ThreadState)); ptr->context = EGL_NO_CONTEXT; ptr->surface = EGL_NO_SURFACE; pthread_setspecific(ThreadStateKey, ptr); @@ -60,7 +60,7 @@ EGLDisplay eglGetDisplay(EGLNativeDisplayType display_id) { return gDisplay; } -EGLBoolean eglInitialize(EGLDisplay dpy, EGLint *major, EGLint *minor) { +EGLBoolean eglInitialize(EGLDisplay dpy, EGLint* major, EGLint* minor) { return EGL_TRUE; } @@ -68,30 +68,27 @@ EGLBoolean eglTerminate(EGLDisplay dpy) { return EGL_TRUE; } -const char * eglQueryString(EGLDisplay dpy, EGLint name) { +const char* eglQueryString(EGLDisplay dpy, EGLint name) { if (name == EGL_EXTENSIONS) { return "EGL_KHR_swap_buffers_with_damage"; } return ""; } -EGLBoolean eglChooseConfig(EGLDisplay dpy, const EGLint *attrib_list, - EGLConfig *configs, EGLint config_size, - EGLint *num_config) { +EGLBoolean eglChooseConfig(EGLDisplay dpy, const EGLint* attrib_list, EGLConfig* configs, + EGLint config_size, EGLint* num_config) { memset(configs, 9, sizeof(EGLConfig) * config_size); *num_config = config_size; return EGL_TRUE; } -EGLSurface eglCreateWindowSurface(EGLDisplay dpy, EGLConfig config, - EGLNativeWindowType win, - const EGLint *attrib_list) { - return (EGLSurface) malloc(sizeof(void*)); +EGLSurface eglCreateWindowSurface(EGLDisplay dpy, EGLConfig config, EGLNativeWindowType win, + const EGLint* attrib_list) { + return (EGLSurface)malloc(sizeof(void*)); } -EGLSurface eglCreatePbufferSurface(EGLDisplay dpy, EGLConfig config, - const EGLint *attrib_list) { - return (EGLSurface) malloc(sizeof(void*)); +EGLSurface eglCreatePbufferSurface(EGLDisplay dpy, EGLConfig config, const EGLint* attrib_list) { + return (EGLSurface)malloc(sizeof(void*)); } EGLBoolean eglDestroySurface(EGLDisplay dpy, EGLSurface surface) { @@ -99,8 +96,7 @@ EGLBoolean eglDestroySurface(EGLDisplay dpy, EGLSurface surface) { return EGL_TRUE; } -EGLBoolean eglQuerySurface(EGLDisplay dpy, EGLSurface surface, - EGLint attribute, EGLint *value) { +EGLBoolean eglQuerySurface(EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint* value) { *value = 1000; return EGL_TRUE; } @@ -109,8 +105,7 @@ EGLBoolean eglReleaseThread(void) { return EGL_TRUE; } -EGLBoolean eglSurfaceAttrib(EGLDisplay dpy, EGLSurface surface, - EGLint attribute, EGLint value) { +EGLBoolean eglSurfaceAttrib(EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint value) { return EGL_TRUE; } @@ -118,18 +113,16 @@ EGLBoolean eglSwapInterval(EGLDisplay dpy, EGLint interval) { return EGL_TRUE; } -EGLContext eglCreateContext(EGLDisplay dpy, EGLConfig config, - EGLContext share_context, - const EGLint *attrib_list) { - return (EGLContext) malloc(sizeof(void*)); +EGLContext eglCreateContext(EGLDisplay dpy, EGLConfig config, EGLContext share_context, + const EGLint* attrib_list) { + return (EGLContext)malloc(sizeof(void*)); } EGLBoolean eglDestroyContext(EGLDisplay dpy, EGLContext ctx) { free(ctx); return EGL_TRUE; } -EGLBoolean eglMakeCurrent(EGLDisplay dpy, EGLSurface draw, - EGLSurface read, EGLContext ctx) { +EGLBoolean eglMakeCurrent(EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx) { ThreadState* state = getThreadState(); state->surface = draw; state->context = ctx; @@ -152,15 +145,17 @@ EGLBoolean eglSwapBuffers(EGLDisplay dpy, EGLSurface surface) { return EGL_TRUE; } -EGLBoolean eglSwapBuffersWithDamageKHR(EGLDisplay dpy, EGLSurface surface, EGLint *rects, EGLint rectCount) { +EGLBoolean eglSwapBuffersWithDamageKHR(EGLDisplay dpy, EGLSurface surface, EGLint* rects, + EGLint rectCount) { return EGL_TRUE; } -EGLImageKHR eglCreateImageKHR(EGLDisplay dpy, EGLContext ctx, EGLenum target, EGLClientBuffer buffer, const EGLint *attrib_list) { - return (EGLImageKHR) malloc(sizeof(EGLImageKHR)); +EGLImageKHR eglCreateImageKHR(EGLDisplay dpy, EGLContext ctx, EGLenum target, + EGLClientBuffer buffer, const EGLint* attrib_list) { + return (EGLImageKHR)malloc(sizeof(EGLImageKHR)); } -EGLSyncKHR eglCreateSyncKHR(EGLDisplay dpy, EGLenum type, const EGLint *attrib_list) { +EGLSyncKHR eglCreateSyncKHR(EGLDisplay dpy, EGLenum type, const EGLint* attrib_list) { return gFence; } diff --git a/libs/hwui/font/CacheTexture.cpp b/libs/hwui/font/CacheTexture.cpp index 73beba9297c4..5dd8bb8119e9 100644 --- a/libs/hwui/font/CacheTexture.cpp +++ b/libs/hwui/font/CacheTexture.cpp @@ -16,12 +16,12 @@ #include <SkGlyph.h> -#include "CacheTexture.h" -#include "FontUtil.h" #include "../Caches.h" #include "../Debug.h" #include "../Extensions.h" #include "../PixelBuffer.h" +#include "CacheTexture.h" +#include "FontUtil.h" namespace android { namespace uirenderer { @@ -37,9 +37,8 @@ namespace uirenderer { */ CacheBlock* CacheBlock::insertBlock(CacheBlock* head, CacheBlock* newBlock) { #if DEBUG_FONT_RENDERER - ALOGD("insertBlock: this, x, y, w, h = %p, %d, %d, %d, %d", - newBlock, newBlock->mX, newBlock->mY, - newBlock->mWidth, newBlock->mHeight); + ALOGD("insertBlock: this, x, y, w, h = %p, %d, %d, %d, %d", newBlock, newBlock->mX, + newBlock->mY, newBlock->mWidth, newBlock->mHeight); #endif CacheBlock* currBlock = head; @@ -81,9 +80,8 @@ CacheBlock* CacheBlock::insertBlock(CacheBlock* head, CacheBlock* newBlock) { CacheBlock* CacheBlock::removeBlock(CacheBlock* head, CacheBlock* blockToRemove) { #if DEBUG_FONT_RENDERER - ALOGD("removeBlock: this, x, y, w, h = %p, %d, %d, %d, %d", - blockToRemove, blockToRemove->mX, blockToRemove->mY, - blockToRemove->mWidth, blockToRemove->mHeight); + ALOGD("removeBlock: this, x, y, w, h = %p, %d, %d, %d, %d", blockToRemove, blockToRemove->mX, + blockToRemove->mY, blockToRemove->mWidth, blockToRemove->mHeight); #endif CacheBlock* newHead = head; @@ -93,7 +91,7 @@ CacheBlock* CacheBlock::removeBlock(CacheBlock* head, CacheBlock* blockToRemove) if (prevBlock) { // If this doesn't hold, we have a use-after-free below. LOG_ALWAYS_FATAL_IF(head == blockToRemove, - "removeBlock: head should not have a previous block"); + "removeBlock: head should not have a previous block"); prevBlock->mNext = nextBlock; } else { newHead = nextBlock; @@ -121,8 +119,9 @@ CacheTexture::CacheTexture(uint16_t width, uint16_t height, GLenum format, uint3 , mCaches(Caches::getInstance()) { mTexture.blend = true; - mCacheBlocks = new CacheBlock(TEXTURE_BORDER_SIZE, TEXTURE_BORDER_SIZE, - getWidth() - TEXTURE_BORDER_SIZE, getHeight() - TEXTURE_BORDER_SIZE); + mCacheBlocks = + new CacheBlock(TEXTURE_BORDER_SIZE, TEXTURE_BORDER_SIZE, + getWidth() - TEXTURE_BORDER_SIZE, getHeight() - TEXTURE_BORDER_SIZE); // OpenGL ES 3.0+ lets us specify the row length for unpack operations such // as glTexSubImage2D(). This allows us to upload a sub-rectangle of a texture. @@ -150,8 +149,9 @@ void CacheTexture::reset() { void CacheTexture::init() { // reset, then create a new remainder space to start again reset(); - mCacheBlocks = new CacheBlock(TEXTURE_BORDER_SIZE, TEXTURE_BORDER_SIZE, - getWidth() - TEXTURE_BORDER_SIZE, getHeight() - TEXTURE_BORDER_SIZE); + mCacheBlocks = + new CacheBlock(TEXTURE_BORDER_SIZE, TEXTURE_BORDER_SIZE, + getWidth() - TEXTURE_BORDER_SIZE, getHeight() - TEXTURE_BORDER_SIZE); } void CacheTexture::releaseMesh() { @@ -233,7 +233,7 @@ bool CacheTexture::fitBitmap(const SkGlyph& glyph, uint32_t* retOriginX, uint32_ if (mFormat != GL_ALPHA) { #if DEBUG_FONT_RENDERER ALOGD("fitBitmap: texture format %x is inappropriate for monochromatic glyphs", - mFormat); + mFormat); #endif return false; } @@ -272,8 +272,8 @@ bool CacheTexture::fitBitmap(const SkGlyph& glyph, uint32_t* retOriginX, uint32_ // it's the remainder space (mY == 0) or there's only enough height for this one glyph // or it's within ROUNDING_SIZE of the block width if (roundedUpW <= cacheBlock->mWidth && glyphH <= cacheBlock->mHeight && - (cacheBlock->mY == TEXTURE_BORDER_SIZE || - (cacheBlock->mWidth - roundedUpW < CACHE_BLOCK_ROUNDING_SIZE))) { + (cacheBlock->mY == TEXTURE_BORDER_SIZE || + (cacheBlock->mWidth - roundedUpW < CACHE_BLOCK_ROUNDING_SIZE))) { if (cacheBlock->mHeight - glyphH < glyphH) { // Only enough space for this glyph - don't bother rounding up the width roundedUpW = glyphW; @@ -292,12 +292,13 @@ bool CacheTexture::fitBitmap(const SkGlyph& glyph, uint32_t* retOriginX, uint32_ if (getHeight() - glyphH >= glyphH) { // There's enough height left over to create a new CacheBlock - CacheBlock* newBlock = new CacheBlock(oldX, glyphH + TEXTURE_BORDER_SIZE, - roundedUpW, getHeight() - glyphH - TEXTURE_BORDER_SIZE); + CacheBlock* newBlock = + new CacheBlock(oldX, glyphH + TEXTURE_BORDER_SIZE, roundedUpW, + getHeight() - glyphH - TEXTURE_BORDER_SIZE); #if DEBUG_FONT_RENDERER ALOGD("fitBitmap: Created new block: this, x, y, w, h = %p, %d, %d, %d, %d", - newBlock, newBlock->mX, newBlock->mY, - newBlock->mWidth, newBlock->mHeight); + newBlock, newBlock->mX, newBlock->mY, newBlock->mWidth, + newBlock->mHeight); #endif mCacheBlocks = CacheBlock::insertBlock(mCacheBlocks, newBlock); } @@ -307,8 +308,8 @@ bool CacheTexture::fitBitmap(const SkGlyph& glyph, uint32_t* retOriginX, uint32_ cacheBlock->mHeight -= glyphH; #if DEBUG_FONT_RENDERER ALOGD("fitBitmap: Added to existing block: this, x, y, w, h = %p, %d, %d, %d, %d", - cacheBlock, cacheBlock->mX, cacheBlock->mY, - cacheBlock->mWidth, cacheBlock->mHeight); + cacheBlock, cacheBlock->mX, cacheBlock->mY, cacheBlock->mWidth, + cacheBlock->mHeight); #endif } @@ -319,7 +320,7 @@ bool CacheTexture::fitBitmap(const SkGlyph& glyph, uint32_t* retOriginX, uint32_ mDirty = true; const Rect r(*retOriginX - TEXTURE_BORDER_SIZE, *retOriginY - TEXTURE_BORDER_SIZE, - *retOriginX + glyphW, *retOriginY + glyphH); + *retOriginX + glyphW, *retOriginY + glyphH); mDirtyRect.unionWith(r); mNumGlyphs++; @@ -350,5 +351,5 @@ uint32_t CacheTexture::calculateFreeMemory() const { return free; } -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/font/CacheTexture.h b/libs/hwui/font/CacheTexture.h index 6750a8ae11cf..654378eeb47e 100644 --- a/libs/hwui/font/CacheTexture.h +++ b/libs/hwui/font/CacheTexture.h @@ -26,7 +26,6 @@ #include <SkGlyph.h> #include <utils/Log.h> - namespace android { namespace uirenderer { @@ -53,9 +52,8 @@ struct CacheBlock { CacheBlock* mNext; CacheBlock* mPrev; - CacheBlock(uint16_t x, uint16_t y, uint16_t width, uint16_t height): - mX(x), mY(y), mWidth(width), mHeight(height), mNext(nullptr), mPrev(nullptr) { - } + CacheBlock(uint16_t x, uint16_t y, uint16_t width, uint16_t height) + : mX(x), mY(y), mWidth(width), mHeight(height), mNext(nullptr), mPrev(nullptr) {} static CacheBlock* insertBlock(CacheBlock* head, CacheBlock* newBlock); static CacheBlock* removeBlock(CacheBlock* head, CacheBlock* blockToRemove); @@ -63,9 +61,8 @@ struct CacheBlock { void output() { CacheBlock* currBlock = this; while (currBlock) { - ALOGD("Block: this, x, y, w, h = %p, %d, %d, %d, %d", - currBlock, currBlock->mX, currBlock->mY, - currBlock->mWidth, currBlock->mHeight); + ALOGD("Block: this, x, y, w, h = %p, %d, %d, %d, %d", currBlock, currBlock->mX, + currBlock->mY, currBlock->mWidth, currBlock->mHeight); currBlock = currBlock->mNext; } } @@ -91,29 +88,19 @@ public: bool fitBitmap(const SkGlyph& glyph, uint32_t* retOriginX, uint32_t* retOriginY); - inline uint16_t getWidth() const { - return mWidth; - } + inline uint16_t getWidth() const { return mWidth; } - inline uint16_t getHeight() const { - return mHeight; - } + inline uint16_t getHeight() const { return mHeight; } - inline GLenum getFormat() const { - return mFormat; - } + inline GLenum getFormat() const { return mFormat; } inline uint32_t getOffset(uint16_t x, uint16_t y) const { return (y * getWidth() + x) * PixelBuffer::formatSize(mFormat); } - inline const Rect* getDirtyRect() const { - return &mDirtyRect; - } + inline const Rect* getDirtyRect() const { return &mDirtyRect; } - inline PixelBuffer* getPixelBuffer() const { - return mPixelBuffer; - } + inline PixelBuffer* getPixelBuffer() const { return mPixelBuffer; } Texture& getTexture() { allocatePixelBuffer(); @@ -125,43 +112,28 @@ public: return mTexture.id(); } - inline bool isDirty() const { - return mDirty; - } + inline bool isDirty() const { return mDirty; } - inline bool getLinearFiltering() const { - return mLinearFiltering; - } + inline bool getLinearFiltering() const { return mLinearFiltering; } /** * This method assumes that the proper texture unit is active. */ void setLinearFiltering(bool linearFiltering); - inline uint16_t getGlyphCount() const { - return mNumGlyphs; - } + inline uint16_t getGlyphCount() const { return mNumGlyphs; } - TextureVertex* mesh() const { - return mMesh; - } + TextureVertex* mesh() const { return mMesh; } - uint32_t meshElementCount() const { - return mCurrentQuad * 6; - } + uint32_t meshElementCount() const { return mCurrentQuad * 6; } - uint16_t* indices() const { - return (uint16_t*) nullptr; - } + uint16_t* indices() const { return (uint16_t*)nullptr; } - void resetMesh() { - mCurrentQuad = 0; - } + void resetMesh() { mCurrentQuad = 0; } - inline void addQuad(float x1, float y1, float u1, float v1, - float x2, float y2, float u2, float v2, - float x3, float y3, float u3, float v3, - float x4, float y4, float u4, float v4) { + inline void addQuad(float x1, float y1, float u1, float v1, float x2, float y2, float u2, + float v2, float x3, float y3, float u3, float v3, float x4, float y4, + float u4, float v4) { TextureVertex* mesh = mMesh + mCurrentQuad * 4; TextureVertex::set(mesh++, x2, y2, u2, v2); TextureVertex::set(mesh++, x3, y3, u3, v3); @@ -170,13 +142,9 @@ public: mCurrentQuad++; } - bool canDraw() const { - return mCurrentQuad > 0; - } + bool canDraw() const { return mCurrentQuad > 0; } - bool endOfMesh() const { - return mCurrentQuad == mMaxQuadCount; - } + bool endOfMesh() const { return mCurrentQuad == mMaxQuadCount; } uint32_t calculateFreeMemory() const; @@ -199,7 +167,7 @@ private: Rect mDirtyRect; }; -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android -#endif // ANDROID_HWUI_CACHE_TEXTURE_H +#endif // ANDROID_HWUI_CACHE_TEXTURE_H diff --git a/libs/hwui/font/CachedGlyphInfo.h b/libs/hwui/font/CachedGlyphInfo.h index 073d59bdea3f..93bb823db512 100644 --- a/libs/hwui/font/CachedGlyphInfo.h +++ b/libs/hwui/font/CachedGlyphInfo.h @@ -50,7 +50,7 @@ struct CachedGlyphInfo { CacheTexture* mCacheTexture; }; -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android -#endif // ANDROID_HWUI_CACHED_GLYPH_INFO_H +#endif // ANDROID_HWUI_CACHED_GLYPH_INFO_H diff --git a/libs/hwui/font/Font.cpp b/libs/hwui/font/Font.cpp index 24d497cb1860..41a24a809e89 100644 --- a/libs/hwui/font/Font.cpp +++ b/libs/hwui/font/Font.cpp @@ -19,17 +19,17 @@ #include <utils/JenkinsHash.h> #include <utils/Trace.h> -#include <SkSurfaceProps.h> #include <SkGlyph.h> #include <SkGlyphCache.h> +#include <SkSurfaceProps.h> #include <SkUtils.h> -#include "FontUtil.h" -#include "Font.h" #include "../Debug.h" #include "../FontRenderer.h" #include "../PixelBuffer.h" #include "../Properties.h" +#include "Font.h" +#include "FontUtil.h" namespace android { namespace uirenderer { @@ -38,8 +38,8 @@ namespace uirenderer { // Font /////////////////////////////////////////////////////////////////////////////// -Font::Font(FontRenderer* state, const Font::FontDescription& desc) : - mState(state), mDescription(desc) { } +Font::Font(FontRenderer* state, const Font::FontDescription& desc) + : mState(state), mDescription(desc) {} Font::FontDescription::FontDescription(const SkPaint* paint, const SkMatrix& rasterMatrix) : mLookupTransform(rasterMatrix) { @@ -82,7 +82,7 @@ hash_t Font::FontDescription::hash() const { } int Font::FontDescription::compare(const Font::FontDescription& lhs, - const Font::FontDescription& rhs) { + const Font::FontDescription& rhs) { int deltaInt = int(lhs.mFontId) - int(rhs.mFontId); if (deltaInt != 0) return deltaInt; @@ -110,15 +110,15 @@ int Font::FontDescription::compare(const Font::FontDescription& lhs, deltaInt = int(lhs.mHinting) - int(rhs.mHinting); if (deltaInt != 0) return deltaInt; - if (lhs.mLookupTransform[SkMatrix::kMScaleX] < - rhs.mLookupTransform[SkMatrix::kMScaleX]) return -1; - if (lhs.mLookupTransform[SkMatrix::kMScaleX] > - rhs.mLookupTransform[SkMatrix::kMScaleX]) return +1; + if (lhs.mLookupTransform[SkMatrix::kMScaleX] < rhs.mLookupTransform[SkMatrix::kMScaleX]) + return -1; + if (lhs.mLookupTransform[SkMatrix::kMScaleX] > rhs.mLookupTransform[SkMatrix::kMScaleX]) + return +1; - if (lhs.mLookupTransform[SkMatrix::kMScaleY] < - rhs.mLookupTransform[SkMatrix::kMScaleY]) return -1; - if (lhs.mLookupTransform[SkMatrix::kMScaleY] > - rhs.mLookupTransform[SkMatrix::kMScaleY]) return +1; + if (lhs.mLookupTransform[SkMatrix::kMScaleY] < rhs.mLookupTransform[SkMatrix::kMScaleY]) + return -1; + if (lhs.mLookupTransform[SkMatrix::kMScaleY] > rhs.mLookupTransform[SkMatrix::kMScaleY]) + return +1; return 0; } @@ -132,10 +132,10 @@ void Font::invalidateTextureCache(CacheTexture* cacheTexture) { } } -void Font::measureCachedGlyph(CachedGlyphInfo *glyph, int x, int y, - uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos) { - int width = (int) glyph->mBitmapWidth; - int height = (int) glyph->mBitmapHeight; +void Font::measureCachedGlyph(CachedGlyphInfo* glyph, int x, int y, uint8_t* bitmap, + uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos) { + int width = (int)glyph->mBitmapWidth; + int height = (int)glyph->mBitmapHeight; int nPenX = x + glyph->mBitmapLeft; int nPenY = y + glyph->mBitmapTop; @@ -154,10 +154,10 @@ void Font::measureCachedGlyph(CachedGlyphInfo *glyph, int x, int y, } } -void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y, - uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos) { - float width = (float) glyph->mBitmapWidth; - float height = (float) glyph->mBitmapHeight; +void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y, uint8_t* bitmap, uint32_t bitmapW, + uint32_t bitmapH, Rect* bounds, const float* pos) { + float width = (float)glyph->mBitmapWidth; + float height = (float)glyph->mBitmapHeight; float nPenX = x + glyph->mBitmapLeft; float nPenY = y + glyph->mBitmapTop + height; @@ -167,16 +167,16 @@ void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y, float v1 = glyph->mBitmapMinV; float v2 = glyph->mBitmapMaxV; - mState->appendMeshQuad(nPenX, nPenY, u1, v2, - nPenX + width, nPenY, u2, v2, - nPenX + width, nPenY - height, u2, v1, - nPenX, nPenY - height, u1, v1, glyph->mCacheTexture); + mState->appendMeshQuad(nPenX, nPenY, u1, v2, nPenX + width, nPenY, u2, v2, nPenX + width, + nPenY - height, u2, v1, nPenX, nPenY - height, u1, v1, + glyph->mCacheTexture); } -void Font::drawCachedGlyphTransformed(CachedGlyphInfo* glyph, int x, int y, - uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos) { - float width = (float) glyph->mBitmapWidth; - float height = (float) glyph->mBitmapHeight; +void Font::drawCachedGlyphTransformed(CachedGlyphInfo* glyph, int x, int y, uint8_t* bitmap, + uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, + const float* pos) { + float width = (float)glyph->mBitmapWidth; + float height = (float)glyph->mBitmapHeight; SkPoint p[4]; p[0].iset(glyph->mBitmapLeft, glyph->mBitmapTop + height); @@ -196,15 +196,14 @@ void Font::drawCachedGlyphTransformed(CachedGlyphInfo* glyph, int x, int y, float v1 = glyph->mBitmapMinV; float v2 = glyph->mBitmapMaxV; - mState->appendRotatedMeshQuad( - p[0].x(), p[0].y(), u1, v2, - p[1].x(), p[1].y(), u2, v2, - p[2].x(), p[2].y(), u2, v1, - p[3].x(), p[3].y(), u1, v1, glyph->mCacheTexture); + mState->appendRotatedMeshQuad(p[0].x(), p[0].y(), u1, v2, p[1].x(), p[1].y(), u2, v2, p[2].x(), + p[2].y(), u2, v1, p[3].x(), p[3].y(), u1, v1, + glyph->mCacheTexture); } void Font::drawCachedGlyphBitmap(CachedGlyphInfo* glyph, int x, int y, uint8_t* bitmap, - uint32_t bitmapWidth, uint32_t bitmapHeight, Rect* bounds, const float* pos) { + uint32_t bitmapWidth, uint32_t bitmapHeight, Rect* bounds, + const float* pos) { int dstX = x + glyph->mBitmapLeft; int dstY = y + glyph->mBitmapTop; @@ -221,12 +220,11 @@ void Font::drawCachedGlyphBitmap(CachedGlyphInfo* glyph, int x, int y, uint8_t* const uint8_t* cacheBuffer = pixelBuffer->map(); for (uint32_t cacheY = startY, bitmapY = dstY * bitmapWidth; cacheY < endY; - cacheY += srcStride, bitmapY += bitmapWidth) { - + cacheY += srcStride, bitmapY += bitmapWidth) { for (uint32_t i = 0; i < glyph->mBitmapWidth; ++i) { uint8_t* dst = &(bitmap[bitmapY + dstX + i]); - const uint8_t& src = cacheBuffer[ - cacheY + (glyph->mStartX + i)*formatSize + alpha_channel_offset]; + const uint8_t& src = + cacheBuffer[cacheY + (glyph->mStartX + i) * formatSize + alpha_channel_offset]; // Add alpha values to a max of 255, full opacity. This is done to handle // fonts/strings where glyphs overlap. *dst = std::min(*dst + src, 255); @@ -235,7 +233,7 @@ void Font::drawCachedGlyphBitmap(CachedGlyphInfo* glyph, int x, int y, uint8_t* } void Font::drawCachedGlyph(CachedGlyphInfo* glyph, float x, float hOffset, float vOffset, - SkPathMeasure& measure, SkPoint* position, SkVector* tangent) { + SkPathMeasure& measure, SkPoint* position, SkVector* tangent) { const float halfWidth = glyph->mBitmapWidth * 0.5f; const float height = glyph->mBitmapHeight; @@ -249,13 +247,13 @@ void Font::drawCachedGlyph(CachedGlyphInfo* glyph, float x, float hOffset, float // Move along the tangent and offset by the normal destination[0].set(-tangent->fX * halfWidth - tangent->fY * vOffset, - -tangent->fY * halfWidth + tangent->fX * vOffset); + -tangent->fY * halfWidth + tangent->fX * vOffset); destination[1].set(tangent->fX * halfWidth - tangent->fY * vOffset, - tangent->fY * halfWidth + tangent->fX * vOffset); + tangent->fY * halfWidth + tangent->fX * vOffset); destination[2].set(destination[1].fX + tangent->fY * height, - destination[1].fY - tangent->fX * height); + destination[1].fY - tangent->fX * height); destination[3].set(destination[0].fX + tangent->fY * height, - destination[0].fY - tangent->fX * height); + destination[0].fY - tangent->fX * height); const float u1 = glyph->mBitmapMinU; const float u2 = glyph->mBitmapMaxU; @@ -263,14 +261,10 @@ void Font::drawCachedGlyph(CachedGlyphInfo* glyph, float x, float hOffset, float const float v2 = glyph->mBitmapMaxV; mState->appendRotatedMeshQuad( - position->x() + destination[0].x(), - position->y() + destination[0].y(), u1, v2, - position->x() + destination[1].x(), - position->y() + destination[1].y(), u2, v2, - position->x() + destination[2].x(), - position->y() + destination[2].y(), u2, v1, - position->x() + destination[3].x(), - position->y() + destination[3].y(), u1, v1, + position->x() + destination[0].x(), position->y() + destination[0].y(), u1, v2, + position->x() + destination[1].x(), position->y() + destination[1].y(), u2, v2, + position->x() + destination[2].x(), position->y() + destination[2].y(), u2, v1, + position->x() + destination[3].x(), position->y() + destination[3].y(), u1, v1, glyph->mCacheTexture); } @@ -280,7 +274,8 @@ CachedGlyphInfo* Font::getCachedGlyph(const SkPaint* paint, glyph_t textUnit, bo // Is the glyph still in texture cache? if (!cachedGlyph->mIsValid) { SkSurfaceProps surfaceProps(0, kUnknown_SkPixelGeometry); - SkAutoGlyphCacheNoGamma autoCache(*paint, &surfaceProps, &mDescription.mLookupTransform); + SkAutoGlyphCacheNoGamma autoCache(*paint, &surfaceProps, + &mDescription.mLookupTransform); const SkGlyph& skiaGlyph = GET_METRICS(autoCache.getCache(), textUnit); updateGlyphCache(paint, skiaGlyph, autoCache.getCache(), cachedGlyph, precaching); } @@ -291,14 +286,13 @@ CachedGlyphInfo* Font::getCachedGlyph(const SkPaint* paint, glyph_t textUnit, bo return cachedGlyph; } -void Font::render(const SkPaint* paint, const glyph_t* glyphs, - int numGlyphs, int x, int y, const float* positions) { - render(paint, glyphs, numGlyphs, x, y, FRAMEBUFFER, nullptr, - 0, 0, nullptr, positions); +void Font::render(const SkPaint* paint, const glyph_t* glyphs, int numGlyphs, int x, int y, + const float* positions) { + render(paint, glyphs, numGlyphs, x, y, FRAMEBUFFER, nullptr, 0, 0, nullptr, positions); } -void Font::render(const SkPaint* paint, const glyph_t* glyphs, int numGlyphs, - const SkPath* path, float hOffset, float vOffset) { +void Font::render(const SkPaint* paint, const glyph_t* glyphs, int numGlyphs, const SkPath* path, + float hOffset, float vOffset) { if (numGlyphs == 0 || glyphs == nullptr) { return; } @@ -345,8 +339,8 @@ void Font::render(const SkPaint* paint, const glyph_t* glyphs, int numGlyphs, } } -void Font::measure(const SkPaint* paint, const glyph_t* glyphs, - int numGlyphs, Rect *bounds, const float* positions) { +void Font::measure(const SkPaint* paint, const glyph_t* glyphs, int numGlyphs, Rect* bounds, + const float* positions) { if (bounds == nullptr) { ALOGE("No return rectangle provided to measure text"); return; @@ -374,21 +368,19 @@ void Font::precache(const SkPaint* paint, const glyph_t* glyphs, int numGlyphs) } } -void Font::render(const SkPaint* paint, const glyph_t* glyphs, - int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap, - uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* positions) { +void Font::render(const SkPaint* paint, const glyph_t* glyphs, int numGlyphs, int x, int y, + RenderMode mode, uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, + Rect* bounds, const float* positions) { if (numGlyphs == 0 || glyphs == nullptr) { return; } - static RenderGlyph gRenderGlyph[] = { - &android::uirenderer::Font::drawCachedGlyph, - &android::uirenderer::Font::drawCachedGlyphTransformed, - &android::uirenderer::Font::drawCachedGlyphBitmap, - &android::uirenderer::Font::drawCachedGlyphBitmap, - &android::uirenderer::Font::measureCachedGlyph, - &android::uirenderer::Font::measureCachedGlyph - }; + static RenderGlyph gRenderGlyph[] = {&android::uirenderer::Font::drawCachedGlyph, + &android::uirenderer::Font::drawCachedGlyphTransformed, + &android::uirenderer::Font::drawCachedGlyphBitmap, + &android::uirenderer::Font::drawCachedGlyphBitmap, + &android::uirenderer::Font::measureCachedGlyph, + &android::uirenderer::Font::measureCachedGlyph}; RenderGlyph render = gRenderGlyph[(mode << 1) + !mIdentityTransform]; int glyphsCount = 0; @@ -406,13 +398,12 @@ void Font::render(const SkPaint* paint, const glyph_t* glyphs, // If it's still not valid, we couldn't cache it, so we shouldn't // draw garbage; also skip empty glyphs (spaces) if (cachedGlyph->mIsValid && cachedGlyph->mCacheTexture) { - int penX = x + (int) roundf(positions[(glyphsCount << 1)]); - int penY = y + (int) roundf(positions[(glyphsCount << 1) + 1]); + int penX = x + (int)roundf(positions[(glyphsCount << 1)]); + int penY = y + (int)roundf(positions[(glyphsCount << 1) + 1]); #ifdef BUGREPORT_FONT_CACHE_USAGE mState->historyTracker().glyphRendered(cachedGlyph, penX, penY); #endif - (*this.*render)(cachedGlyph, penX, penY, - bitmap, bitmapW, bitmapH, bounds, positions); + (*this.*render)(cachedGlyph, penX, penY, bitmap, bitmapW, bitmapH, bounds, positions); } else { #ifdef BUGREPORT_FONT_CACHE_USAGE mState->historyTracker().glyphRendered(cachedGlyph, -1, -1); @@ -424,7 +415,7 @@ void Font::render(const SkPaint* paint, const glyph_t* glyphs, } void Font::updateGlyphCache(const SkPaint* paint, const SkGlyph& skiaGlyph, - SkGlyphCache* skiaGlyphCache, CachedGlyphInfo* glyph, bool precaching) { + SkGlyphCache* skiaGlyphCache, CachedGlyphInfo* glyph, bool precaching) { glyph->mAdvanceX = skiaGlyph.fAdvanceX; glyph->mAdvanceY = skiaGlyph.fAdvanceY; glyph->mBitmapLeft = skiaGlyph.fLeft; @@ -458,10 +449,10 @@ void Font::updateGlyphCache(const SkPaint* paint, const SkGlyph& skiaGlyph, uint32_t cacheWidth = glyph->mCacheTexture->getWidth(); uint32_t cacheHeight = glyph->mCacheTexture->getHeight(); - glyph->mBitmapMinU = startX / (float) cacheWidth; - glyph->mBitmapMinV = startY / (float) cacheHeight; - glyph->mBitmapMaxU = endX / (float) cacheWidth; - glyph->mBitmapMaxV = endY / (float) cacheHeight; + glyph->mBitmapMinU = startX / (float)cacheWidth; + glyph->mBitmapMinV = startY / (float)cacheHeight; + glyph->mBitmapMaxU = endX / (float)cacheWidth; + glyph->mBitmapMaxV = endY / (float)cacheHeight; mState->setTextureDirty(); } @@ -495,5 +486,5 @@ Font* Font::create(FontRenderer* state, const SkPaint* paint, const SkMatrix& ma return font; } -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/font/Font.h b/libs/hwui/font/Font.h index 504dabb5bcc0..85221bc069f1 100644 --- a/libs/hwui/font/Font.h +++ b/libs/hwui/font/Font.h @@ -21,14 +21,14 @@ #include <utils/KeyedVector.h> -#include <SkScalar.h> #include <SkPaint.h> #include <SkPathMeasure.h> +#include <SkScalar.h> #include <SkTypeface.h> -#include "FontUtil.h" -#include "../Rect.h" #include "../Matrix.h" +#include "../Rect.h" +#include "FontUtil.h" class SkGlyphCache; @@ -49,9 +49,7 @@ class FontRenderer; */ class Font { public: - enum Style { - kFakeBold = 1 - }; + enum Style { kFakeBold = 1 }; struct FontDescription { FontDescription(const SkPaint* paint, const SkMatrix& matrix); @@ -60,13 +58,9 @@ public: hash_t hash() const; - bool operator==(const FontDescription& other) const { - return compare(*this, other) == 0; - } + bool operator==(const FontDescription& other) const { return compare(*this, other) == 0; } - bool operator!=(const FontDescription& other) const { - return compare(*this, other) != 0; - } + bool operator!=(const FontDescription& other) const { return compare(*this, other) != 0; } SkFontID mFontId; float mFontSize; @@ -83,15 +77,13 @@ public: ~Font(); - void render(const SkPaint* paint, const glyph_t* glyphs, - int numGlyphs, int x, int y, const float* positions); + void render(const SkPaint* paint, const glyph_t* glyphs, int numGlyphs, int x, int y, + const float* positions); - void render(const SkPaint* paint, const glyph_t* glyphs, - int numGlyphs, const SkPath* path, float hOffset, float vOffset); + void render(const SkPaint* paint, const glyph_t* glyphs, int numGlyphs, const SkPath* path, + float hOffset, float vOffset); - const Font::FontDescription& getDescription() const { - return mDescription; - } + const Font::FontDescription& getDescription() const { return mDescription; } /** * Creates a new font associated with the specified font state. @@ -103,8 +95,8 @@ private: Font(FontRenderer* state, const Font::FontDescription& desc); - typedef void (Font::*RenderGlyph)(CachedGlyphInfo*, int, int, uint8_t*, - uint32_t, uint32_t, Rect*, const float*); + typedef void (Font::*RenderGlyph)(CachedGlyphInfo*, int, int, uint8_t*, uint32_t, uint32_t, + Rect*, const float*); enum RenderMode { FRAMEBUFFER, @@ -114,36 +106,33 @@ private: void precache(const SkPaint* paint, const glyph_t* glyphs, int numGlyphs); - void render(const SkPaint* paint, const glyph_t* glyphs, - int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap, - uint32_t bitmapW, uint32_t bitmapH, Rect *bounds, const float* positions); + void render(const SkPaint* paint, const glyph_t* glyphs, int numGlyphs, int x, int y, + RenderMode mode, uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, + const float* positions); - void measure(const SkPaint* paint, const glyph_t* glyphs, - int numGlyphs, Rect *bounds, const float* positions); + void measure(const SkPaint* paint, const glyph_t* glyphs, int numGlyphs, Rect* bounds, + const float* positions); void invalidateTextureCache(CacheTexture* cacheTexture = nullptr); CachedGlyphInfo* cacheGlyph(const SkPaint* paint, glyph_t glyph, bool precaching); void updateGlyphCache(const SkPaint* paint, const SkGlyph& skiaGlyph, - SkGlyphCache* skiaGlyphCache, CachedGlyphInfo* glyph, bool precaching); - - void measureCachedGlyph(CachedGlyphInfo* glyph, int x, int y, - uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH, - Rect* bounds, const float* pos); - void drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y, - uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH, - Rect* bounds, const float* pos); - void drawCachedGlyphTransformed(CachedGlyphInfo* glyph, int x, int y, - uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH, - Rect* bounds, const float* pos); - void drawCachedGlyphBitmap(CachedGlyphInfo* glyph, int x, int y, - uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH, - Rect* bounds, const float* pos); + SkGlyphCache* skiaGlyphCache, CachedGlyphInfo* glyph, bool precaching); + + void measureCachedGlyph(CachedGlyphInfo* glyph, int x, int y, uint8_t* bitmap, uint32_t bitmapW, + uint32_t bitmapH, Rect* bounds, const float* pos); + void drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y, uint8_t* bitmap, uint32_t bitmapW, + uint32_t bitmapH, Rect* bounds, const float* pos); + void drawCachedGlyphTransformed(CachedGlyphInfo* glyph, int x, int y, uint8_t* bitmap, + uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, + const float* pos); + void drawCachedGlyphBitmap(CachedGlyphInfo* glyph, int x, int y, uint8_t* bitmap, + uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos); void drawCachedGlyph(CachedGlyphInfo* glyph, float x, float hOffset, float vOffset, - SkPathMeasure& measure, SkPoint* position, SkVector* tangent); + SkPathMeasure& measure, SkPoint* position, SkVector* tangent); CachedGlyphInfo* getCachedGlyph(const SkPaint* paint, glyph_t textUnit, - bool precaching = false); + bool precaching = false); FontRenderer* mState; FontDescription mDescription; @@ -154,8 +143,7 @@ private: bool mIdentityTransform; }; -inline int strictly_order_type(const Font::FontDescription& lhs, - const Font::FontDescription& rhs) { +inline int strictly_order_type(const Font::FontDescription& lhs, const Font::FontDescription& rhs) { return Font::FontDescription::compare(lhs, rhs) < 0; } @@ -167,7 +155,7 @@ inline hash_t hash_type(const Font::FontDescription& entry) { return entry.hash(); } -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android -#endif // ANDROID_HWUI_FONT_H +#endif // ANDROID_HWUI_FONT_H diff --git a/libs/hwui/font/FontCacheHistoryTracker.cpp b/libs/hwui/font/FontCacheHistoryTracker.cpp index a2bfb27535e5..5b61c494524a 100644 --- a/libs/hwui/font/FontCacheHistoryTracker.cpp +++ b/libs/hwui/font/FontCacheHistoryTracker.cpp @@ -16,15 +16,15 @@ #include "FontCacheHistoryTracker.h" -#include "CachedGlyphInfo.h" #include "CacheTexture.h" +#include "CachedGlyphInfo.h" namespace android { namespace uirenderer { void FontCacheHistoryTracker::dumpCachedGlyph(String8& log, const CachedGlyph& glyph) { log.appendFormat("glyph (texture %p, position: (%d, %d), size: %dx%d, gen: %d)", glyph.texture, - glyph.startX, glyph.startY, glyph.bitmapW, glyph.bitmapH, glyph.generation); + glyph.startX, glyph.startY, glyph.bitmapW, glyph.bitmapH, glyph.generation); } void FontCacheHistoryTracker::dumpRenderEntry(String8& log, const RenderEntry& entry) { @@ -40,7 +40,7 @@ void FontCacheHistoryTracker::dumpRenderEntry(String8& log, const RenderEntry& e void FontCacheHistoryTracker::dumpUploadEntry(String8& log, const CachedGlyph& glyph) { if (glyph.bitmapW == 0 && glyph.bitmapH == 0) { log.appendFormat(" cleared cachetexture %p in gen %d\n", glyph.texture, - glyph.generation); + glyph.generation); } else { log.appendFormat(" uploaded "); dumpCachedGlyph(log, glyph); @@ -73,7 +73,7 @@ void FontCacheHistoryTracker::glyphRendered(CachedGlyphInfo* glyphInfo, int penX } void FontCacheHistoryTracker::glyphUploaded(CacheTexture* texture, uint32_t x, uint32_t y, - uint16_t glyphW, uint16_t glyphH) { + uint16_t glyphW, uint16_t glyphH) { CachedGlyph& glyph = mUploadHistory.next(); glyph.generation = generation; glyph.texture = texture; @@ -96,5 +96,5 @@ void FontCacheHistoryTracker::glyphsCleared(CacheTexture* texture) { void FontCacheHistoryTracker::frameCompleted() { generation++; } -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/font/FontCacheHistoryTracker.h b/libs/hwui/font/FontCacheHistoryTracker.h index f1d9b9f10dc0..4b9ceccc09c9 100644 --- a/libs/hwui/font/FontCacheHistoryTracker.h +++ b/libs/hwui/font/FontCacheHistoryTracker.h @@ -35,6 +35,7 @@ public: void frameCompleted(); void dump(String8& log) const; + private: struct CachedGlyph { void* texture; @@ -60,5 +61,5 @@ private: uint16_t generation = 0; }; -}; // namespace uirenderer -}; // namespace android
\ No newline at end of file +}; // namespace uirenderer +}; // namespace android
\ No newline at end of file diff --git a/libs/hwui/font/FontUtil.h b/libs/hwui/font/FontUtil.h index d4b4ff949f5a..596e153db6a9 100644 --- a/libs/hwui/font/FontUtil.h +++ b/libs/hwui/font/FontUtil.h @@ -26,11 +26,11 @@ /////////////////////////////////////////////////////////////////////////////// #ifdef TEXTURE_BORDER_SIZE - #if TEXTURE_BORDER_SIZE != 1 - #error TEXTURE_BORDER_SIZE other than 1 is not currently supported - #endif +#if TEXTURE_BORDER_SIZE != 1 +#error TEXTURE_BORDER_SIZE other than 1 is not currently supported +#endif #else - #define TEXTURE_BORDER_SIZE 1 +#define TEXTURE_BORDER_SIZE 1 #endif #define CACHE_BLOCK_ROUNDING_SIZE 4 @@ -43,4 +43,4 @@ typedef uint16_t glyph_t; // [-1, 1]. Result is an integral float. #define AUTO_KERN(prev, next) static_cast<float>(((next) - (prev) + 32) >> 6) -#endif // ANDROID_HWUI_FONT_UTIL_H +#endif // ANDROID_HWUI_FONT_UTIL_H diff --git a/libs/hwui/hwui/AnimatedImageDrawable.cpp b/libs/hwui/hwui/AnimatedImageDrawable.cpp new file mode 100644 index 000000000000..e01bf3d003e6 --- /dev/null +++ b/libs/hwui/hwui/AnimatedImageDrawable.cpp @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "AnimatedImageDrawable.h" + +#include "thread/Task.h" +#include "thread/TaskManager.h" +#include "thread/TaskProcessor.h" +#include "utils/TraceUtils.h" + +#include <SkPicture.h> +#include <SkRefCnt.h> +#include <SkTime.h> +#include <SkTLazy.h> + +namespace android { + +AnimatedImageDrawable::AnimatedImageDrawable(sk_sp<SkAnimatedImage> animatedImage) + : mSkAnimatedImage(std::move(animatedImage)) { } + +void AnimatedImageDrawable::syncProperties() { + mAlpha = mStagingAlpha; + mColorFilter = mStagingColorFilter; +} + +bool AnimatedImageDrawable::start() { + SkAutoExclusive lock(mLock); + if (mSkAnimatedImage->isRunning()) { + return false; + } + + if (!mSnapshot) { + mSnapshot.reset(mSkAnimatedImage->newPictureSnapshot()); + } + + // While stopped, update() does not decode, but it does advance the time. + // This prevents us from skipping ahead when we resume. + const double currentTime = SkTime::GetMSecs(); + mSkAnimatedImage->update(currentTime); + mSkAnimatedImage->start(); + return mSkAnimatedImage->isRunning(); +} + +void AnimatedImageDrawable::stop() { + SkAutoExclusive lock(mLock); + mSkAnimatedImage->stop(); +} + +bool AnimatedImageDrawable::isRunning() { + return mSkAnimatedImage->isRunning(); +} + +// This is really a Task<void> but that doesn't really work when Future<> +// expects to be able to get/set a value +class AnimatedImageDrawable::AnimatedImageTask : public uirenderer::Task<bool> { +public: + AnimatedImageTask(AnimatedImageDrawable* animatedImageDrawable) + : mAnimatedImageDrawable(sk_ref_sp(animatedImageDrawable)) {} + + sk_sp<AnimatedImageDrawable> mAnimatedImageDrawable; + bool mIsCompleted = false; +}; + +class AnimatedImageDrawable::AnimatedImageTaskProcessor : public uirenderer::TaskProcessor<bool> { +public: + explicit AnimatedImageTaskProcessor(uirenderer::TaskManager* taskManager) + : uirenderer::TaskProcessor<bool>(taskManager) {} + ~AnimatedImageTaskProcessor() {} + + virtual void onProcess(const sp<uirenderer::Task<bool>>& task) override { + ATRACE_NAME("Updating AnimatedImageDrawables"); + AnimatedImageTask* t = static_cast<AnimatedImageTask*>(task.get()); + t->mAnimatedImageDrawable->update(); + t->mIsCompleted = true; + task->setResult(true); + }; +}; + +void AnimatedImageDrawable::scheduleUpdate(uirenderer::TaskManager* taskManager) { + if (!mSkAnimatedImage->isRunning() + || (mDecodeTask.get() != nullptr && !mDecodeTask->mIsCompleted)) { + return; + } + + if (!mDecodeTaskProcessor.get()) { + mDecodeTaskProcessor = new AnimatedImageTaskProcessor(taskManager); + } + + // TODO get one frame ahead and only schedule updates when you need to replenish + mDecodeTask = new AnimatedImageTask(this); + mDecodeTaskProcessor->add(mDecodeTask); +} + +void AnimatedImageDrawable::update() { + SkAutoExclusive lock(mLock); + + if (!mSkAnimatedImage->isRunning()) { + return; + } + + const double currentTime = SkTime::GetMSecs(); + if (currentTime >= mNextFrameTime) { + mNextFrameTime = mSkAnimatedImage->update(currentTime); + mSnapshot.reset(mSkAnimatedImage->newPictureSnapshot()); + mIsDirty = true; + } +} + +void AnimatedImageDrawable::onDraw(SkCanvas* canvas) { + SkTLazy<SkPaint> lazyPaint; + if (mAlpha != SK_AlphaOPAQUE || mColorFilter.get()) { + lazyPaint.init(); + lazyPaint.get()->setAlpha(mAlpha); + lazyPaint.get()->setColorFilter(mColorFilter); + lazyPaint.get()->setFilterQuality(kLow_SkFilterQuality); + } + + SkAutoExclusive lock(mLock); + if (mSnapshot) { + canvas->drawPicture(mSnapshot, nullptr, lazyPaint.getMaybeNull()); + } else { + // TODO: we could potentially keep the cached surface around if there is a paint and we know + // the drawable is attached to the view system + SkAutoCanvasRestore acr(canvas, false); + if (lazyPaint.isValid()) { + canvas->saveLayer(mSkAnimatedImage->getBounds(), lazyPaint.get()); + } + mSkAnimatedImage->draw(canvas); + } + + mIsDirty = false; +} + +double AnimatedImageDrawable::drawStaging(SkCanvas* canvas) { + // update the drawable with the current time + double nextUpdate = mSkAnimatedImage->update(SkTime::GetMSecs()); + SkAutoCanvasRestore acr(canvas, false); + if (mStagingAlpha != SK_AlphaOPAQUE || mStagingColorFilter.get()) { + SkPaint paint; + paint.setAlpha(mStagingAlpha); + paint.setColorFilter(mStagingColorFilter); + canvas->saveLayer(mSkAnimatedImage->getBounds(), &paint); + } + canvas->drawDrawable(mSkAnimatedImage.get()); + return nextUpdate; +} + +}; // namespace android diff --git a/libs/hwui/hwui/AnimatedImageDrawable.h b/libs/hwui/hwui/AnimatedImageDrawable.h new file mode 100644 index 000000000000..1ebb58561f4d --- /dev/null +++ b/libs/hwui/hwui/AnimatedImageDrawable.h @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <cutils/compiler.h> +#include <utils/RefBase.h> + +#include <SkAnimatedImage.h> +#include <SkCanvas.h> +#include <SkColorFilter.h> +#include <SkDrawable.h> +#include <SkMutex.h> + +class SkPicture; + +namespace android { + +namespace uirenderer { +class TaskManager; +} + +/** + * Native component of android.graphics.drawable.AnimatedImageDrawables.java. This class can be + * drawn into Canvas.h and maintains the state needed to drive the animation from the RenderThread. + */ +class ANDROID_API AnimatedImageDrawable : public SkDrawable { +public: + AnimatedImageDrawable(sk_sp<SkAnimatedImage> animatedImage); + + /** + * This returns true if the animation has updated and signals that the next draw will contain + * new content. + */ + bool isDirty() const { return mIsDirty; } + + int getStagingAlpha() const { return mStagingAlpha; } + void setStagingAlpha(int alpha) { mStagingAlpha = alpha; } + void setStagingColorFilter(sk_sp<SkColorFilter> filter) { mStagingColorFilter = filter; } + void syncProperties(); + + virtual SkRect onGetBounds() override { + return mSkAnimatedImage->getBounds(); + } + + double drawStaging(SkCanvas* canvas); + + // Returns true if the animation was started; false otherwise (e.g. it was already running) + bool start(); + void stop(); + bool isRunning(); + + void scheduleUpdate(uirenderer::TaskManager* taskManager); + +protected: + virtual void onDraw(SkCanvas* canvas) override; + +private: + void update(); + + sk_sp<SkAnimatedImage> mSkAnimatedImage; + sk_sp<SkPicture> mSnapshot; + SkMutex mLock; + + int mStagingAlpha = SK_AlphaOPAQUE; + sk_sp<SkColorFilter> mStagingColorFilter; + + int mAlpha = SK_AlphaOPAQUE; + sk_sp<SkColorFilter> mColorFilter; + double mNextFrameTime = 0.0; + bool mIsDirty = false; + + class AnimatedImageTask; + class AnimatedImageTaskProcessor; + sp<AnimatedImageTask> mDecodeTask; + sp<AnimatedImageTaskProcessor> mDecodeTaskProcessor; +}; + +}; // namespace android diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp index 75b6d233643d..795ec5bd4f76 100644 --- a/libs/hwui/hwui/Bitmap.cpp +++ b/libs/hwui/hwui/Bitmap.cpp @@ -17,37 +17,37 @@ #include "Caches.h" #include "renderthread/EglManager.h" -#include "renderthread/RenderThread.h" #include "renderthread/RenderProxy.h" +#include "renderthread/RenderThread.h" #include "utils/Color.h" #include <sys/mman.h> -#include <log/log.h> #include <cutils/ashmem.h> +#include <log/log.h> -#include <private/gui/ComposerService.h> #include <binder/IServiceManager.h> +#include <private/gui/ComposerService.h> #include <ui/PixelFormat.h> #include <SkCanvas.h> #include <SkImagePriv.h> +#include <SkToSRGBColorFilter.h> namespace android { static bool computeAllocationSize(size_t rowBytes, int height, size_t* size) { int32_t rowBytes32 = SkToS32(rowBytes); - int64_t bigSize = (int64_t) height * rowBytes32; + int64_t bigSize = (int64_t)height * rowBytes32; if (rowBytes32 < 0 || !sk_64_isS32(bigSize)) { - return false; // allocation will be too large + return false; // allocation will be too large } *size = sk_64_asS32(bigSize); return true; } -typedef sk_sp<Bitmap> (*AllocPixelRef)(size_t allocSize, const SkImageInfo& info, - size_t rowBytes); +typedef sk_sp<Bitmap> (*AllocPixelRef)(size_t allocSize, const SkImageInfo& info, size_t rowBytes); static sk_sp<Bitmap> allocateBitmap(SkBitmap* bitmap, AllocPixelRef alloc) { const SkImageInfo& info = bitmap->info(); @@ -73,7 +73,7 @@ static sk_sp<Bitmap> allocateBitmap(SkBitmap* bitmap, AllocPixelRef alloc) { } sk_sp<Bitmap> Bitmap::allocateAshmemBitmap(SkBitmap* bitmap) { - return allocateBitmap(bitmap, &Bitmap::allocateAshmemBitmap); + return allocateBitmap(bitmap, &Bitmap::allocateAshmemBitmap); } static sk_sp<Bitmap> allocateHeapBitmap(size_t size, const SkImageInfo& info, size_t rowBytes) { @@ -89,7 +89,7 @@ sk_sp<Bitmap> Bitmap::allocateHardwareBitmap(SkBitmap& bitmap) { } sk_sp<Bitmap> Bitmap::allocateHeapBitmap(SkBitmap* bitmap) { - return allocateBitmap(bitmap, &android::allocateHeapBitmap); + return allocateBitmap(bitmap, &android::allocateHeapBitmap); } sk_sp<Bitmap> Bitmap::allocateHeapBitmap(const SkImageInfo& info) { @@ -101,8 +101,7 @@ sk_sp<Bitmap> Bitmap::allocateHeapBitmap(const SkImageInfo& info) { return android::allocateHeapBitmap(size, info, info.minRowBytes()); } -sk_sp<Bitmap> Bitmap::allocateAshmemBitmap(size_t size, const SkImageInfo& info, - size_t rowBytes) { +sk_sp<Bitmap> Bitmap::allocateAshmemBitmap(size_t size, const SkImageInfo& info, size_t rowBytes) { // Create new ashmem region with read/write priv int fd = ashmem_create_region("bitmap", size); if (fd < 0) { @@ -124,25 +123,25 @@ sk_sp<Bitmap> Bitmap::allocateAshmemBitmap(size_t size, const SkImageInfo& info, } void FreePixelRef(void* addr, void* context) { - auto pixelRef = (SkPixelRef*) context; + auto pixelRef = (SkPixelRef*)context; pixelRef->unref(); } sk_sp<Bitmap> Bitmap::createFrom(const SkImageInfo& info, SkPixelRef& pixelRef) { pixelRef.ref(); - return sk_sp<Bitmap>(new Bitmap((void*) pixelRef.pixels(), (void*) &pixelRef, FreePixelRef, - info, pixelRef.rowBytes())); + return sk_sp<Bitmap>(new Bitmap((void*)pixelRef.pixels(), (void*)&pixelRef, FreePixelRef, info, + pixelRef.rowBytes())); } sk_sp<Bitmap> Bitmap::createFrom(sp<GraphicBuffer> graphicBuffer) { PixelFormat format = graphicBuffer->getPixelFormat(); if (!graphicBuffer.get() || - (format != PIXEL_FORMAT_RGBA_8888 && format != PIXEL_FORMAT_RGBA_FP16)) { + (format != PIXEL_FORMAT_RGBA_8888 && format != PIXEL_FORMAT_RGBA_FP16)) { return nullptr; } SkImageInfo info = SkImageInfo::Make(graphicBuffer->getWidth(), graphicBuffer->getHeight(), - kRGBA_8888_SkColorType, kPremul_SkAlphaType, - SkColorSpace::MakeSRGB()); + kRGBA_8888_SkColorType, kPremul_SkAlphaType, + SkColorSpace::MakeSRGB()); return sk_sp<Bitmap>(new Bitmap(graphicBuffer.get(), info)); } @@ -154,8 +153,8 @@ static SkImageInfo validateAlpha(const SkImageInfo& info) { // Need to validate the alpha type to filter against the color type // to prevent things like a non-opaque RGB565 bitmap SkAlphaType alphaType; - LOG_ALWAYS_FATAL_IF(!SkColorTypeValidateAlphaType( - info.colorType(), info.alphaType(), &alphaType), + LOG_ALWAYS_FATAL_IF( + !SkColorTypeValidateAlphaType(info.colorType(), info.alphaType(), &alphaType), "Failed to validate alpha type!"); return info.makeAlphaType(alphaType); } @@ -172,28 +171,27 @@ void Bitmap::reconfigure(const SkImageInfo& newInfo, size_t rowBytes) { } Bitmap::Bitmap(void* address, size_t size, const SkImageInfo& info, size_t rowBytes) - : SkPixelRef(info.width(), info.height(), address, rowBytes) - , mInfo(validateAlpha(info)) - , mPixelStorageType(PixelStorageType::Heap) { + : SkPixelRef(info.width(), info.height(), address, rowBytes) + , mInfo(validateAlpha(info)) + , mPixelStorageType(PixelStorageType::Heap) { mPixelStorage.heap.address = address; mPixelStorage.heap.size = size; } -Bitmap::Bitmap(void* address, void* context, FreeFunc freeFunc, - const SkImageInfo& info, size_t rowBytes) - : SkPixelRef(info.width(), info.height(), address, rowBytes) - , mInfo(validateAlpha(info)) - , mPixelStorageType(PixelStorageType::External) { +Bitmap::Bitmap(void* address, void* context, FreeFunc freeFunc, const SkImageInfo& info, + size_t rowBytes) + : SkPixelRef(info.width(), info.height(), address, rowBytes) + , mInfo(validateAlpha(info)) + , mPixelStorageType(PixelStorageType::External) { mPixelStorage.external.address = address; mPixelStorage.external.context = context; mPixelStorage.external.freeFunc = freeFunc; } -Bitmap::Bitmap(void* address, int fd, size_t mappedSize, - const SkImageInfo& info, size_t rowBytes) - : SkPixelRef(info.width(), info.height(), address, rowBytes) - , mInfo(validateAlpha(info)) - , mPixelStorageType(PixelStorageType::Ashmem) { +Bitmap::Bitmap(void* address, int fd, size_t mappedSize, const SkImageInfo& info, size_t rowBytes) + : SkPixelRef(info.width(), info.height(), address, rowBytes) + , mInfo(validateAlpha(info)) + , mPixelStorageType(PixelStorageType::Ashmem) { mPixelStorage.ashmem.address = address; mPixelStorage.ashmem.fd = fd; mPixelStorage.ashmem.size = mappedSize; @@ -206,35 +204,31 @@ Bitmap::Bitmap(GraphicBuffer* buffer, const SkImageInfo& info) , mPixelStorageType(PixelStorageType::Hardware) { mPixelStorage.hardware.buffer = buffer; buffer->incStrong(buffer); - setImmutable(); // HW bitmaps are always immutable + setImmutable(); // HW bitmaps are always immutable if (uirenderer::Properties::isSkiaEnabled()) { - // GraphicBuffer should be in the display color space (Bitmap::createFrom is always - // passing SRGB). The code that uploads into a GraphicBuffer should do color conversion if - // needed. mImage = SkImage::MakeFromAHardwareBuffer(reinterpret_cast<AHardwareBuffer*>(buffer), - mInfo.alphaType(), nullptr); + mInfo.alphaType(), mInfo.refColorSpace()); } } Bitmap::~Bitmap() { switch (mPixelStorageType) { - case PixelStorageType::External: - mPixelStorage.external.freeFunc(mPixelStorage.external.address, - mPixelStorage.external.context); - break; - case PixelStorageType::Ashmem: - munmap(mPixelStorage.ashmem.address, mPixelStorage.ashmem.size); - close(mPixelStorage.ashmem.fd); - break; - case PixelStorageType::Heap: - free(mPixelStorage.heap.address); - break; - case PixelStorageType::Hardware: - auto buffer = mPixelStorage.hardware.buffer; - buffer->decStrong(buffer); - mPixelStorage.hardware.buffer = nullptr; - break; - + case PixelStorageType::External: + mPixelStorage.external.freeFunc(mPixelStorage.external.address, + mPixelStorage.external.context); + break; + case PixelStorageType::Ashmem: + munmap(mPixelStorage.ashmem.address, mPixelStorage.ashmem.size); + close(mPixelStorage.ashmem.fd); + break; + case PixelStorageType::Heap: + free(mPixelStorage.heap.address); + break; + case PixelStorageType::Hardware: + auto buffer = mPixelStorage.hardware.buffer; + buffer->decStrong(buffer); + mPixelStorage.hardware.buffer = nullptr; + break; } android::uirenderer::renderthread::RenderProxy::onBitmapDestroyed(getStableID()); @@ -250,32 +244,32 @@ void Bitmap::setHasHardwareMipMap(bool hasMipMap) { void* Bitmap::getStorage() const { switch (mPixelStorageType) { - case PixelStorageType::External: - return mPixelStorage.external.address; - case PixelStorageType::Ashmem: - return mPixelStorage.ashmem.address; - case PixelStorageType::Heap: - return mPixelStorage.heap.address; - case PixelStorageType::Hardware: - return nullptr; + case PixelStorageType::External: + return mPixelStorage.external.address; + case PixelStorageType::Ashmem: + return mPixelStorage.ashmem.address; + case PixelStorageType::Heap: + return mPixelStorage.heap.address; + case PixelStorageType::Hardware: + return nullptr; } } int Bitmap::getAshmemFd() const { switch (mPixelStorageType) { - case PixelStorageType::Ashmem: - return mPixelStorage.ashmem.fd; - default: - return -1; + case PixelStorageType::Ashmem: + return mPixelStorage.ashmem.fd; + default: + return -1; } } size_t Bitmap::getAllocationByteCount() const { switch (mPixelStorageType) { - case PixelStorageType::Heap: - return mPixelStorage.heap.size; - default: - return rowBytes() * height(); + case PixelStorageType::Heap: + return mPixelStorage.heap.size; + default: + return rowBytes() * height(); } } @@ -296,7 +290,8 @@ void Bitmap::getSkBitmap(SkBitmap* outBitmap) { if (isHardware()) { if (uirenderer::Properties::isSkiaEnabled()) { outBitmap->allocPixels(SkImageInfo::Make(info().width(), info().height(), - info().colorType(), info().alphaType(), nullptr)); + info().colorType(), info().alphaType(), + nullptr)); } else { outBitmap->allocPixels(info()); } @@ -319,7 +314,7 @@ GraphicBuffer* Bitmap::graphicBuffer() { return nullptr; } -sk_sp<SkImage> Bitmap::makeImage() { +sk_sp<SkImage> Bitmap::makeImage(sk_sp<SkColorFilter>* outputColorFilter) { sk_sp<SkImage> image = mImage; if (!image) { SkASSERT(!(isHardware() && uirenderer::Properties::isSkiaEnabled())); @@ -330,14 +325,13 @@ sk_sp<SkImage> Bitmap::makeImage() { // Note we don't cache in this case, because the raster image holds a pointer to this Bitmap // internally and ~Bitmap won't be invoked. // TODO: refactor Bitmap to not derive from SkPixelRef, which would allow caching here. - if (uirenderer::Properties::isSkiaEnabled()) { - image = SkMakeImageInColorSpace(skiaBitmap, SkColorSpace::MakeSRGB(), - skiaBitmap.getGenerationID(), kNever_SkCopyPixelsMode); - } else { - image = SkMakeImageFromRasterBitmap(skiaBitmap, kNever_SkCopyPixelsMode); - } + image = SkMakeImageFromRasterBitmap(skiaBitmap, kNever_SkCopyPixelsMode); + } + if (uirenderer::Properties::isSkiaEnabled() && image->colorSpace() != nullptr && + !image->colorSpace()->isSRGB()) { + *outputColorFilter = SkToSRGBColorFilter::Make(image->refColorSpace()); } return image; } -} // namespace android +} // namespace android diff --git a/libs/hwui/hwui/Bitmap.h b/libs/hwui/hwui/Bitmap.h index 634e76450c38..a75276fc2d0e 100644 --- a/libs/hwui/hwui/Bitmap.h +++ b/libs/hwui/hwui/Bitmap.h @@ -16,13 +16,14 @@ #pragma once #include <SkBitmap.h> +#include <SkColorFilter.h> #include <SkColorSpace.h> #include <SkImage.h> +#include <SkImage.h> #include <SkImageInfo.h> #include <SkPixelRef.h> #include <cutils/compiler.h> #include <ui/GraphicBuffer.h> -#include <SkImage.h> namespace android { @@ -35,7 +36,7 @@ enum class PixelStorageType { namespace uirenderer { namespace renderthread { - class RenderThread; +class RenderThread; } } @@ -52,17 +53,16 @@ public: static sk_sp<Bitmap> allocateAshmemBitmap(SkBitmap* bitmap); static sk_sp<Bitmap> allocateAshmemBitmap(size_t allocSize, const SkImageInfo& info, - size_t rowBytes); + size_t rowBytes); static sk_sp<Bitmap> createFrom(sp<GraphicBuffer> graphicBuffer); static sk_sp<Bitmap> createFrom(const SkImageInfo&, SkPixelRef&); Bitmap(void* address, size_t allocSize, const SkImageInfo& info, size_t rowBytes); - Bitmap(void* address, void* context, FreeFunc freeFunc, - const SkImageInfo& info, size_t rowBytes); - Bitmap(void* address, int fd, size_t mappedSize, const SkImageInfo& info, - size_t rowBytes); + Bitmap(void* address, void* context, FreeFunc freeFunc, const SkImageInfo& info, + size_t rowBytes); + Bitmap(void* address, int fd, size_t mappedSize, const SkImageInfo& info, size_t rowBytes); Bitmap(GraphicBuffer* buffer, const SkImageInfo& info); int rowBytesAsPixels() const { @@ -84,21 +84,27 @@ public: bool isOpaque() const { return mInfo.isOpaque(); } SkColorType colorType() const { return mInfo.colorType(); } - const SkImageInfo& info() const { - return mInfo; - } + const SkImageInfo& info() const { return mInfo; } void getBounds(SkRect* bounds) const; - bool isHardware() const { - return mPixelStorageType == PixelStorageType::Hardware; - } + bool isHardware() const { return mPixelStorageType == PixelStorageType::Hardware; } GraphicBuffer* graphicBuffer(); - // makeImage creates or returns a cached SkImage. Can be invoked from UI or render thread. - // Caching is supported only for HW Bitmaps with skia pipeline. - sk_sp<SkImage> makeImage(); + /** + * Creates or returns a cached SkImage and is safe to be invoked from either + * the UI or RenderThread. + * + * @param outputColorFilter is a required param that will be populated by + * this function if the bitmap's colorspace is not sRGB. If populated the + * filter will convert colors from the bitmaps colorspace into sRGB. It + * is the callers responsibility to use this colorFilter when drawing + * this image into any destination that is presumed to be sRGB (i.e. a + * buffer that has no colorspace defined). + */ + sk_sp<SkImage> makeImage(sk_sp<SkColorFilter>* outputColorFilter); + private: virtual ~Bitmap(); void* getStorage() const; @@ -129,7 +135,7 @@ private: } hardware; } mPixelStorage; - sk_sp<SkImage> mImage; // Cache is used only for HW Bitmaps with Skia pipeline. + sk_sp<SkImage> mImage; // Cache is used only for HW Bitmaps with Skia pipeline. }; -} //namespace android +} // namespace android diff --git a/libs/hwui/hwui/Canvas.cpp b/libs/hwui/hwui/Canvas.cpp index e754daf7c42e..284fd836745c 100644 --- a/libs/hwui/hwui/Canvas.cpp +++ b/libs/hwui/hwui/Canvas.cpp @@ -16,13 +16,13 @@ #include "Canvas.h" -#include "RecordingCanvas.h" -#include "RenderNode.h" #include "MinikinUtils.h" #include "Paint.h" #include "Properties.h" -#include "pipeline/skia/SkiaRecordingCanvas.h" +#include "RecordingCanvas.h" +#include "RenderNode.h" #include "Typeface.h" +#include "pipeline/skia/SkiaRecordingCanvas.h" #include <SkDrawFilter.h> @@ -36,7 +36,7 @@ Canvas* Canvas::create_recording_canvas(int width, int height, uirenderer::Rende } static inline void drawStroke(SkScalar left, SkScalar right, SkScalar top, SkScalar thickness, - const SkPaint& paint, Canvas* canvas) { + const SkPaint& paint, Canvas* canvas) { const SkScalar strokeWidth = fmax(thickness, 1.0f); const SkScalar bottom = top + strokeWidth; canvas->drawRect(left, top, right, bottom, paint); @@ -91,20 +91,18 @@ static void simplifyPaint(int color, SkPaint* paint) { class DrawTextFunctor { public: - DrawTextFunctor(const minikin::Layout& layout, Canvas* canvas, - const SkPaint& paint, float x, float y, minikin::MinikinRect& bounds, - float totalAdvance) - : layout(layout) - , canvas(canvas) - , paint(paint) - , x(x) - , y(y) - , bounds(bounds) - , totalAdvance(totalAdvance) { - } + DrawTextFunctor(const minikin::Layout& layout, Canvas* canvas, const SkPaint& paint, float x, + float y, minikin::MinikinRect& bounds, float totalAdvance) + : layout(layout) + , canvas(canvas) + , paint(paint) + , x(x) + , y(y) + , bounds(bounds) + , totalAdvance(totalAdvance) {} void operator()(size_t start, size_t end) { - auto glyphFunc = [&] (uint16_t* text, float* positions) { + auto glyphFunc = [&](uint16_t* text, float* positions) { if (canvas->drawTextAbsolutePos()) { for (size_t i = start, textIndex = 0, posIndex = 0; i < end; i++) { text[textIndex++] = layout.getGlyphId(i); @@ -133,20 +131,21 @@ public: simplifyPaint(darken ? SK_ColorWHITE : SK_ColorBLACK, &outlinePaint); outlinePaint.setStyle(SkPaint::kStrokeAndFill_Style); canvas->drawGlyphs(glyphFunc, glyphCount, outlinePaint, x, y, bounds.mLeft, bounds.mTop, - bounds.mRight, bounds.mBottom, totalAdvance); + bounds.mRight, bounds.mBottom, totalAdvance); // inner SkPaint innerPaint(paint); simplifyPaint(darken ? SK_ColorBLACK : SK_ColorWHITE, &innerPaint); innerPaint.setStyle(SkPaint::kFill_Style); canvas->drawGlyphs(glyphFunc, glyphCount, innerPaint, x, y, bounds.mLeft, bounds.mTop, - bounds.mRight, bounds.mBottom, totalAdvance); + bounds.mRight, bounds.mBottom, totalAdvance); } else { // standard draw path canvas->drawGlyphs(glyphFunc, glyphCount, paint, x, y, bounds.mLeft, bounds.mTop, - bounds.mRight, bounds.mBottom, totalAdvance); + bounds.mRight, bounds.mBottom, totalAdvance); } } + private: const minikin::Layout& layout; Canvas* canvas; @@ -157,13 +156,15 @@ private: float totalAdvance; }; -void Canvas::drawText(const uint16_t* text, int start, int count, int contextCount, - float x, float y, int bidiFlags, const Paint& origPaint, Typeface* typeface) { +void Canvas::drawText(const uint16_t* text, int start, int count, int contextCount, float x, + float y, minikin::Bidi bidiFlags, const Paint& origPaint, + const Typeface* typeface, minikin::MeasuredText* mt, int mtOffset) { // minikin may modify the original paint Paint paint(origPaint); - minikin::Layout layout = MinikinUtils::doLayout( - &paint, bidiFlags, typeface, text, start, count, contextCount); + minikin::Layout layout = + MinikinUtils::doLayout(&paint, bidiFlags, typeface, text, start, count, contextCount, + mt, mtOffset); x += MinikinUtils::xOffsetForTextAlign(&paint, layout); @@ -185,18 +186,18 @@ void Canvas::drawText(const uint16_t* text, int start, int count, int contextCou class DrawTextOnPathFunctor { public: DrawTextOnPathFunctor(const minikin::Layout& layout, Canvas* canvas, float hOffset, - float vOffset, const Paint& paint, const SkPath& path) - : layout(layout) - , canvas(canvas) - , hOffset(hOffset) - , vOffset(vOffset) - , paint(paint) - , path(path) { - } + float vOffset, const Paint& paint, const SkPath& path) + : layout(layout) + , canvas(canvas) + , hOffset(hOffset) + , vOffset(vOffset) + , paint(paint) + , path(path) {} void operator()(size_t start, size_t end) { canvas->drawLayoutOnPath(layout, hOffset, vOffset, paint, path, start, end); } + private: const minikin::Layout& layout; Canvas* canvas; @@ -206,11 +207,13 @@ private: const SkPath& path; }; -void Canvas::drawTextOnPath(const uint16_t* text, int count, int bidiFlags, const SkPath& path, - float hOffset, float vOffset, const Paint& paint, Typeface* typeface) { +void Canvas::drawTextOnPath(const uint16_t* text, int count, minikin::Bidi bidiFlags, + const SkPath& path, float hOffset, float vOffset, const Paint& paint, + const Typeface* typeface) { Paint paintCopy(paint); - minikin::Layout layout = MinikinUtils::doLayout( - &paintCopy, bidiFlags, typeface, text, 0, count, count); + minikin::Layout layout = + MinikinUtils::doLayout(&paintCopy, bidiFlags, typeface, text, 0, count, count, nullptr, + 0); hOffset += MinikinUtils::hOffsetForTextAlign(&paintCopy, layout, path); // Set align to left for drawing, as we don't want individual @@ -222,4 +225,4 @@ void Canvas::drawTextOnPath(const uint16_t* text, int count, int bidiFlags, cons MinikinUtils::forFontRun(layout, &paintCopy, f); } -} // namespace android +} // namespace android diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h index ac8a08169997..3ddf1c48b83f 100644 --- a/libs/hwui/hwui/Canvas.h +++ b/libs/hwui/hwui/Canvas.h @@ -19,46 +19,50 @@ #include <cutils/compiler.h> #include <utils/Functor.h> +#include <androidfw/ResourceTypes.h> #include "GlFunctorLifecycleListener.h" +#include "Properties.h" #include "utils/Macros.h" -#include <androidfw/ResourceTypes.h> #include <SkBitmap.h> #include <SkCanvas.h> #include <SkMatrix.h> +class SkAnimatedImage; class SkCanvasState; class SkVertices; namespace minikin { - class Layout; +class Layout; +class MeasuredText; +enum class Bidi : uint8_t; } namespace android { namespace uirenderer { - class CanvasPropertyPaint; - class CanvasPropertyPrimitive; - class DeferredLayerUpdater; - class DisplayList; - class RenderNode; +class CanvasPropertyPaint; +class CanvasPropertyPrimitive; +class DeferredLayerUpdater; +class DisplayList; +class RenderNode; } namespace SaveFlags { // These must match the corresponding Canvas API constants. enum { - Matrix = 0x01, - Clip = 0x02, + Matrix = 0x01, + Clip = 0x02, HasAlphaLayer = 0x04, - ClipToLayer = 0x10, + ClipToLayer = 0x10, // Helper constant - MatrixClip = Matrix | Clip, + MatrixClip = Matrix | Clip, }; typedef uint32_t Flags; -} // namespace SaveFlags +} // namespace SaveFlags namespace uirenderer { class SkiaCanvasProxy; @@ -70,13 +74,14 @@ typedef uirenderer::VectorDrawable::Tree VectorDrawableRoot; typedef std::function<void(uint16_t* text, float* positions)> ReadGlyphFunc; +class AnimatedImageDrawable; class Bitmap; class Paint; struct Typeface; class ANDROID_API Canvas { public: - virtual ~Canvas() {}; + virtual ~Canvas(){}; static Canvas* create_canvas(const SkBitmap& bitmap); @@ -95,17 +100,8 @@ public: determined based on Properties::getRenderPipelineType(). * */ - static WARN_UNUSED_RESULT Canvas* create_recording_canvas(int width, int height, - uirenderer::RenderNode* renderNode = nullptr); - - enum class XformToSRGB { - // Transform any Bitmaps to the sRGB color space before drawing. - kImmediate, - - // Draw the Bitmap as is. This likely means that we are recording and that the - // transform can be handled at playback time. - kDefer, - }; + static WARN_UNUSED_RESULT Canvas* create_recording_canvas( + int width, int height, uirenderer::RenderNode* renderNode = nullptr); /** * Create a new Canvas object which delegates to an SkCanvas. @@ -114,12 +110,10 @@ public: * delegated to this object. This function will call ref() on the * SkCanvas, and the returned Canvas will unref() it upon * destruction. - * @param xformToSRGB Indicates if bitmaps should be xformed to the sRGB - * color space before drawing. * @return new non-null Canvas Object. The type of DisplayList produced by this canvas is * determined based on Properties::getRenderPipelineType(). */ - static Canvas* create_canvas(SkCanvas* skiaCanvas, XformToSRGB xformToSRGB); + static Canvas* create_canvas(SkCanvas* skiaCanvas); /** * Provides a Skia SkCanvas interface that acts as a proxy to this Canvas. @@ -135,41 +129,43 @@ public: */ virtual SkCanvas* asSkCanvas() = 0; - virtual void setBitmap(const SkBitmap& bitmap) = 0; virtual bool isOpaque() = 0; virtual int width() = 0; virtual int height() = 0; -// ---------------------------------------------------------------------------- -// View System operations (not exposed in public Canvas API) -// ---------------------------------------------------------------------------- + // ---------------------------------------------------------------------------- + // View System operations (not exposed in public Canvas API) + // ---------------------------------------------------------------------------- virtual void resetRecording(int width, int height, - uirenderer::RenderNode* renderNode = nullptr) = 0; + uirenderer::RenderNode* renderNode = nullptr) = 0; virtual uirenderer::DisplayList* finishRecording() = 0; virtual void insertReorderBarrier(bool enableReorder) = 0; - virtual void setHighContrastText(bool highContrastText) = 0; - virtual bool isHighContrastText() = 0; + bool isHighContrastText() const { return uirenderer::Properties::enableHighContrastText; } virtual void drawRoundRect(uirenderer::CanvasPropertyPrimitive* left, - uirenderer::CanvasPropertyPrimitive* top, uirenderer::CanvasPropertyPrimitive* right, - uirenderer::CanvasPropertyPrimitive* bottom, uirenderer::CanvasPropertyPrimitive* rx, - uirenderer::CanvasPropertyPrimitive* ry, uirenderer::CanvasPropertyPaint* paint) = 0; + uirenderer::CanvasPropertyPrimitive* top, + uirenderer::CanvasPropertyPrimitive* right, + uirenderer::CanvasPropertyPrimitive* bottom, + uirenderer::CanvasPropertyPrimitive* rx, + uirenderer::CanvasPropertyPrimitive* ry, + uirenderer::CanvasPropertyPaint* paint) = 0; virtual void drawCircle(uirenderer::CanvasPropertyPrimitive* x, - uirenderer::CanvasPropertyPrimitive* y, uirenderer::CanvasPropertyPrimitive* radius, - uirenderer::CanvasPropertyPaint* paint) = 0; + uirenderer::CanvasPropertyPrimitive* y, + uirenderer::CanvasPropertyPrimitive* radius, + uirenderer::CanvasPropertyPaint* paint) = 0; virtual void drawLayer(uirenderer::DeferredLayerUpdater* layerHandle) = 0; virtual void drawRenderNode(uirenderer::RenderNode* renderNode) = 0; virtual void callDrawGLFunction(Functor* functor, - uirenderer::GlFunctorLifecycleListener* listener) = 0; + uirenderer::GlFunctorLifecycleListener* listener) = 0; -// ---------------------------------------------------------------------------- -// Canvas state operations -// ---------------------------------------------------------------------------- + // ---------------------------------------------------------------------------- + // Canvas state operations + // ---------------------------------------------------------------------------- // Save (layer) virtual int getSaveCount() const = 0; @@ -177,10 +173,10 @@ public: virtual void restore() = 0; virtual void restoreToCount(int saveCount) = 0; - virtual int saveLayer(float left, float top, float right, float bottom, - const SkPaint* paint, SaveFlags::Flags flags) = 0; - virtual int saveLayerAlpha(float left, float top, float right, float bottom, - int alpha, SaveFlags::Flags flags) = 0; + virtual int saveLayer(float left, float top, float right, float bottom, const SkPaint* paint, + SaveFlags::Flags flags) = 0; + virtual int saveLayerAlpha(float left, float top, float right, float bottom, int alpha, + SaveFlags::Flags flags) = 0; // Matrix virtual void getMatrix(SkMatrix* outMatrix) const = 0; @@ -197,8 +193,7 @@ public: virtual bool quickRejectRect(float left, float top, float right, float bottom) const = 0; virtual bool quickRejectPath(const SkPath& path) const = 0; - virtual bool clipRect(float left, float top, float right, float bottom, - SkClipOp op) = 0; + virtual bool clipRect(float left, float top, float right, float bottom, SkClipOp op) = 0; virtual bool clipPath(const SkPath* path, SkClipOp op) = 0; // filters @@ -208,9 +203,9 @@ public: // WebView only virtual SkCanvasState* captureCanvasState() const { return nullptr; } -// ---------------------------------------------------------------------------- -// Canvas draw operations -// ---------------------------------------------------------------------------- + // ---------------------------------------------------------------------------- + // Canvas draw operations + // ---------------------------------------------------------------------------- virtual void drawColor(int color, SkBlendMode mode) = 0; virtual void drawPaint(const SkPaint& paint) = 0; @@ -218,34 +213,34 @@ public: virtual void drawPoint(float x, float y, const SkPaint& paint) = 0; virtual void drawPoints(const float* points, int floatCount, const SkPaint& paint) = 0; virtual void drawLine(float startX, float startY, float stopX, float stopY, - const SkPaint& paint) = 0; + const SkPaint& paint) = 0; virtual void drawLines(const float* points, int floatCount, const SkPaint& paint) = 0; virtual void drawRect(float left, float top, float right, float bottom, - const SkPaint& paint) = 0; + const SkPaint& paint) = 0; virtual void drawRegion(const SkRegion& region, const SkPaint& paint) = 0; - virtual void drawRoundRect(float left, float top, float right, float bottom, - float rx, float ry, const SkPaint& paint) = 0; + virtual void drawRoundRect(float left, float top, float right, float bottom, float rx, float ry, + const SkPaint& paint) = 0; virtual void drawCircle(float x, float y, float radius, const SkPaint& paint) = 0; virtual void drawOval(float left, float top, float right, float bottom, - const SkPaint& paint) = 0; - virtual void drawArc(float left, float top, float right, float bottom, - float startAngle, float sweepAngle, bool useCenter, const SkPaint& paint) = 0; + const SkPaint& paint) = 0; + virtual void drawArc(float left, float top, float right, float bottom, float startAngle, + float sweepAngle, bool useCenter, const SkPaint& paint) = 0; virtual void drawPath(const SkPath& path, const SkPaint& paint) = 0; virtual void drawVertices(const SkVertices*, SkBlendMode, const SkPaint& paint) = 0; // Bitmap-based - virtual void drawBitmap(Bitmap& bitmap, float left, float top, - const SkPaint* paint) = 0; - virtual void drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, - const SkPaint* paint) = 0; - virtual void drawBitmap(Bitmap& bitmap, float srcLeft, float srcTop, - float srcRight, float srcBottom, float dstLeft, float dstTop, - float dstRight, float dstBottom, const SkPaint* paint) = 0; + virtual void drawBitmap(Bitmap& bitmap, float left, float top, const SkPaint* paint) = 0; + virtual void drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, const SkPaint* paint) = 0; + virtual void drawBitmap(Bitmap& bitmap, float srcLeft, float srcTop, float srcRight, + float srcBottom, float dstLeft, float dstTop, float dstRight, + float dstBottom, const SkPaint* paint) = 0; virtual void drawBitmapMesh(Bitmap& bitmap, int meshWidth, int meshHeight, - const float* vertices, const int* colors, const SkPaint* paint) = 0; - virtual void drawNinePatch(Bitmap& bitmap, const android::Res_png_9patch& chunk, - float dstLeft, float dstTop, float dstRight, float dstBottom, - const SkPaint* paint) = 0; + const float* vertices, const int* colors, const SkPaint* paint) = 0; + virtual void drawNinePatch(Bitmap& bitmap, const android::Res_png_9patch& chunk, float dstLeft, + float dstTop, float dstRight, float dstBottom, + const SkPaint* paint) = 0; + + virtual double drawAnimatedImage(AnimatedImageDrawable* imgDrawable) = 0; /** * Specifies if the positions passed to ::drawText are absolute or relative @@ -265,11 +260,13 @@ public: * Converts utf16 text to glyphs, calculating position and boundary, * and delegating the final draw to virtual drawGlyphs method. */ - void drawText(const uint16_t* text, int start, int count, int contextCount, - float x, float y, int bidiFlags, const Paint& origPaint, Typeface* typeface); + void drawText(const uint16_t* text, int start, int count, int contextCount, float x, float y, + minikin::Bidi bidiFlags, const Paint& origPaint, const Typeface* typeface, + minikin::MeasuredText* mt, int mtOffset); - void drawTextOnPath(const uint16_t* text, int count, int bidiFlags, const SkPath& path, - float hOffset, float vOffset, const Paint& paint, Typeface* typeface); + void drawTextOnPath(const uint16_t* text, int count, minikin::Bidi bidiFlags, + const SkPath& path, float hOffset, float vOffset, const Paint& paint, + const Typeface* typeface); protected: void drawTextDecorations(float x, float y, float length, const SkPaint& paint); @@ -280,13 +277,14 @@ protected: * totalAdvance: used to define width of text decorations (underlines, strikethroughs). */ virtual void drawGlyphs(ReadGlyphFunc glyphFunc, int count, const SkPaint& paint, float x, - float y, float boundsLeft, float boundsTop, float boundsRight, float boundsBottom, - float totalAdvance) = 0; + float y, float boundsLeft, float boundsTop, float boundsRight, + float boundsBottom, float totalAdvance) = 0; virtual void drawLayoutOnPath(const minikin::Layout& layout, float hOffset, float vOffset, - const SkPaint& paint, const SkPath& path, size_t start, size_t end) = 0; + const SkPaint& paint, const SkPath& path, size_t start, + size_t end) = 0; friend class DrawTextFunctor; friend class DrawTextOnPathFunctor; friend class uirenderer::SkiaCanvasProxy; }; -}; // namespace android +}; // namespace android diff --git a/libs/hwui/hwui/MinikinSkia.cpp b/libs/hwui/hwui/MinikinSkia.cpp index ba4e3a4df578..5d33860bab6b 100644 --- a/libs/hwui/hwui/MinikinSkia.cpp +++ b/libs/hwui/hwui/MinikinSkia.cpp @@ -16,37 +16,41 @@ #include "MinikinSkia.h" -#include <log/log.h> #include <SkFontDescriptor.h> #include <SkFontMgr.h> #include <SkPaint.h> #include <SkTypeface.h> +#include <log/log.h> namespace android { MinikinFontSkia::MinikinFontSkia(sk_sp<SkTypeface> typeface, const void* fontData, size_t fontSize, - int ttcIndex, const std::vector<minikin::FontVariation>& axes) : - minikin::MinikinFont(typeface->uniqueID()), mTypeface(std::move(typeface)), mFontData(fontData), - mFontSize(fontSize), mTtcIndex(ttcIndex), mAxes(axes) { -} + int ttcIndex, const std::vector<minikin::FontVariation>& axes) + : minikin::MinikinFont(typeface->uniqueID()) + , mTypeface(std::move(typeface)) + , mFontData(fontData) + , mFontSize(fontSize) + , mTtcIndex(ttcIndex) + , mAxes(axes) {} static void MinikinFontSkia_SetSkiaPaint(const minikin::MinikinFont* font, SkPaint* skPaint, - const minikin::MinikinPaint& paint) { + const minikin::MinikinPaint& paint, + const minikin::FontFakery& fakery) { skPaint->setTextEncoding(SkPaint::kGlyphID_TextEncoding); skPaint->setTextSize(paint.size); skPaint->setTextScaleX(paint.scaleX); skPaint->setTextSkewX(paint.skewX); MinikinFontSkia::unpackPaintFlags(skPaint, paint.paintFlags); // Apply font fakery on top of user-supplied flags. - MinikinFontSkia::populateSkPaint(skPaint, font, paint.fakery); + MinikinFontSkia::populateSkPaint(skPaint, font, fakery); } -float MinikinFontSkia::GetHorizontalAdvance(uint32_t glyph_id, - const minikin::MinikinPaint &paint) const { +float MinikinFontSkia::GetHorizontalAdvance(uint32_t glyph_id, const minikin::MinikinPaint& paint, + const minikin::FontFakery& fakery) const { SkPaint skPaint; uint16_t glyph16 = glyph_id; SkScalar skWidth; - MinikinFontSkia_SetSkiaPaint(this, &skPaint, paint); + MinikinFontSkia_SetSkiaPaint(this, &skPaint, paint, fakery); skPaint.getTextWidths(&glyph16, sizeof(glyph16), &skWidth, NULL); #ifdef VERBOSE ALOGD("width for typeface %d glyph %d = %f", mTypeface->uniqueID(), glyph_id, skWidth); @@ -55,11 +59,12 @@ float MinikinFontSkia::GetHorizontalAdvance(uint32_t glyph_id, } void MinikinFontSkia::GetBounds(minikin::MinikinRect* bounds, uint32_t glyph_id, - const minikin::MinikinPaint& paint) const { + const minikin::MinikinPaint& paint, + const minikin::FontFakery& fakery) const { SkPaint skPaint; uint16_t glyph16 = glyph_id; SkRect skBounds; - MinikinFontSkia_SetSkiaPaint(this, &skPaint, paint); + MinikinFontSkia_SetSkiaPaint(this, &skPaint, paint, fakery); skPaint.getTextWidths(&glyph16, sizeof(glyph16), NULL, &skBounds); bounds->mLeft = skBounds.fLeft; bounds->mTop = skBounds.fTop; @@ -67,7 +72,19 @@ void MinikinFontSkia::GetBounds(minikin::MinikinRect* bounds, uint32_t glyph_id, bounds->mBottom = skBounds.fBottom; } -SkTypeface *MinikinFontSkia::GetSkTypeface() const { +void MinikinFontSkia::GetFontExtent(minikin::MinikinExtent* extent, + const minikin::MinikinPaint& paint, + const minikin::FontFakery& fakery) const { + SkPaint skPaint; + MinikinFontSkia_SetSkiaPaint(this, &skPaint, paint, fakery); + SkPaint::FontMetrics metrics; + skPaint.getFontMetrics(&metrics); + extent->ascent = metrics.fAscent; + extent->descent = metrics.fDescent; + extent->line_gap = metrics.fLeading; +} + +SkTypeface* MinikinFontSkia::GetSkTypeface() const { return mTypeface.get(); } @@ -93,14 +110,14 @@ const std::vector<minikin::FontVariation>& MinikinFontSkia::GetAxes() const { std::shared_ptr<minikin::MinikinFont> MinikinFontSkia::createFontWithVariation( const std::vector<minikin::FontVariation>& variations) const { - SkFontMgr::FontParameters params; + SkFontArguments params; int ttcIndex; - SkStreamAsset* stream = mTypeface->openStream(&ttcIndex); + std::unique_ptr<SkStreamAsset> stream(mTypeface->openStream(&ttcIndex)); LOG_ALWAYS_FATAL_IF(stream == nullptr, "openStream failed"); params.setCollectionIndex(ttcIndex); - std::vector<SkFontMgr::FontParameters::Axis> skAxes; + std::vector<SkFontArguments::Axis> skAxes; skAxes.resize(variations.size()); for (size_t i = 0; i < variations.size(); i++) { skAxes[i].fTag = variations[i].axisTag; @@ -108,10 +125,10 @@ std::shared_ptr<minikin::MinikinFont> MinikinFontSkia::createFontWithVariation( } params.setAxes(skAxes.data(), skAxes.size()); sk_sp<SkFontMgr> fm(SkFontMgr::RefDefault()); - sk_sp<SkTypeface> face(fm->createFromStream(stream, params)); + sk_sp<SkTypeface> face(fm->makeFromStream(std::move(stream), params)); return std::make_shared<MinikinFontSkia>(std::move(face), mFontData, mFontSize, ttcIndex, - variations); + variations); } uint32_t MinikinFontSkia::packPaintFlags(const SkPaint* paint) { @@ -119,9 +136,9 @@ uint32_t MinikinFontSkia::packPaintFlags(const SkPaint* paint) { SkPaint::Hinting hinting = paint->getHinting(); // select only flags that might affect text layout flags &= (SkPaint::kAntiAlias_Flag | SkPaint::kFakeBoldText_Flag | SkPaint::kLinearText_Flag | - SkPaint::kSubpixelText_Flag | SkPaint::kDevKernText_Flag | - SkPaint::kEmbeddedBitmapText_Flag | SkPaint::kAutoHinting_Flag | - SkPaint::kVerticalText_Flag); + SkPaint::kSubpixelText_Flag | SkPaint::kDevKernText_Flag | + SkPaint::kEmbeddedBitmapText_Flag | SkPaint::kAutoHinting_Flag | + SkPaint::kVerticalText_Flag); flags |= (hinting << 16); return flags; } @@ -132,12 +149,11 @@ void MinikinFontSkia::unpackPaintFlags(SkPaint* paint, uint32_t paintFlags) { } void MinikinFontSkia::populateSkPaint(SkPaint* paint, const MinikinFont* font, - minikin::FontFakery fakery) { + minikin::FontFakery fakery) { paint->setTypeface(reinterpret_cast<const MinikinFontSkia*>(font)->RefSkTypeface()); paint->setFakeBoldText(paint->isFakeBoldText() || fakery.isFakeBold()); if (fakery.isFakeItalic()) { paint->setTextSkewX(paint->getTextSkewX() - 0.25f); } } - } diff --git a/libs/hwui/hwui/MinikinSkia.h b/libs/hwui/hwui/MinikinSkia.h index 6c12485845fd..d1565986304f 100644 --- a/libs/hwui/hwui/MinikinSkia.h +++ b/libs/hwui/hwui/MinikinSkia.h @@ -17,9 +17,9 @@ #ifndef _ANDROID_GRAPHICS_MINIKIN_SKIA_H_ #define _ANDROID_GRAPHICS_MINIKIN_SKIA_H_ +#include <SkRefCnt.h> #include <cutils/compiler.h> #include <minikin/MinikinFont.h> -#include <SkRefCnt.h> class SkPaint; class SkTypeface; @@ -29,13 +29,17 @@ namespace android { class ANDROID_API MinikinFontSkia : public minikin::MinikinFont { public: explicit MinikinFontSkia(sk_sp<SkTypeface> typeface, const void* fontData, size_t fontSize, - int ttcIndex, const std::vector<minikin::FontVariation>& axes); + int ttcIndex, const std::vector<minikin::FontVariation>& axes); - float GetHorizontalAdvance(uint32_t glyph_id, - const minikin::MinikinPaint &paint) const; + float GetHorizontalAdvance(uint32_t glyph_id, const minikin::MinikinPaint& paint, + const minikin::FontFakery& fakery) const override; void GetBounds(minikin::MinikinRect* bounds, uint32_t glyph_id, - const minikin::MinikinPaint &paint) const; + const minikin::MinikinPaint& paint, + const minikin::FontFakery& fakery) const override; + + void GetFontExtent(minikin::MinikinExtent* extent, const minikin::MinikinPaint& paint, + const minikin::FontFakery& fakery) const override; SkTypeface* GetSkTypeface() const; sk_sp<SkTypeface> RefSkTypeface() const; @@ -53,7 +57,8 @@ public: // set typeface and fake bold/italic parameters static void populateSkPaint(SkPaint* paint, const minikin::MinikinFont* font, - minikin::FontFakery fakery); + minikin::FontFakery fakery); + private: sk_sp<SkTypeface> mTypeface; diff --git a/libs/hwui/hwui/MinikinUtils.cpp b/libs/hwui/hwui/MinikinUtils.cpp index 415eef77f44e..ba877d395c8f 100644 --- a/libs/hwui/hwui/MinikinUtils.cpp +++ b/libs/hwui/hwui/MinikinUtils.cpp @@ -20,60 +20,68 @@ #include <log/log.h> +#include <minikin/MeasuredText.h> #include "Paint.h" #include "SkPathMeasure.h" #include "Typeface.h" namespace android { -minikin::FontStyle MinikinUtils::prepareMinikinPaint(minikin::MinikinPaint* minikinPaint, - const Paint* paint, Typeface* typeface) { +minikin::MinikinPaint MinikinUtils::prepareMinikinPaint(const Paint* paint, + const Typeface* typeface) { const Typeface* resolvedFace = Typeface::resolveDefault(typeface); - minikin::FontStyle resolved = resolvedFace->fStyle; - - /* Prepare minikin FontStyle */ - minikin::FontVariant minikinVariant = (paint->getFontVariant() == minikin::VARIANT_ELEGANT) ? - minikin::VARIANT_ELEGANT : minikin::VARIANT_COMPACT; - const uint32_t langListId = paint->getMinikinLangListId(); - minikin::FontStyle minikinStyle(langListId, minikinVariant, resolved.getWeight(), - resolved.getItalic()); + minikin::MinikinPaint minikinPaint; /* Prepare minikin Paint */ - // Note: it would be nice to handle fractional size values (it would improve smooth zoom - // behavior), but historically size has been treated as an int. - // TODO: explore whether to enable fractional sizes, possibly when linear text flag is set. - minikinPaint->size = (int)paint->getTextSize(); - minikinPaint->scaleX = paint->getTextScaleX(); - minikinPaint->skewX = paint->getTextSkewX(); - minikinPaint->letterSpacing = paint->getLetterSpacing(); - minikinPaint->wordSpacing = paint->getWordSpacing(); - minikinPaint->paintFlags = MinikinFontSkia::packPaintFlags(paint); - minikinPaint->fontFeatureSettings = paint->getFontFeatureSettings(); - minikinPaint->hyphenEdit = minikin::HyphenEdit(paint->getHyphenEdit()); - return minikinStyle; + minikinPaint.size = + paint->isLinearText() ? paint->getTextSize() : static_cast<int>(paint->getTextSize()); + minikinPaint.scaleX = paint->getTextScaleX(); + minikinPaint.skewX = paint->getTextSkewX(); + minikinPaint.letterSpacing = paint->getLetterSpacing(); + minikinPaint.wordSpacing = paint->getWordSpacing(); + minikinPaint.paintFlags = MinikinFontSkia::packPaintFlags(paint); + minikinPaint.localeListId = paint->getMinikinLocaleListId(); + minikinPaint.familyVariant = paint->getFamilyVariant(); + minikinPaint.fontStyle = resolvedFace->fStyle; + minikinPaint.fontFeatureSettings = paint->getFontFeatureSettings(); + minikinPaint.hyphenEdit = minikin::HyphenEdit(paint->getHyphenEdit()); + return minikinPaint; } -minikin::Layout MinikinUtils::doLayout(const Paint* paint, int bidiFlags, - Typeface* typeface, const uint16_t* buf, size_t start, size_t count, - size_t bufSize) { - minikin::MinikinPaint minikinPaint; - minikin::FontStyle minikinStyle = prepareMinikinPaint(&minikinPaint, paint, typeface); +minikin::Layout MinikinUtils::doLayout(const Paint* paint, minikin::Bidi bidiFlags, + const Typeface* typeface, const uint16_t* buf, size_t start, + size_t count, size_t bufSize, minikin::MeasuredText* mt, + int mtOffset) { + minikin::MinikinPaint minikinPaint = prepareMinikinPaint(paint, typeface); + const auto& fc = Typeface::resolveDefault(typeface)->fFontCollection; minikin::Layout layout; - layout.doLayout(buf, start, count, bufSize, bidiFlags, minikinStyle, minikinPaint, - Typeface::resolveDefault(typeface)->fFontCollection); + + if (mt == nullptr) { + layout.doLayout(buf, start, count, bufSize, bidiFlags, minikinPaint, fc); + return layout; + } + + if (mt->buildLayout(minikin::U16StringPiece(buf, bufSize), + minikin::Range(start, start + count), + minikinPaint, fc, bidiFlags, mtOffset, &layout)) { + return layout; + } + + layout.doLayout(buf, start, count, bufSize, bidiFlags, minikinPaint, fc); return layout; } -float MinikinUtils::measureText(const Paint* paint, int bidiFlags, Typeface* typeface, - const uint16_t* buf, size_t start, size_t count, size_t bufSize, float *advances) { - minikin::MinikinPaint minikinPaint; - minikin::FontStyle minikinStyle = prepareMinikinPaint(&minikinPaint, paint, typeface); - Typeface* resolvedTypeface = Typeface::resolveDefault(typeface); - return minikin::Layout::measureText(buf, start, count, bufSize, bidiFlags, minikinStyle, - minikinPaint, resolvedTypeface->fFontCollection, advances); +float MinikinUtils::measureText(const Paint* paint, minikin::Bidi bidiFlags, + const Typeface* typeface, const uint16_t* buf, size_t start, + size_t count, size_t bufSize, float* advances) { + minikin::MinikinPaint minikinPaint = prepareMinikinPaint(paint, typeface); + const Typeface* resolvedTypeface = Typeface::resolveDefault(typeface); + return minikin::Layout::measureText(buf, start, count, bufSize, bidiFlags, minikinPaint, + resolvedTypeface->fFontCollection, advances, + nullptr /* extent */); } -bool MinikinUtils::hasVariationSelector(Typeface* typeface, uint32_t codepoint, uint32_t vs) { +bool MinikinUtils::hasVariationSelector(const Typeface* typeface, uint32_t codepoint, uint32_t vs) { const Typeface* resolvedFace = Typeface::resolveDefault(typeface); return resolvedFace->fFontCollection->hasVariationSelector(codepoint, vs); } @@ -93,7 +101,7 @@ float MinikinUtils::xOffsetForTextAlign(Paint* paint, const minikin::Layout& lay } float MinikinUtils::hOffsetForTextAlign(Paint* paint, const minikin::Layout& layout, - const SkPath& path) { + const SkPath& path) { float align = 0; switch (paint->getTextAlign()) { case Paint::kCenter_Align: @@ -108,5 +116,4 @@ float MinikinUtils::hOffsetForTextAlign(Paint* paint, const minikin::Layout& lay SkPathMeasure measure(path, false); return align * (layout.getAdvance() - measure.getLength()); } - -} +} // namespace android diff --git a/libs/hwui/hwui/MinikinUtils.h b/libs/hwui/hwui/MinikinUtils.h index 0f22adc5d42b..124fe4f81fbe 100644 --- a/libs/hwui/hwui/MinikinUtils.h +++ b/libs/hwui/hwui/MinikinUtils.h @@ -19,38 +19,46 @@ * Paint and so on. **/ - // TODO: does this really need to be separate from MinikinSkia? +// TODO: does this really need to be separate from MinikinSkia? #ifndef _ANDROID_GRAPHICS_MINIKIN_UTILS_H_ #define _ANDROID_GRAPHICS_MINIKIN_UTILS_H_ #include <cutils/compiler.h> #include <minikin/Layout.h> -#include "Paint.h" #include "MinikinSkia.h" +#include "Paint.h" #include "Typeface.h" +#include <log/log.h> + +namespace minikin { +class MeasuredText; +} // namespace minikin namespace android { class MinikinUtils { public: - ANDROID_API static minikin::FontStyle prepareMinikinPaint(minikin::MinikinPaint* minikinPaint, - const Paint* paint, Typeface* typeface); + ANDROID_API static minikin::MinikinPaint prepareMinikinPaint(const Paint* paint, + const Typeface* typeface); - ANDROID_API static minikin::Layout doLayout(const Paint* paint, int bidiFlags, - Typeface* typeface, const uint16_t* buf, size_t start, size_t count, - size_t bufSize); + ANDROID_API static minikin::Layout doLayout(const Paint* paint, minikin::Bidi bidiFlags, + const Typeface* typeface, const uint16_t* buf, + size_t start, size_t count, size_t bufSize, + minikin::MeasuredText* mt, int mtOffset); - ANDROID_API static float measureText(const Paint* paint, int bidiFlags, Typeface* typeface, - const uint16_t* buf, size_t start, size_t count, size_t bufSize, float *advances); + ANDROID_API static float measureText(const Paint* paint, minikin::Bidi bidiFlags, + const Typeface* typeface, const uint16_t* buf, + size_t start, size_t count, size_t bufSize, + float* advances); - ANDROID_API static bool hasVariationSelector(Typeface* typeface, uint32_t codepoint, - uint32_t vs); + ANDROID_API static bool hasVariationSelector(const Typeface* typeface, uint32_t codepoint, + uint32_t vs); ANDROID_API static float xOffsetForTextAlign(Paint* paint, const minikin::Layout& layout); ANDROID_API static float hOffsetForTextAlign(Paint* paint, const minikin::Layout& layout, - const SkPath& path); + const SkPath& path); // f is a functor of type void f(size_t start, size_t end); template <typename F> ANDROID_API static void forFontRun(const minikin::Layout& layout, Paint* paint, F& f) { diff --git a/libs/hwui/hwui/Paint.h b/libs/hwui/hwui/Paint.h index a5d83a0ca018..002f75906c35 100644 --- a/libs/hwui/hwui/Paint.h +++ b/libs/hwui/hwui/Paint.h @@ -17,6 +17,8 @@ #ifndef ANDROID_GRAPHICS_PAINT_H_ #define ANDROID_GRAPHICS_PAINT_H_ +#include "Typeface.h" + #include <cutils/compiler.h> #include <SkPaint.h> @@ -30,8 +32,8 @@ class ANDROID_API Paint : public SkPaint { public: // Default values for underlined and strikethrough text, // as defined by Skia in SkTextFormatParams.h. - constexpr static float kStdStrikeThru_Offset = (-6.0f / 21.0f); - constexpr static float kStdUnderline_Offset = (1.0f / 9.0f); + constexpr static float kStdStrikeThru_Offset = (-6.0f / 21.0f); + constexpr static float kStdUnderline_Offset = (1.0f / 9.0f); constexpr static float kStdUnderline_Thickness = (1.0f / 18.0f); constexpr static float kStdUnderline_Top = @@ -49,67 +51,54 @@ public: Paint& operator=(const Paint& other); friend bool operator==(const Paint& a, const Paint& b); - friend bool operator!=(const Paint& a, const Paint& b) { - return !(a == b); - } + friend bool operator!=(const Paint& a, const Paint& b) { return !(a == b); } - void setLetterSpacing(float letterSpacing) { - mLetterSpacing = letterSpacing; - } + void setLetterSpacing(float letterSpacing) { mLetterSpacing = letterSpacing; } - float getLetterSpacing() const { - return mLetterSpacing; - } + float getLetterSpacing() const { return mLetterSpacing; } - void setWordSpacing(float wordSpacing) { - mWordSpacing = wordSpacing; - } + void setWordSpacing(float wordSpacing) { mWordSpacing = wordSpacing; } - float getWordSpacing() const { - return mWordSpacing; - } + float getWordSpacing() const { return mWordSpacing; } void setFontFeatureSettings(const std::string& fontFeatureSettings) { mFontFeatureSettings = fontFeatureSettings; } - std::string getFontFeatureSettings() const { - return mFontFeatureSettings; - } + std::string getFontFeatureSettings() const { return mFontFeatureSettings; } - void setMinikinLangListId(uint32_t minikinLangListId) { - mMinikinLangListId = minikinLangListId; + void setMinikinLocaleListId(uint32_t minikinLocaleListId) { + mMinikinLocaleListId = minikinLocaleListId; } - uint32_t getMinikinLangListId() const { - return mMinikinLangListId; - } + uint32_t getMinikinLocaleListId() const { return mMinikinLocaleListId; } - void setFontVariant(minikin::FontVariant variant) { - mFontVariant = variant; - } + void setFamilyVariant(minikin::FontFamily::Variant variant) { mFamilyVariant = variant; } - minikin::FontVariant getFontVariant() const { - return mFontVariant; - } + minikin::FontFamily::Variant getFamilyVariant() const { return mFamilyVariant; } - void setHyphenEdit(uint32_t hyphen) { - mHyphenEdit = hyphen; - } + void setHyphenEdit(uint32_t hyphen) { mHyphenEdit = hyphen; } - uint32_t getHyphenEdit() const { - return mHyphenEdit; - } + uint32_t getHyphenEdit() const { return mHyphenEdit; } + + void setAndroidTypeface(Typeface* typeface) { mTypeface = typeface; } + + const Typeface* getAndroidTypeface() const { return mTypeface; } private: float mLetterSpacing = 0; float mWordSpacing = 0; std::string mFontFeatureSettings; - uint32_t mMinikinLangListId; - minikin::FontVariant mFontVariant; + uint32_t mMinikinLocaleListId; + minikin::FontFamily::Variant mFamilyVariant; uint32_t mHyphenEdit = 0; + // The native Typeface object has the same lifetime of the Java Typeface + // object. The Java Paint object holds a strong reference to the Java Typeface + // object. Thus, following pointer can never be a dangling pointer. Note that + // nullptr is valid: it means the default typeface. + const Typeface* mTypeface = nullptr; }; } // namespace android -#endif // ANDROID_GRAPHICS_PAINT_H_ +#endif // ANDROID_GRAPHICS_PAINT_H_ diff --git a/libs/hwui/hwui/PaintImpl.cpp b/libs/hwui/hwui/PaintImpl.cpp index 67427433bb89..ae9c475d09d4 100644 --- a/libs/hwui/hwui/PaintImpl.cpp +++ b/libs/hwui/hwui/PaintImpl.cpp @@ -18,45 +18,52 @@ namespace android { -Paint::Paint() : - SkPaint(), mLetterSpacing(0), mWordSpacing(0), mFontFeatureSettings(), - mMinikinLangListId(0), mFontVariant(minikin::VARIANT_DEFAULT) { -} +Paint::Paint() + : SkPaint() + , mLetterSpacing(0) + , mWordSpacing(0) + , mFontFeatureSettings() + , mMinikinLocaleListId(0) + , mFamilyVariant(minikin::FontFamily::Variant::DEFAULT) {} -Paint::Paint(const Paint& paint) : SkPaint(paint), - mLetterSpacing(paint.mLetterSpacing), mWordSpacing(paint.mWordSpacing), - mFontFeatureSettings(paint.mFontFeatureSettings), - mMinikinLangListId(paint.mMinikinLangListId), mFontVariant(paint.mFontVariant), - mHyphenEdit(paint.mHyphenEdit) { -} +Paint::Paint(const Paint& paint) + : SkPaint(paint) + , mLetterSpacing(paint.mLetterSpacing) + , mWordSpacing(paint.mWordSpacing) + , mFontFeatureSettings(paint.mFontFeatureSettings) + , mMinikinLocaleListId(paint.mMinikinLocaleListId) + , mFamilyVariant(paint.mFamilyVariant) + , mHyphenEdit(paint.mHyphenEdit) + , mTypeface(paint.mTypeface) {} -Paint::Paint(const SkPaint& paint) : SkPaint(paint), - mLetterSpacing(0), mWordSpacing(0), mFontFeatureSettings(), mMinikinLangListId(0), - mFontVariant(minikin::VARIANT_DEFAULT) { -} +Paint::Paint(const SkPaint& paint) + : SkPaint(paint) + , mLetterSpacing(0) + , mWordSpacing(0) + , mFontFeatureSettings() + , mMinikinLocaleListId(0) + , mFamilyVariant(minikin::FontFamily::Variant::DEFAULT) {} -Paint::~Paint() { -} +Paint::~Paint() {} Paint& Paint::operator=(const Paint& other) { SkPaint::operator=(other); mLetterSpacing = other.mLetterSpacing; mWordSpacing = other.mWordSpacing; mFontFeatureSettings = other.mFontFeatureSettings; - mMinikinLangListId = other.mMinikinLangListId; - mFontVariant = other.mFontVariant; + mMinikinLocaleListId = other.mMinikinLocaleListId; + mFamilyVariant = other.mFamilyVariant; mHyphenEdit = other.mHyphenEdit; + mTypeface = other.mTypeface; return *this; } bool operator==(const Paint& a, const Paint& b) { - return static_cast<const SkPaint&>(a) == static_cast<const SkPaint&>(b) - && a.mLetterSpacing == b.mLetterSpacing - && a.mWordSpacing == b.mWordSpacing - && a.mFontFeatureSettings == b.mFontFeatureSettings - && a.mMinikinLangListId == b.mMinikinLangListId - && a.mFontVariant == b.mFontVariant - && a.mHyphenEdit == b.mHyphenEdit; -} - + return static_cast<const SkPaint&>(a) == static_cast<const SkPaint&>(b) && + a.mLetterSpacing == b.mLetterSpacing && a.mWordSpacing == b.mWordSpacing && + a.mFontFeatureSettings == b.mFontFeatureSettings && + a.mMinikinLocaleListId == b.mMinikinLocaleListId && + a.mFamilyVariant == b.mFamilyVariant && a.mHyphenEdit == b.mHyphenEdit && + a.mTypeface == b.mTypeface; } +} // namespace android diff --git a/libs/hwui/hwui/Typeface.cpp b/libs/hwui/hwui/Typeface.cpp index f66bb045373c..ebc14c8b675b 100644 --- a/libs/hwui/hwui/Typeface.cpp +++ b/libs/hwui/hwui/Typeface.cpp @@ -14,23 +14,17 @@ * limitations under the License. */ -/** - * This is the implementation of the Typeface object. Historically, it has - * just been SkTypeface, but we are migrating to Minikin. For the time - * being, that choice is hidden under the USE_MINIKIN compile-time flag. - */ - #include "Typeface.h" -#include <pthread.h> #include <fcntl.h> // For tests. -#include <sys/stat.h> // For tests. +#include <pthread.h> #include <sys/mman.h> // For tests. +#include <sys/stat.h> // For tests. #include "MinikinSkia.h" -#include "SkTypeface.h" #include "SkPaint.h" #include "SkStream.h" // Fot tests. +#include "SkTypeface.h" #include <minikin/FontCollection.h> #include <minikin/FontFamily.h> @@ -40,65 +34,64 @@ namespace android { -static SkTypeface::Style computeSkiaStyle(int weight, bool italic) { +static Typeface::Style computeAPIStyle(int weight, bool italic) { // This bold detection comes from SkTypeface.h if (weight >= SkFontStyle::kSemiBold_Weight) { - return italic ? SkTypeface::kBoldItalic : SkTypeface::kBold; + return italic ? Typeface::kBoldItalic : Typeface::kBold; } else { - return italic ? SkTypeface::kItalic : SkTypeface::kNormal; + return italic ? Typeface::kItalic : Typeface::kNormal; } } static minikin::FontStyle computeMinikinStyle(int weight, bool italic) { - // TODO: Better to use raw base weight value for font selection instead of dividing by 100. - const int minikinWeight = uirenderer::MathUtils::clamp((weight + 50) / 100, 1, 10); - return minikin::FontStyle(minikinWeight, italic); + return minikin::FontStyle(uirenderer::MathUtils::clamp(weight, 1, 1000), + static_cast<minikin::FontStyle::Slant>(italic)); } // Resolve the relative weight from the baseWeight and target style. -static minikin::FontStyle computeRelativeStyle(int baseWeight, SkTypeface::Style relativeStyle) { +static minikin::FontStyle computeRelativeStyle(int baseWeight, Typeface::Style relativeStyle) { int weight = baseWeight; - if ((relativeStyle & SkTypeface::kBold) != 0) { + if ((relativeStyle & Typeface::kBold) != 0) { weight += 300; } - bool italic = (relativeStyle & SkTypeface::kItalic) != 0; + bool italic = (relativeStyle & Typeface::kItalic) != 0; return computeMinikinStyle(weight, italic); } -Typeface* gDefaultTypeface = NULL; +const Typeface* gDefaultTypeface = NULL; -Typeface* Typeface::resolveDefault(Typeface* src) { - LOG_ALWAYS_FATAL_IF(gDefaultTypeface == nullptr); +const Typeface* Typeface::resolveDefault(const Typeface* src) { + LOG_ALWAYS_FATAL_IF(src == nullptr && gDefaultTypeface == nullptr); return src == nullptr ? gDefaultTypeface : src; } -Typeface* Typeface::createRelative(Typeface* src, SkTypeface::Style style) { - Typeface* resolvedFace = Typeface::resolveDefault(src); +Typeface* Typeface::createRelative(Typeface* src, Typeface::Style style) { + const Typeface* resolvedFace = Typeface::resolveDefault(src); Typeface* result = new Typeface; if (result != nullptr) { result->fFontCollection = resolvedFace->fFontCollection; result->fBaseWeight = resolvedFace->fBaseWeight; - result->fSkiaStyle = style; + result->fAPIStyle = style; result->fStyle = computeRelativeStyle(result->fBaseWeight, style); } return result; } Typeface* Typeface::createAbsolute(Typeface* base, int weight, bool italic) { - Typeface* resolvedFace = Typeface::resolveDefault(base); + const Typeface* resolvedFace = Typeface::resolveDefault(base); Typeface* result = new Typeface(); if (result != nullptr) { result->fFontCollection = resolvedFace->fFontCollection; result->fBaseWeight = resolvedFace->fBaseWeight; - result->fSkiaStyle = computeSkiaStyle(weight, italic); + result->fAPIStyle = computeAPIStyle(weight, italic); result->fStyle = computeMinikinStyle(weight, italic); } return result; } -Typeface* Typeface::createFromTypefaceWithVariation(Typeface* src, - const std::vector<minikin::FontVariation>& variations) { - Typeface* resolvedFace = Typeface::resolveDefault(src); +Typeface* Typeface::createFromTypefaceWithVariation( + Typeface* src, const std::vector<minikin::FontVariation>& variations) { + const Typeface* resolvedFace = Typeface::resolveDefault(src); Typeface* result = new Typeface(); if (result != nullptr) { result->fFontCollection = @@ -111,27 +104,26 @@ Typeface* Typeface::createFromTypefaceWithVariation(Typeface* src, // Do not update styles. // TODO: We may want to update base weight if the 'wght' is specified. result->fBaseWeight = resolvedFace->fBaseWeight; - result->fSkiaStyle = resolvedFace->fSkiaStyle; + result->fAPIStyle = resolvedFace->fAPIStyle; result->fStyle = resolvedFace->fStyle; } return result; } Typeface* Typeface::createWithDifferentBaseWeight(Typeface* src, int weight) { - Typeface* resolvedFace = Typeface::resolveDefault(src); + const Typeface* resolvedFace = Typeface::resolveDefault(src); Typeface* result = new Typeface; if (result != nullptr) { result->fFontCollection = resolvedFace->fFontCollection; result->fBaseWeight = weight; - result->fSkiaStyle = resolvedFace->fSkiaStyle; - result->fStyle = computeRelativeStyle(weight, result->fSkiaStyle); + result->fAPIStyle = resolvedFace->fAPIStyle; + result->fStyle = computeRelativeStyle(weight, result->fAPIStyle); } return result; } -Typeface* Typeface::createFromFamilies( - std::vector<std::shared_ptr<minikin::FontFamily>>&& families, - int weight, int italic) { +Typeface* Typeface::createFromFamilies(std::vector<std::shared_ptr<minikin::FontFamily>>&& families, + int weight, int italic) { Typeface* result = new Typeface; result->fFontCollection.reset(new minikin::FontCollection(families)); @@ -141,7 +133,7 @@ Typeface* Typeface::createFromFamilies( const minikin::FontStyle defaultStyle; const minikin::MinikinFont* mf = - families.empty() ? nullptr : families[0]->getClosestMatch(defaultStyle).font; + families.empty() ? nullptr : families[0]->getClosestMatch(defaultStyle).font; if (mf != nullptr) { SkTypeface* skTypeface = reinterpret_cast<const MinikinFontSkia*>(mf)->GetSkTypeface(); const SkFontStyle& style = skTypeface->fontStyle(); @@ -157,7 +149,7 @@ Typeface* Typeface::createFromFamilies( weight = weightFromFont; } if (italic == RESOLVE_BY_FONT_TABLE) { - italic = italicFromFont? 1 : 0; + italic = italicFromFont ? 1 : 0; } } @@ -167,12 +159,12 @@ Typeface* Typeface::createFromFamilies( } result->fBaseWeight = weight; - result->fSkiaStyle = computeSkiaStyle(weight, italic); + result->fAPIStyle = computeAPIStyle(weight, italic); result->fStyle = computeMinikinStyle(weight, italic); return result; } -void Typeface::setDefault(Typeface* face) { +void Typeface::setDefault(const Typeface* face) { gDefaultTypeface = face; } @@ -191,17 +183,16 @@ void Typeface::setRobotoTypefaceForTest() { std::shared_ptr<minikin::MinikinFont> font = std::make_shared<MinikinFontSkia>( std::move(typeface), data, st.st_size, 0, std::vector<minikin::FontVariation>()); std::shared_ptr<minikin::FontFamily> family = std::make_shared<minikin::FontFamily>( - std::vector<minikin::Font>({ minikin::Font(std::move(font), minikin::FontStyle()) })); + std::vector<minikin::Font>({minikin::Font(std::move(font), minikin::FontStyle())})); std::shared_ptr<minikin::FontCollection> collection = std::make_shared<minikin::FontCollection>(std::move(family)); Typeface* hwTypeface = new Typeface(); hwTypeface->fFontCollection = collection; - hwTypeface->fSkiaStyle = SkTypeface::kNormal; + hwTypeface->fAPIStyle = Typeface::kNormal; hwTypeface->fBaseWeight = SkFontStyle::kNormal_Weight; - hwTypeface->fStyle = minikin::FontStyle(4 /* weight */, false /* italic */); + hwTypeface->fStyle = minikin::FontStyle(); Typeface::setDefault(hwTypeface); } - -} +} // namespace android diff --git a/libs/hwui/hwui/Typeface.h b/libs/hwui/hwui/Typeface.h index db0b2cdeba00..ef8d8f4ee4f3 100644 --- a/libs/hwui/hwui/Typeface.h +++ b/libs/hwui/hwui/Typeface.h @@ -14,7 +14,6 @@ * limitations under the License. */ - #ifndef _ANDROID_GRAPHICS_TYPEFACE_IMPL_H_ #define _ANDROID_GRAPHICS_TYPEFACE_IMPL_H_ @@ -22,8 +21,8 @@ #include <cutils/compiler.h> #include <minikin/FontCollection.h> -#include <vector> #include <memory> +#include <vector> namespace android { @@ -32,16 +31,17 @@ namespace android { constexpr int RESOLVE_BY_FONT_TABLE = -1; struct ANDROID_API Typeface { - public: +public: std::shared_ptr<minikin::FontCollection> fFontCollection; // resolved style actually used for rendering minikin::FontStyle fStyle; - // style used for constructing and querying Typeface objects - SkTypeface::Style fSkiaStyle; + // style used in the API + enum Style : uint8_t { kNormal = 0, kBold = 0x01, kItalic = 0x02, kBoldItalic = 0x03 }; + Style fAPIStyle; - static Typeface* resolveDefault(Typeface* src); + static const Typeface* resolveDefault(const Typeface* src); // The following three functions create new Typeface from an existing Typeface with a different // style. There is a base weight concept which is used for calculating relative style from an @@ -68,26 +68,24 @@ struct ANDROID_API Typeface { // // Typeface* black = createAbsolute(base, 900, false); // Rendered with a weight of 900. static Typeface* createWithDifferentBaseWeight(Typeface* src, int baseweight); - static Typeface* createRelative(Typeface* src, SkTypeface::Style desiredStyle); + static Typeface* createRelative(Typeface* src, Style desiredStyle); static Typeface* createAbsolute(Typeface* base, int weight, bool italic); - static Typeface* createFromTypefaceWithVariation(Typeface* src, - const std::vector<minikin::FontVariation>& variations); + static Typeface* createFromTypefaceWithVariation( + Typeface* src, const std::vector<minikin::FontVariation>& variations); static Typeface* createFromFamilies( - std::vector<std::shared_ptr<minikin::FontFamily>>&& families, - int weight, int italic); + std::vector<std::shared_ptr<minikin::FontFamily>>&& families, int weight, int italic); - static void setDefault(Typeface* face); + static void setDefault(const Typeface* face); // Sets roboto font as the default typeface for testing purpose. static void setRobotoTypefaceForTest(); - private: + +private: // base weight in CSS-style units, 1..1000 int fBaseWeight; - }; - } #endif // _ANDROID_GRAPHICS_TYPEFACE_IMPL_H_ diff --git a/libs/hwui/pipeline/skia/AnimatedDrawables.h b/libs/hwui/pipeline/skia/AnimatedDrawables.h index 44c494f77231..efef6de2a9e1 100644 --- a/libs/hwui/pipeline/skia/AnimatedDrawables.h +++ b/libs/hwui/pipeline/skia/AnimatedDrawables.h @@ -16,10 +16,10 @@ #pragma once -#include "CanvasProperty.h" -#include <utils/RefBase.h> #include <SkCanvas.h> #include <SkDrawable.h> +#include <utils/RefBase.h> +#include "CanvasProperty.h" namespace android { namespace uirenderer { @@ -28,16 +28,12 @@ namespace skiapipeline { class AnimatedRoundRect : public SkDrawable { public: AnimatedRoundRect(uirenderer::CanvasPropertyPrimitive* left, - uirenderer::CanvasPropertyPrimitive* top, uirenderer::CanvasPropertyPrimitive* right, - uirenderer::CanvasPropertyPrimitive* bottom, uirenderer::CanvasPropertyPrimitive* rx, - uirenderer::CanvasPropertyPrimitive* ry, uirenderer::CanvasPropertyPaint* p) - : mLeft(left) - , mTop(top) - , mRight(right) - , mBottom(bottom) - , mRx(rx) - , mRy(ry) - , mPaint(p) {} + uirenderer::CanvasPropertyPrimitive* top, + uirenderer::CanvasPropertyPrimitive* right, + uirenderer::CanvasPropertyPrimitive* bottom, + uirenderer::CanvasPropertyPrimitive* rx, + uirenderer::CanvasPropertyPrimitive* ry, uirenderer::CanvasPropertyPaint* p) + : mLeft(left), mTop(top), mRight(right), mBottom(bottom), mRx(rx), mRy(ry), mPaint(p) {} protected: virtual SkRect onGetBounds() override { @@ -61,11 +57,9 @@ private: class AnimatedCircle : public SkDrawable { public: AnimatedCircle(uirenderer::CanvasPropertyPrimitive* x, uirenderer::CanvasPropertyPrimitive* y, - uirenderer::CanvasPropertyPrimitive* radius, uirenderer::CanvasPropertyPaint* paint) - : mX(x) - , mY(y) - , mRadius(radius) - , mPaint(paint) {} + uirenderer::CanvasPropertyPrimitive* radius, + uirenderer::CanvasPropertyPaint* paint) + : mX(x), mY(y), mRadius(radius), mPaint(paint) {} protected: virtual SkRect onGetBounds() override { @@ -85,6 +79,6 @@ private: sp<uirenderer::CanvasPropertyPaint> mPaint; }; -}; // namespace skiapipeline -}; // namespace uirenderer -}; // namespace android +}; // namespace skiapipeline +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/pipeline/skia/DumpOpsCanvas.h b/libs/hwui/pipeline/skia/DumpOpsCanvas.h index 34fb04ccad55..1f83d1a201b0 100644 --- a/libs/hwui/pipeline/skia/DumpOpsCanvas.h +++ b/libs/hwui/pipeline/skia/DumpOpsCanvas.h @@ -16,6 +16,7 @@ #pragma once +#include "RenderNode.h" #include "SkiaDisplayList.h" namespace android { @@ -32,8 +33,7 @@ public: : mOutput(output) , mLevel(level) , mDisplayList(displayList) - , mIdent((level + 1) * 2, ' ') { - } + , mIdent((level + 1) * 2, ' ') {} protected: void onClipRect(const SkRect& rect, SkClipOp, ClipEdgeStyle) override { @@ -52,9 +52,7 @@ protected: mOutput << mIdent << "clipRegion" << std::endl; } - void onDrawPaint(const SkPaint&) override { - mOutput << mIdent << "drawPaint" << std::endl; - } + void onDrawPaint(const SkPaint&) override { mOutput << mIdent << "drawPaint" << std::endl; } void onDrawPath(const SkPath&, const SkPaint&) override { mOutput << mIdent << "drawPath" << std::endl; @@ -92,22 +90,21 @@ protected: mOutput << mIdent << "drawPosText" << std::endl; } - void onDrawPosTextH(const void*, size_t, const SkScalar[], SkScalar, - const SkPaint&) override { + void onDrawPosTextH(const void*, size_t, const SkScalar[], SkScalar, const SkPaint&) override { mOutput << mIdent << "drawPosTextH" << std::endl; } void onDrawTextOnPath(const void*, size_t, const SkPath&, const SkMatrix*, - const SkPaint&) override { + const SkPaint&) override { mOutput << mIdent << "drawTextOnPath" << std::endl; } void onDrawTextRSXform(const void*, size_t, const SkRSXform[], const SkRect*, - const SkPaint&) override { + const SkPaint&) override { mOutput << mIdent << "drawTextRSXform" << std::endl; } - void onDrawTextBlob(const SkTextBlob*, SkScalar,SkScalar, const SkPaint&) override { + void onDrawTextBlob(const SkTextBlob*, SkScalar, SkScalar, const SkPaint&) override { mOutput << mIdent << "drawTextBlob" << std::endl; } @@ -116,17 +113,17 @@ protected: } void onDrawImageNine(const SkImage*, const SkIRect& center, const SkRect& dst, - const SkPaint*) override { + const SkPaint*) override { mOutput << mIdent << "drawImageNine" << std::endl; } void onDrawImageRect(const SkImage*, const SkRect*, const SkRect&, const SkPaint*, - SrcRectConstraint) override { + SrcRectConstraint) override { mOutput << mIdent << "drawImageRect" << std::endl; } void onDrawImageLattice(const SkImage*, const Lattice& lattice, const SkRect& dst, - const SkPaint*) override { + const SkPaint*) override { mOutput << mIdent << "drawImageLattice" << std::endl; } @@ -157,21 +154,21 @@ protected: private: RenderNodeDrawable* getRenderNodeDrawable(SkDrawable* drawable) { - for (auto& child : mDisplayList.mChildNodes) { + for (auto& child : mDisplayList.mChildNodes) { if (drawable == &child) { return &child; } - } - return nullptr; + } + return nullptr; } GLFunctorDrawable* getGLFunctorDrawable(SkDrawable* drawable) { - for (auto& child : mDisplayList.mChildFunctors) { + for (auto& child : mDisplayList.mChildFunctors) { if (drawable == &child) { return &child; } - } - return nullptr; + } + return nullptr; } std::ostream& mOutput; @@ -180,6 +177,6 @@ private: std::string mIdent; }; -}; // namespace skiapipeline -}; // namespace uirenderer -}; // namespace android +}; // namespace skiapipeline +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp index ea302a154616..145e3c485cbc 100644 --- a/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp +++ b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp @@ -15,18 +15,19 @@ */ #include "GLFunctorDrawable.h" +#include <GrContext.h> +#include <private/hwui/DrawGlInfo.h> #include "GlFunctorLifecycleListener.h" #include "RenderNode.h" +#include "SkAndroidFrameworkUtils.h" #include "SkClipStack.h" -#include <private/hwui/DrawGlInfo.h> -#include <GrContext.h> namespace android { namespace uirenderer { namespace skiapipeline { GLFunctorDrawable::~GLFunctorDrawable() { - if(mListener.get() != nullptr) { + if (mListener.get() != nullptr) { mListener->onGlFunctorReleased(mFunctor); } } @@ -49,8 +50,6 @@ void GLFunctorDrawable::onDraw(SkCanvas* canvas) { return; } - canvas->flush(); - if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) { canvas->clear(SK_ColorRED); return; @@ -72,33 +71,33 @@ void GLFunctorDrawable::onDraw(SkCanvas* canvas) { info.height = canvasInfo.height(); mat4.asColMajorf(&info.transform[0]); - //apply a simple clip with a scissor or a complex clip with a stencil + bool clearStencilAfterFunctor = false; + + // apply a simple clip with a scissor or a complex clip with a stencil SkRegion clipRegion; canvas->temporary_internal_getRgnClip(&clipRegion); if (CC_UNLIKELY(clipRegion.isComplex())) { - //It is only a temporary solution to use a scissor to draw the stencil. - //There is a bug 31489986 to implement efficiently non-rectangular clips. glDisable(GL_SCISSOR_TEST); - glDisable(GL_STENCIL_TEST); - glStencilMask(0xff); + glStencilMask(0x1); glClearStencil(0); glClear(GL_STENCIL_BUFFER_BIT); - glEnable(GL_SCISSOR_TEST); - SkRegion::Cliperator it(clipRegion, ibounds); - while (!it.done()) { - setScissor(info.height, it.rect()); - glClearStencil(0x1); - glClear(GL_STENCIL_BUFFER_BIT); - it.next(); + bool stencilWritten = SkAndroidFrameworkUtils::clipWithStencil(canvas); + canvas->flush(); + if (stencilWritten) { + glStencilMask(0x1); + glStencilFunc(GL_EQUAL, 0x1, 0x1); + glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + clearStencilAfterFunctor = true; + glEnable(GL_STENCIL_TEST); + } else { + glDisable(GL_STENCIL_TEST); } - glDisable(GL_SCISSOR_TEST); - glStencilFunc(GL_EQUAL, 0x1, 0xff); - glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); - glEnable(GL_STENCIL_TEST); } else if (clipRegion.isEmpty()) { + canvas->flush(); glDisable(GL_STENCIL_TEST); glDisable(GL_SCISSOR_TEST); } else { + canvas->flush(); glDisable(GL_STENCIL_TEST); glEnable(GL_SCISSOR_TEST); setScissor(info.height, clipRegion.getBounds()); @@ -106,9 +105,18 @@ void GLFunctorDrawable::onDraw(SkCanvas* canvas) { (*mFunctor)(DrawGlInfo::kModeDraw, &info); + if (clearStencilAfterFunctor) { + // clear stencil buffer as it may be used by Skia + glDisable(GL_SCISSOR_TEST); + glDisable(GL_STENCIL_TEST); + glStencilMask(0x1); + glClearStencil(0); + glClear(GL_STENCIL_BUFFER_BIT); + } + canvas->getGrContext()->resetContext(); - } +} -}; // namespace skiapipeline -}; // namespace uirenderer -}; // namespace android +}; // namespace skiapipeline +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/pipeline/skia/GLFunctorDrawable.h b/libs/hwui/pipeline/skia/GLFunctorDrawable.h index 012c948be9b3..af57d7d33c2c 100644 --- a/libs/hwui/pipeline/skia/GLFunctorDrawable.h +++ b/libs/hwui/pipeline/skia/GLFunctorDrawable.h @@ -19,8 +19,8 @@ #include <SkCanvas.h> #include <SkDrawable.h> -#include <utils/RefBase.h> #include <utils/Functor.h> +#include <utils/RefBase.h> namespace android { namespace uirenderer { @@ -36,24 +36,21 @@ namespace skiapipeline { class GLFunctorDrawable : public SkDrawable { public: GLFunctorDrawable(Functor* functor, GlFunctorLifecycleListener* listener, SkCanvas* canvas) - : mFunctor(functor) - , mListener(listener) - , mBounds(canvas->getLocalClipBounds()) - {} + : mFunctor(functor), mListener(listener), mBounds(canvas->getLocalClipBounds()) {} virtual ~GLFunctorDrawable(); void syncFunctor() const; - protected: +protected: virtual SkRect onGetBounds() override { return mBounds; } virtual void onDraw(SkCanvas* canvas) override; - private: - Functor* mFunctor; - sp<GlFunctorLifecycleListener> mListener; - const SkRect mBounds; +private: + Functor* mFunctor; + sp<GlFunctorLifecycleListener> mListener; + const SkRect mBounds; }; -}; // namespace skiapipeline -}; // namespace uirenderer -}; // namespace android +}; // namespace skiapipeline +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/pipeline/skia/LayerDrawable.cpp b/libs/hwui/pipeline/skia/LayerDrawable.cpp index 4feeb2d6facb..d9584db2df9d 100644 --- a/libs/hwui/pipeline/skia/LayerDrawable.cpp +++ b/libs/hwui/pipeline/skia/LayerDrawable.cpp @@ -14,8 +14,8 @@ * limitations under the License. */ -#include "GlLayer.h" #include "LayerDrawable.h" +#include "GlLayer.h" #include "VkLayer.h" #include "GrBackendSurface.h" @@ -28,29 +28,32 @@ namespace uirenderer { namespace skiapipeline { void LayerDrawable::onDraw(SkCanvas* canvas) { - DrawLayer(canvas->getGrContext(), canvas, mLayer.get()); + Layer* layer = mLayerUpdater->backingLayer(); + if (layer) { + DrawLayer(canvas->getGrContext(), canvas, layer); + } } bool LayerDrawable::DrawLayer(GrContext* context, SkCanvas* canvas, Layer* layer) { - // transform the matrix based on the layer - int saveCount = -1; - if (!layer->getTransform().isIdentity()) { - saveCount = canvas->save(); - SkMatrix transform; - layer->getTransform().copyTo(transform); - canvas->concat(transform); + if (context == nullptr) { + SkDEBUGF(("Attempting to draw LayerDrawable into an unsupported surface")); + return false; } - + // transform the matrix based on the layer + SkMatrix layerTransform; + layer->getTransform().copyTo(layerTransform); sk_sp<SkImage> layerImage; + int layerWidth = layer->getWidth(); + int layerHeight = layer->getHeight(); if (layer->getApi() == Layer::Api::OpenGL) { GlLayer* glLayer = static_cast<GlLayer*>(layer); GrGLTextureInfo externalTexture; externalTexture.fTarget = glLayer->getRenderTarget(); externalTexture.fID = glLayer->getTextureId(); - GrBackendTexture backendTexture(glLayer->getWidth(), glLayer->getHeight(), - kRGBA_8888_GrPixelConfig, externalTexture); + GrBackendTexture backendTexture(layerWidth, layerHeight, kRGBA_8888_GrPixelConfig, + externalTexture); layerImage = SkImage::MakeFromTexture(context, backendTexture, kTopLeft_GrSurfaceOrigin, - kPremul_SkAlphaType, nullptr); + kPremul_SkAlphaType, nullptr); } else { SkASSERT(layer->getApi() == Layer::Api::Vulkan); VkLayer* vkLayer = static_cast<VkLayer*>(layer); @@ -59,20 +62,42 @@ bool LayerDrawable::DrawLayer(GrContext* context, SkCanvas* canvas, Layer* layer } if (layerImage) { + SkMatrix textureMatrix; + layer->getTexTransform().copyTo(textureMatrix); + // TODO: after skia bug https://bugs.chromium.org/p/skia/issues/detail?id=7075 is fixed + // use bottom left origin and remove flipV and invert transformations. + SkMatrix flipV; + flipV.setAll(1, 0, 0, 0, -1, 1, 0, 0, 1); + textureMatrix.preConcat(flipV); + textureMatrix.preScale(1.0f / layerWidth, 1.0f / layerHeight); + textureMatrix.postScale(layerWidth, layerHeight); + SkMatrix textureMatrixInv; + if (!textureMatrix.invert(&textureMatrixInv)) { + textureMatrixInv = textureMatrix; + } + + SkMatrix matrix = SkMatrix::Concat(layerTransform, textureMatrixInv); + SkPaint paint; paint.setAlpha(layer->getAlpha()); paint.setBlendMode(layer->getMode()); paint.setColorFilter(sk_ref_sp(layer->getColorFilter())); - canvas->drawImage(layerImage, 0, 0, &paint); - } - // restore the original matrix - if (saveCount >= 0) { - canvas->restoreToCount(saveCount); + + const bool nonIdentityMatrix = !matrix.isIdentity(); + if (nonIdentityMatrix) { + canvas->save(); + canvas->concat(matrix); + } + canvas->drawImage(layerImage.get(), 0, 0, &paint); + // restore the original matrix + if (nonIdentityMatrix) { + canvas->restore(); + } } return layerImage; } -}; // namespace skiapipeline -}; // namespace uirenderer -}; // namespace android +}; // namespace skiapipeline +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/pipeline/skia/LayerDrawable.h b/libs/hwui/pipeline/skia/LayerDrawable.h index 431989519a70..345038769306 100644 --- a/libs/hwui/pipeline/skia/LayerDrawable.h +++ b/libs/hwui/pipeline/skia/LayerDrawable.h @@ -16,7 +16,7 @@ #pragma once -#include "Layer.h" +#include "DeferredLayerUpdater.h" #include <SkCanvas.h> #include <SkDrawable.h> @@ -29,21 +29,21 @@ namespace skiapipeline { * Draws a layer backed by an OpenGL texture into a SkCanvas. */ class LayerDrawable : public SkDrawable { - public: - explicit LayerDrawable(Layer* layer) - : mLayer(layer) {} +public: + explicit LayerDrawable(DeferredLayerUpdater* layerUpdater) : mLayerUpdater(layerUpdater) {} static bool DrawLayer(GrContext* context, SkCanvas* canvas, Layer* layer); - protected: - virtual SkRect onGetBounds() override { - return SkRect::MakeWH(mLayer->getWidth(), mLayer->getHeight()); - } - virtual void onDraw(SkCanvas* canvas) override; + +protected: + virtual SkRect onGetBounds() override { + return SkRect::MakeWH(mLayerUpdater->getWidth(), mLayerUpdater->getHeight()); + } + virtual void onDraw(SkCanvas* canvas) override; private: - sp<Layer> mLayer; + sp<DeferredLayerUpdater> mLayerUpdater; }; -}; // namespace skiapipeline -}; // namespace uirenderer -}; // namespace android +}; // namespace skiapipeline +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp index 4ee47afe87fd..77925fd87fc7 100644 --- a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp +++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp @@ -15,6 +15,7 @@ */ #include "RenderNodeDrawable.h" +#include <SkPaintFilterCanvas.h> #include "RenderNode.h" #include "SkiaDisplayList.h" #include "SkiaPipeline.h" @@ -24,23 +25,24 @@ namespace android { namespace uirenderer { namespace skiapipeline { -void RenderNodeDrawable::drawBackwardsProjectedNodes(SkCanvas* canvas, const SkiaDisplayList& displayList, - int nestLevel) { +void RenderNodeDrawable::drawBackwardsProjectedNodes(SkCanvas* canvas, + const SkiaDisplayList& displayList, + int nestLevel) { LOG_ALWAYS_FATAL_IF(0 == nestLevel && !displayList.mProjectionReceiver); for (auto& child : displayList.mChildNodes) { const RenderProperties& childProperties = child.getNodeProperties(); - //immediate children cannot be projected on their parent + // immediate children cannot be projected on their parent if (childProperties.getProjectBackwards() && nestLevel > 0) { SkAutoCanvasRestore acr2(canvas, true); - //Apply recorded matrix, which is a total matrix saved at recording time to avoid - //replaying all DL commands. + // Apply recorded matrix, which is a total matrix saved at recording time to avoid + // replaying all DL commands. canvas->concat(child.getRecordedMatrix()); child.drawContent(canvas); } - //skip walking sub-nodes if current display list contains a receiver with exception of - //level 0, which is a known receiver + // skip walking sub-nodes if current display list contains a receiver with exception of + // level 0, which is a known receiver if (0 == nestLevel || !displayList.containsProjectionReceiver()) { SkAutoCanvasRestore acr(canvas, true); SkMatrix nodeMatrix; @@ -50,9 +52,9 @@ void RenderNodeDrawable::drawBackwardsProjectedNodes(SkCanvas* canvas, const Ski hwuiMatrix.copyTo(nodeMatrix); canvas->concat(nodeMatrix); SkiaDisplayList* childDisplayList = static_cast<SkiaDisplayList*>( - (const_cast<DisplayList*>(childNode->getDisplayList()))); + (const_cast<DisplayList*>(childNode->getDisplayList()))); if (childDisplayList) { - drawBackwardsProjectedNodes(canvas, *childDisplayList, nestLevel+1); + drawBackwardsProjectedNodes(canvas, *childDisplayList, nestLevel + 1); } } } @@ -91,8 +93,8 @@ const RenderProperties& RenderNodeDrawable::getNodeProperties() const { } void RenderNodeDrawable::onDraw(SkCanvas* canvas) { - //negative and positive Z order are drawn out of order, if this render node drawable is in - //a reordering section + // negative and positive Z order are drawn out of order, if this render node drawable is in + // a reordering section if ((!mInReorderingSection) || MathUtils::isZero(mRenderNode->properties().getZ())) { this->forceDraw(canvas); } @@ -100,7 +102,7 @@ void RenderNodeDrawable::onDraw(SkCanvas* canvas) { void RenderNodeDrawable::forceDraw(SkCanvas* canvas) { RenderNode* renderNode = mRenderNode.get(); - if (SkiaPipeline::skpCaptureEnabled()) { + if (CC_UNLIKELY(Properties::skpCaptureEnabled)) { SkRect dimensions = SkRect::MakeWH(renderNode->getWidth(), renderNode->getHeight()); canvas->drawAnnotation(dimensions, renderNode->getName(), nullptr); } @@ -108,7 +110,8 @@ void RenderNodeDrawable::forceDraw(SkCanvas* canvas) { // We only respect the nothingToDraw check when we are composing a layer. This // ensures that we paint the layer even if it is not currently visible in the // event that the properties change and it becomes visible. - if (!renderNode->isRenderable() || (renderNode->nothingToDraw() && mComposeLayer)) { + if ((mProjectedDisplayList == nullptr && !renderNode->isRenderable()) || + (renderNode->nothingToDraw() && mComposeLayer)) { return; } @@ -117,13 +120,13 @@ void RenderNodeDrawable::forceDraw(SkCanvas* canvas) { SkAutoCanvasRestore acr(canvas, true); const RenderProperties& properties = this->getNodeProperties(); - //pass this outline to the children that may clip backward projected nodes - displayList->mProjectedOutline = displayList->containsProjectionReceiver() - ? &properties.getOutline() : nullptr; + // pass this outline to the children that may clip backward projected nodes + displayList->mProjectedOutline = + displayList->containsProjectionReceiver() ? &properties.getOutline() : nullptr; if (!properties.getProjectBackwards()) { drawContent(canvas); if (mProjectedDisplayList) { - acr.restore(); //draw projected children using parent matrix + acr.restore(); // draw projected children using parent matrix LOG_ALWAYS_FATAL_IF(!mProjectedDisplayList->mProjectedOutline); const bool shouldClip = mProjectedDisplayList->mProjectedOutline->getPath(); SkAutoCanvasRestore acr2(canvas, shouldClip); @@ -137,12 +140,11 @@ void RenderNodeDrawable::forceDraw(SkCanvas* canvas) { displayList->mProjectedOutline = nullptr; } -static bool layerNeedsPaint(const LayerProperties& properties, - float alphaMultiplier, SkPaint* paint) { - if (alphaMultiplier < 1.0f - || properties.alpha() < 255 - || properties.xferMode() != SkBlendMode::kSrcOver - || properties.colorFilter() != nullptr) { +static bool layerNeedsPaint(const LayerProperties& properties, float alphaMultiplier, + SkPaint* paint) { + paint->setFilterQuality(kLow_SkFilterQuality); + if (alphaMultiplier < 1.0f || properties.alpha() < 255 || + properties.xferMode() != SkBlendMode::kSrcOver || properties.colorFilter() != nullptr) { paint->setAlpha(properties.alpha() * alphaMultiplier); paint->setBlendMode(properties.xferMode()); paint->setColorFilter(sk_ref_sp(properties.colorFilter())); @@ -151,6 +153,29 @@ static bool layerNeedsPaint(const LayerProperties& properties, return false; } +class AlphaFilterCanvas : public SkPaintFilterCanvas { +public: + AlphaFilterCanvas(SkCanvas* canvas, float alpha) : SkPaintFilterCanvas(canvas), mAlpha(alpha) {} + +protected: + bool onFilter(SkTCopyOnFirstWrite<SkPaint>* paint, Type t) const override { + SkTLazy<SkPaint> defaultPaint; + if (!*paint) { + paint->init(*defaultPaint.init()); + } + paint->writable()->setAlpha((uint8_t)(*paint)->getAlpha() * mAlpha); + return true; + } + void onDrawDrawable(SkDrawable* drawable, const SkMatrix* matrix) override { + // We unroll the drawable using "this" canvas, so that draw calls contained inside will + // get their alpha applied. THe default SkPaintFilterCanvas::onDrawDrawable does not unroll. + drawable->draw(this, matrix); + } + +private: + float mAlpha; +}; + void RenderNodeDrawable::drawContent(SkCanvas* canvas) const { RenderNode* renderNode = mRenderNode.get(); float alphaMultiplier = 1.0f; @@ -167,7 +192,7 @@ void RenderNodeDrawable::drawContent(SkCanvas* canvas) const { displayList->mProjectedReceiverParentMatrix = canvas->getTotalMatrix(); } - //TODO should we let the bound of the drawable do this for us? + // TODO should we let the bound of the drawable do this for us? const SkRect bounds = SkRect::MakeWH(properties.getWidth(), properties.getHeight()); bool quickRejected = properties.getClipToBounds() && canvas->quickReject(bounds); if (!quickRejected) { @@ -176,12 +201,15 @@ void RenderNodeDrawable::drawContent(SkCanvas* canvas) const { // composing a hardware layer if (renderNode->getLayerSurface() && mComposeLayer) { SkASSERT(properties.effectiveLayerType() == LayerType::RenderLayer); - SkPaint* paint = nullptr; - SkPaint tmpPaint; - if (layerNeedsPaint(layerProperties, alphaMultiplier, &tmpPaint)) { - paint = &tmpPaint; - } - renderNode->getLayerSurface()->draw(canvas, 0, 0, paint); + SkPaint paint; + layerNeedsPaint(layerProperties, alphaMultiplier, &paint); + + // surfaces for layers are created on LAYER_SIZE boundaries (which are >= layer size) so + // we need to restrict the portion of the surface drawn to the size of the renderNode. + SkASSERT(renderNode->getLayerSurface()->width() >= bounds.width()); + SkASSERT(renderNode->getLayerSurface()->height() >= bounds.height()); + canvas->drawImageRect(renderNode->getLayerSurface()->makeImageSnapshot().get(), + bounds, bounds, &paint); if (!renderNode->getSkiaLayer()->hasRenderedSinceRepaint) { renderNode->getSkiaLayer()->hasRenderedSinceRepaint = true; @@ -199,7 +227,7 @@ void RenderNodeDrawable::drawContent(SkCanvas* canvas) const { } } - // composing a software layer with alpha + // composing a software layer with alpha } else if (properties.effectiveLayerType() == LayerType::Software) { SkPaint paint; bool needsLayer = layerNeedsPaint(layerProperties, alphaMultiplier, &paint); @@ -211,13 +239,20 @@ void RenderNodeDrawable::drawContent(SkCanvas* canvas) const { canvas->restore(); } } else { - displayList->draw(canvas); + if (alphaMultiplier < 1.0f) { + // Non-layer draw for a view with getHasOverlappingRendering=false, will apply + // the alpha to the paint of each nested draw. + AlphaFilterCanvas alphaCanvas(canvas, alphaMultiplier); + displayList->draw(&alphaCanvas); + } else { + displayList->draw(canvas); + } } } } void RenderNodeDrawable::setViewProperties(const RenderProperties& properties, SkCanvas* canvas, - float* alphaMultiplier) { + float* alphaMultiplier) { if (properties.getLeft() != 0 || properties.getTop() != 0) { canvas->translate(properties.getLeft(), properties.getTop()); } @@ -237,7 +272,7 @@ void RenderNodeDrawable::setViewProperties(const RenderProperties& properties, S int clipFlags = properties.getClippingFlags(); if (properties.getAlpha() < 1) { if (isLayer) { - clipFlags &= ~CLIP_TO_BOUNDS; // bounds clipping done by layer + clipFlags &= ~CLIP_TO_BOUNDS; // bounds clipping done by layer } if (CC_LIKELY(isLayer || !properties.getHasOverlappingRendering())) { *alphaMultiplier = properties.getAlpha(); @@ -246,18 +281,18 @@ void RenderNodeDrawable::setViewProperties(const RenderProperties& properties, S Rect layerBounds(0, 0, properties.getWidth(), properties.getHeight()); if (clipFlags) { properties.getClippingRectForFlags(clipFlags, &layerBounds); - clipFlags = 0; // all clipping done by savelayer + clipFlags = 0; // all clipping done by savelayer } - SkRect bounds = SkRect::MakeLTRB(layerBounds.left, layerBounds.top, - layerBounds.right, layerBounds.bottom); - canvas->saveLayerAlpha(&bounds, (int) (properties.getAlpha() * 255)); + SkRect bounds = SkRect::MakeLTRB(layerBounds.left, layerBounds.top, layerBounds.right, + layerBounds.bottom); + canvas->saveLayerAlpha(&bounds, (int)(properties.getAlpha() * 255)); } if (CC_UNLIKELY(ATRACE_ENABLED() && properties.promotedToLayer())) { // pretend alpha always causes savelayer to warn about // performance problem affecting old versions ATRACE_FORMAT("alpha caused saveLayer %dx%d", properties.getWidth(), - properties.getHeight()); + properties.getHeight()); } } @@ -283,6 +318,6 @@ void RenderNodeDrawable::setViewProperties(const RenderProperties& properties, S } } -}; // namespace skiapipeline -}; // namespace uirenderer -}; // namespace android +}; // namespace skiapipeline +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.h b/libs/hwui/pipeline/skia/RenderNodeDrawable.h index 3eed6476c994..ef21cd8a29b5 100644 --- a/libs/hwui/pipeline/skia/RenderNodeDrawable.h +++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.h @@ -47,7 +47,7 @@ public: * layer into the canvas. */ explicit RenderNodeDrawable(RenderNode* node, SkCanvas* canvas, bool composeLayer = true, - bool inReorderingSection = false) + bool inReorderingSection = false) : mRenderNode(node) , mRecordedTransform(canvas->getTotalMatrix()) , mComposeLayer(composeLayer) @@ -113,13 +113,13 @@ private: * @param nestLevel should be always 0. Used to track how far we are from the receiver. */ void drawBackwardsProjectedNodes(SkCanvas* canvas, const SkiaDisplayList& displayList, - int nestLevel = 0); + int nestLevel = 0); /** * Applies the rendering properties of a view onto a SkCanvas. */ static void setViewProperties(const RenderProperties& properties, SkCanvas* canvas, - float* alphaMultiplier); + float* alphaMultiplier); /** * Stores transform on the canvas at time of recording and is used for @@ -150,6 +150,6 @@ private: SkiaDisplayList* mProjectedDisplayList = nullptr; }; -}; // namespace skiapipeline -}; // namespace uirenderer -}; // namespace android +}; // namespace skiapipeline +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp index 374d364787de..7b59ccf3eec7 100644 --- a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp +++ b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp @@ -30,26 +30,24 @@ namespace uirenderer { namespace skiapipeline { StartReorderBarrierDrawable::StartReorderBarrierDrawable(SkiaDisplayList* data) - : mEndChildIndex(0) - , mBeginChildIndex(data->mChildNodes.size()) - , mDisplayList(data) { -} + : mEndChildIndex(0), mBeginChildIndex(data->mChildNodes.size()), mDisplayList(data) {} void StartReorderBarrierDrawable::onDraw(SkCanvas* canvas) { if (mChildren.empty()) { - //mChildren is allocated and initialized only the first time onDraw is called and cached for - //subsequent calls + // mChildren is allocated and initialized only the first time onDraw is called and cached + // for + // subsequent calls mChildren.reserve(mEndChildIndex - mBeginChildIndex + 1); for (int i = mBeginChildIndex; i <= mEndChildIndex; i++) { mChildren.push_back(const_cast<RenderNodeDrawable*>(&mDisplayList->mChildNodes[i])); } } std::stable_sort(mChildren.begin(), mChildren.end(), - [](RenderNodeDrawable* a, RenderNodeDrawable* b) { - const float aZValue = a->getNodeProperties().getZ(); - const float bZValue = b->getNodeProperties().getZ(); - return aZValue < bZValue; - }); + [](RenderNodeDrawable* a, RenderNodeDrawable* b) { + const float aZValue = a->getNodeProperties().getZ(); + const float bZValue = b->getNodeProperties().getZ(); + return aZValue < bZValue; + }); size_t drawIndex = 0; const size_t endIndex = mChildren.size(); @@ -57,7 +55,7 @@ void StartReorderBarrierDrawable::onDraw(SkCanvas* canvas) { RenderNodeDrawable* childNode = mChildren[drawIndex]; SkASSERT(childNode); const float casterZ = childNode->getNodeProperties().getZ(); - if (casterZ >= -NON_ZERO_EPSILON) { //draw only children with negative Z + if (casterZ >= -NON_ZERO_EPSILON) { // draw only children with negative Z return; } childNode->forceDraw(canvas); @@ -85,8 +83,9 @@ void EndReorderBarrierDrawable::onDraw(SkCanvas* canvas) { size_t drawIndex = 0; const size_t endIndex = zChildren.size(); - while (drawIndex < endIndex //draw only children with positive Z - && zChildren[drawIndex]->getNodeProperties().getZ() <= NON_ZERO_EPSILON) drawIndex++; + while (drawIndex < endIndex // draw only children with positive Z + && zChildren[drawIndex]->getNodeProperties().getZ() <= NON_ZERO_EPSILON) + drawIndex++; size_t shadowIndex = drawIndex; float lastCasterZ = 0.0f; @@ -98,7 +97,7 @@ void EndReorderBarrierDrawable::onDraw(SkCanvas* canvas) { // OR if its caster's Z value is similar to the previous potential caster if (shadowIndex == drawIndex || casterZ - lastCasterZ < SHADOW_DELTA) { this->drawShadow(canvas, zChildren[shadowIndex]); - lastCasterZ = casterZ; // must do this even if current caster not casting a shadow + lastCasterZ = casterZ; // must do this even if current caster not casting a shadow shadowIndex++; continue; } @@ -116,23 +115,21 @@ void EndReorderBarrierDrawable::onDraw(SkCanvas* canvas) { void EndReorderBarrierDrawable::drawShadow(SkCanvas* canvas, RenderNodeDrawable* caster) { const RenderProperties& casterProperties = caster->getNodeProperties(); - if (casterProperties.getAlpha() <= 0.0f - || casterProperties.getOutline().getAlpha() <= 0.0f - || !casterProperties.getOutline().getPath() - || casterProperties.getScaleX() == 0 - || casterProperties.getScaleY() == 0) { + if (casterProperties.getAlpha() <= 0.0f || casterProperties.getOutline().getAlpha() <= 0.0f || + !casterProperties.getOutline().getPath() || casterProperties.getScaleX() == 0 || + casterProperties.getScaleY() == 0) { // no shadow to draw return; } - const SkScalar casterAlpha = casterProperties.getAlpha() - * casterProperties.getOutline().getAlpha(); + const SkScalar casterAlpha = + casterProperties.getAlpha() * casterProperties.getOutline().getAlpha(); if (casterAlpha <= 0.0f) { return; } - float ambientAlpha = (SkiaPipeline::getAmbientShadowAlpha()/255.f)*casterAlpha; - float spotAlpha = (SkiaPipeline::getSpotShadowAlpha()/255.f)*casterAlpha; + float ambientAlpha = (SkiaPipeline::getAmbientShadowAlpha() / 255.f) * casterAlpha; + float spotAlpha = (SkiaPipeline::getSpotShadowAlpha() / 255.f) * casterAlpha; const RevealClip& revealClip = casterProperties.getRevealClip(); const SkPath* revealClipPath = revealClip.getPath(); @@ -163,28 +160,22 @@ void EndReorderBarrierDrawable::drawShadow(SkCanvas* canvas, RenderNodeDrawable* hwuiMatrix.copyTo(shadowMatrix); canvas->concat(shadowMatrix); - const SkPath* casterOutlinePath = casterProperties.getOutline().getPath(); - // holds temporary SkPath to store the result of intersections - SkPath tmpPath; - const SkPath* casterPath = casterOutlinePath; + // default the shadow-casting path to the outline of the caster + const SkPath* casterPath = casterProperties.getOutline().getPath(); + + // intersect the shadow-casting path with the clipBounds, if present + if (clippedToBounds && !casterClipRect.contains(casterPath->getBounds())) { + casterPath = caster->getRenderNode()->getClippedOutline(casterClipRect); + } - // TODO: In to following course of code that calculates the final shape, is there an optimal - // of doing the Op calculations? // intersect the shadow-casting path with the reveal, if present + SkPath tmpPath; // holds temporary SkPath to store the result of intersections if (revealClipPath) { Op(*casterPath, *revealClipPath, kIntersect_SkPathOp, &tmpPath); tmpPath.setIsVolatile(true); casterPath = &tmpPath; } - // intersect the shadow-casting path with the clipBounds, if present - if (clippedToBounds) { - SkPath clipBoundsPath; - clipBoundsPath.addRect(casterClipRect); - Op(*casterPath, clipBoundsPath, kIntersect_SkPathOp, &tmpPath); - tmpPath.setIsVolatile(true); - casterPath = &tmpPath; - } const Vector3 lightPos = SkiaPipeline::getLightCenter(); SkPoint3 skiaLightPos = SkPoint3::Make(lightPos.x, lightPos.y, lightPos.z); SkPoint3 zParams; @@ -196,11 +187,12 @@ void EndReorderBarrierDrawable::drawShadow(SkCanvas* canvas, RenderNodeDrawable* } else { zParams = SkPoint3::Make(0, 0, casterProperties.getZ()); } - SkShadowUtils::DrawShadow(canvas, *casterPath, zParams, skiaLightPos, - SkiaPipeline::getLightRadius(), ambientAlpha, spotAlpha, SK_ColorBLACK, - casterAlpha < 1.0f ? SkShadowFlags::kTransparentOccluder_ShadowFlag : 0); + SkShadowUtils::DrawShadow( + canvas, *casterPath, zParams, skiaLightPos, SkiaPipeline::getLightRadius(), + ambientAlpha, spotAlpha, casterProperties.getShadowColor(), + casterAlpha < 1.0f ? SkShadowFlags::kTransparentOccluder_ShadowFlag : 0); } -}; // namespace skiapipeline -}; // namespace uirenderer -}; // namespace android +}; // namespace skiapipeline +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.h b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.h index 9f00d23ae985..3c48d3604864 100644 --- a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.h +++ b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.h @@ -41,9 +41,7 @@ public: explicit StartReorderBarrierDrawable(SkiaDisplayList* data); protected: - virtual SkRect onGetBounds() override { - return SkRect::MakeLargest(); - } + virtual SkRect onGetBounds() override { return SkRect::MakeLargest(); } virtual void onDraw(SkCanvas* canvas) override; private: @@ -65,16 +63,16 @@ private: class EndReorderBarrierDrawable : public SkDrawable { public: explicit EndReorderBarrierDrawable(StartReorderBarrierDrawable* startBarrier); + protected: - virtual SkRect onGetBounds() override { - return SkRect::MakeLargest(); - } + virtual SkRect onGetBounds() override { return SkRect::MakeLargest(); } virtual void onDraw(SkCanvas* canvas) override; + private: void drawShadow(SkCanvas* canvas, RenderNodeDrawable* caster); StartReorderBarrierDrawable* mStartBarrier; }; -}; // namespace skiapipeline -}; // namespace uirenderer -}; // namespace android +}; // namespace skiapipeline +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/pipeline/skia/ShaderCache.cpp b/libs/hwui/pipeline/skia/ShaderCache.cpp new file mode 100644 index 000000000000..2fa56f613144 --- /dev/null +++ b/libs/hwui/pipeline/skia/ShaderCache.cpp @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ShaderCache.h" +#include <algorithm> +#include <log/log.h> +#include <thread> +#include "FileBlobCache.h" +#include "Properties.h" +#include "utils/TraceUtils.h" + +namespace android { +namespace uirenderer { +namespace skiapipeline { + +// Cache size limits. +static const size_t maxKeySize = 1024; +static const size_t maxValueSize = 64 * 1024; +static const size_t maxTotalSize = 512 * 1024; + +ShaderCache::ShaderCache() { + // There is an "incomplete FileBlobCache type" compilation error, if ctor is moved to header. +} + +ShaderCache ShaderCache::sCache; + +ShaderCache& ShaderCache::get() { + return sCache; +} + +void ShaderCache::initShaderDiskCache() { + ATRACE_NAME("initShaderDiskCache"); + std::lock_guard<std::mutex> lock(mMutex); + + // Emulators can switch between different renders either as part of config + // or snapshot migration. Also, program binaries may not work well on some + // desktop / laptop GPUs. Thus, disable the shader disk cache for emulator builds. + if (!Properties::runningInEmulator && mFilename.length() > 0) { + mBlobCache.reset(new FileBlobCache(maxKeySize, maxValueSize, maxTotalSize, mFilename)); + mInitialized = true; + } +} + +void ShaderCache::setFilename(const char* filename) { + std::lock_guard<std::mutex> lock(mMutex); + mFilename = filename; +} + +BlobCache* ShaderCache::getBlobCacheLocked() { + LOG_ALWAYS_FATAL_IF(!mInitialized, "ShaderCache has not been initialized"); + return mBlobCache.get(); +} + +sk_sp<SkData> ShaderCache::load(const SkData& key) { + ATRACE_NAME("ShaderCache::load"); + size_t keySize = key.size(); + std::lock_guard<std::mutex> lock(mMutex); + if (!mInitialized) { + return nullptr; + } + + // mObservedBlobValueSize is reasonably big to avoid memory reallocation + // Allocate a buffer with malloc. SkData takes ownership of that allocation and will call free. + void* valueBuffer = malloc(mObservedBlobValueSize); + if (!valueBuffer) { + return nullptr; + } + BlobCache* bc = getBlobCacheLocked(); + size_t valueSize = bc->get(key.data(), keySize, valueBuffer, mObservedBlobValueSize); + int maxTries = 3; + while (valueSize > mObservedBlobValueSize && maxTries > 0) { + mObservedBlobValueSize = std::min(valueSize, maxValueSize); + valueBuffer = realloc(valueBuffer, mObservedBlobValueSize); + if (!valueBuffer) { + return nullptr; + } + valueSize = bc->get(key.data(), keySize, valueBuffer, mObservedBlobValueSize); + maxTries--; + } + if (!valueSize) { + free(valueBuffer); + return nullptr; + } + if (valueSize > mObservedBlobValueSize) { + ALOGE("ShaderCache::load value size is too big %d", (int) valueSize); + free(valueBuffer); + return nullptr; + } + return SkData::MakeFromMalloc(valueBuffer, valueSize); +} + +void ShaderCache::store(const SkData& key, const SkData& data) { + ATRACE_NAME("ShaderCache::store"); + std::lock_guard<std::mutex> lock(mMutex); + + if (!mInitialized) { + return; + } + + size_t valueSize = data.size(); + size_t keySize = key.size(); + if (keySize == 0 || valueSize == 0 || valueSize >= maxValueSize) { + ALOGW("ShaderCache::store: sizes %d %d not allowed", (int)keySize, (int)valueSize); + return; + } + + const void* value = data.data(); + + BlobCache* bc = getBlobCacheLocked(); + bc->set(key.data(), keySize, value, valueSize); + + if (!mSavePending && mDeferredSaveDelay > 0) { + mSavePending = true; + std::thread deferredSaveThread([this]() { + sleep(mDeferredSaveDelay); + std::lock_guard<std::mutex> lock(mMutex); + ATRACE_NAME("ShaderCache::saveToDisk"); + if (mInitialized && mBlobCache) { + mBlobCache->writeToFile(); + } + mSavePending = false; + }); + deferredSaveThread.detach(); + } +} + +} /* namespace skiapipeline */ +} /* namespace uirenderer */ +} /* namespace android */ diff --git a/libs/hwui/pipeline/skia/ShaderCache.h b/libs/hwui/pipeline/skia/ShaderCache.h new file mode 100644 index 000000000000..27473d67bd1a --- /dev/null +++ b/libs/hwui/pipeline/skia/ShaderCache.h @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <cutils/compiler.h> +#include <memory> +#include <mutex> +#include <string> +#include <vector> +#include <GrContextOptions.h> + +namespace android { + +class BlobCache; +class FileBlobCache; + +namespace uirenderer { +namespace skiapipeline { + +class ShaderCache : public GrContextOptions::PersistentCache { +public: + /** + * "get" returns a pointer to the singleton ShaderCache object. This + * singleton object will never be destroyed. + */ + ANDROID_API static ShaderCache& get(); + + /** + * "initShaderDiskCache" loads the serialized cache contents from disk and puts the ShaderCache + * into an initialized state, such that it is able to insert and retrieve entries from the + * cache. This should be called when HWUI pipeline is initialized. When not in the initialized + * state the load and store methods will return without performing any cache operations. + */ + virtual void initShaderDiskCache(); + + /** + * "setFilename" sets the name of the file that should be used to store + * cache contents from one program invocation to another. This function does not perform any + * disk operation and it should be invoked before "initShaderCache". + */ + virtual void setFilename(const char* filename); + + /** + * "load" attempts to retrieve the value blob associated with a given key + * blob from cache. This will be called by Skia, when it needs to compile a new SKSL shader. + */ + sk_sp<SkData> load(const SkData& key) override; + + /** + * "store" attempts to insert a new key/value blob pair into the cache. + * This will be called by Skia after it compiled a new SKSL shader + */ + void store(const SkData& key, const SkData& data) override; + +private: + // Creation and (the lack of) destruction is handled internally. + ShaderCache(); + + // Copying is disallowed. + ShaderCache(const ShaderCache&) = delete; + void operator=(const ShaderCache&) = delete; + + /** + * "getBlobCacheLocked" returns the BlobCache object being used to store the + * key/value blob pairs. If the BlobCache object has not yet been created, + * this will do so, loading the serialized cache contents from disk if + * possible. + */ + BlobCache* getBlobCacheLocked(); + + /** + * "mInitialized" indicates whether the ShaderCache is in the initialized + * state. It is initialized to false at construction time, and gets set to + * true when initialize is called. + * When in this state, the cache behaves as normal. When not, + * the load and store methods will return without performing any cache + * operations. + */ + bool mInitialized = false; + + /** + * "mBlobCache" is the cache in which the key/value blob pairs are stored. It + * is initially NULL, and will be initialized by getBlobCacheLocked the + * first time it's needed. + * The blob cache contains the Android build number. We treat version mismatches as an empty + * cache (logic implemented in BlobCache::unflatten). + */ + std::unique_ptr<FileBlobCache> mBlobCache; + + /** + * "mFilename" is the name of the file for storing cache contents in between + * program invocations. It is initialized to an empty string at + * construction time, and can be set with the setCacheFilename method. An + * empty string indicates that the cache should not be saved to or restored + * from disk. + */ + std::string mFilename; + + /** + * "mSavePending" indicates whether or not a deferred save operation is + * pending. Each time a key/value pair is inserted into the cache via + * load, a deferred save is initiated if one is not already pending. + * This will wait some amount of time and then trigger a save of the cache + * contents to disk. + */ + bool mSavePending = false; + + /** + * "mObservedBlobValueSize" is the maximum value size observed by the cache reading function. + */ + size_t mObservedBlobValueSize = 20*1024; + + /** + * The time in seconds to wait before saving newly inserted cache entries. + */ + unsigned int mDeferredSaveDelay = 4; + + /** + * "mMutex" is the mutex used to prevent concurrent access to the member + * variables. It must be locked whenever the member variables are accessed. + */ + mutable std::mutex mMutex; + + /** + * "sCache" is the singleton ShaderCache object. + */ + static ShaderCache sCache; + + friend class ShaderCacheTestUtils; //used for unit testing +}; + +} /* namespace skiapipeline */ +} /* namespace uirenderer */ +} /* namespace android */ diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp index 3ddc09fbeca1..cf0b6a4d1dcc 100644 --- a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp +++ b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp @@ -16,14 +16,13 @@ #include "SkiaDisplayList.h" -#include "renderthread/CanvasContext.h" -#include "VectorDrawable.h" #include "DumpOpsCanvas.h" #include "SkiaPipeline.h" +#include "VectorDrawable.h" +#include "renderthread/CanvasContext.h" #include <SkImagePriv.h> - namespace android { namespace uirenderer { namespace skiapipeline { @@ -32,6 +31,9 @@ void SkiaDisplayList::syncContents() { for (auto& functor : mChildFunctors) { functor.syncFunctor(); } + for (auto& animatedImage : mAnimatedImages) { + animatedImage->syncProperties(); + } for (auto& vectorDrawable : mVectorDrawables) { vectorDrawable->syncProperties(); } @@ -49,8 +51,8 @@ void SkiaDisplayList::updateChildren(std::function<void(RenderNode*)> updateFn) } } -bool SkiaDisplayList::prepareListAndChildren(TreeObserver& observer, TreeInfo& info, - bool functorsNeedLayer, +bool SkiaDisplayList::prepareListAndChildren( + TreeObserver& observer, TreeInfo& info, bool functorsNeedLayer, std::function<void(RenderNode*, TreeObserver&, TreeInfo&, bool)> childFn) { // If the prepare tree is triggered by the UI thread and no previous call to // pinImages has failed then we must pin all mutable images in the GPU cache @@ -64,7 +66,7 @@ bool SkiaDisplayList::prepareListAndChildren(TreeObserver& observer, TreeInfo& i } bool hasBackwardProjectedNodesHere = false; - bool hasBackwardProjectedNodesSubtree= false; + bool hasBackwardProjectedNodesSubtree = false; for (auto& child : mChildNodes) { hasBackwardProjectedNodesHere |= child.getNodeProperties().getProjectBackwards(); @@ -78,23 +80,37 @@ bool SkiaDisplayList::prepareListAndChildren(TreeObserver& observer, TreeInfo& i info.damageAccumulator->popTransform(); } - //The purpose of next block of code is to reset projected display list if there are no - //backward projected nodes. This speeds up drawing, by avoiding an extra walk of the tree + // The purpose of next block of code is to reset projected display list if there are no + // backward projected nodes. This speeds up drawing, by avoiding an extra walk of the tree if (mProjectionReceiver) { - mProjectionReceiver->setProjectedDisplayList(hasBackwardProjectedNodesSubtree ? this : nullptr); + mProjectionReceiver->setProjectedDisplayList(hasBackwardProjectedNodesSubtree ? this + : nullptr); info.hasBackwardProjectedNodes = hasBackwardProjectedNodesHere; } else { - info.hasBackwardProjectedNodes = hasBackwardProjectedNodesSubtree - || hasBackwardProjectedNodesHere; + info.hasBackwardProjectedNodes = + hasBackwardProjectedNodesSubtree || hasBackwardProjectedNodesHere; } bool isDirty = false; + for (auto& animatedImage : mAnimatedImages) { + // If any animated image in the display list needs updated, then damage the node. + if (animatedImage->isDirty()) { + isDirty = true; + } + if (animatedImage->isRunning()) { + static_cast<SkiaPipeline*>(info.canvasContext.getRenderPipeline()) + ->scheduleDeferredUpdate(animatedImage); + info.out.hasAnimations = true; + } + } + for (auto& vectorDrawable : mVectorDrawables) { // If any vector drawable in the display list needs update, damage the node. if (vectorDrawable->isDirty()) { isDirty = true; static_cast<SkiaPipeline*>(info.canvasContext.getRenderPipeline()) - ->getVectorDrawables()->push_back(vectorDrawable); + ->getVectorDrawables() + ->push_back(vectorDrawable); } vectorDrawable->setPropertyChangeWillBeConsumed(true); } @@ -108,6 +124,7 @@ void SkiaDisplayList::reset() { mMutableImages.clear(); mVectorDrawables.clear(); + mAnimatedImages.clear(); mChildFunctors.clear(); mChildNodes.clear(); @@ -121,6 +138,6 @@ void SkiaDisplayList::output(std::ostream& output, uint32_t level) { mDisplayList.draw(&canvas); } -}; // namespace skiapipeline -}; // namespace uirenderer -}; // namespace android +}; // namespace skiapipeline +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.h b/libs/hwui/pipeline/skia/SkiaDisplayList.h index 66375d13826c..818ec114a5b3 100644 --- a/libs/hwui/pipeline/skia/SkiaDisplayList.h +++ b/libs/hwui/pipeline/skia/SkiaDisplayList.h @@ -17,12 +17,13 @@ #pragma once #include "DisplayList.h" +#include "hwui/AnimatedImageDrawable.h" #include "GLFunctorDrawable.h" #include "RenderNodeDrawable.h" -#include <deque> #include <SkLiteDL.h> #include <SkLiteRecorder.h> +#include <deque> namespace android { namespace uirenderer { @@ -62,7 +63,7 @@ public: * need to monitor that they don't extend beyond the lifetime of the class * that creates them. Allocator dtor invokes all SkDrawable dtors. */ - template<class T, typename... Params> + template <class T, typename... Params> SkDrawable* allocateDrawable(Params&&... params) { return allocator.create<T>(std::forward<Params>(params)...); } @@ -113,7 +114,8 @@ public: * to subclass from DisplayList */ - bool prepareListAndChildren(TreeObserver& observer, TreeInfo& info, bool functorsNeedLayer, + bool prepareListAndChildren( + TreeObserver& observer, TreeInfo& info, bool functorsNeedLayer, std::function<void(RenderNode*, TreeObserver&, TreeInfo&, bool)> childFn) override; /** @@ -143,26 +145,28 @@ public: std::deque<GLFunctorDrawable> mChildFunctors; std::vector<SkImage*> mMutableImages; std::vector<VectorDrawableRoot*> mVectorDrawables; + std::vector<AnimatedImageDrawable*> mAnimatedImages; SkLiteDL mDisplayList; - //mProjectionReceiver points to a child node (stored in mChildNodes) that is as a projection - //receiver. It is set at record time and used at both prepare and draw tree traversals to - //make sure backward projected nodes are found and drawn immediately after mProjectionReceiver. + // mProjectionReceiver points to a child node (stored in mChildNodes) that is as a projection + // receiver. It is set at record time and used at both prepare and draw tree traversals to + // make sure backward projected nodes are found and drawn immediately after mProjectionReceiver. RenderNodeDrawable* mProjectionReceiver = nullptr; - //mProjectedOutline is valid only when render node tree is traversed during the draw pass. - //Render nodes that have a child receiver node, will store a pointer to their outline in - //mProjectedOutline. Child receiver node will apply the clip before any backward projected - //node is drawn. + // mProjectedOutline is valid only when render node tree is traversed during the draw pass. + // Render nodes that have a child receiver node, will store a pointer to their outline in + // mProjectedOutline. Child receiver node will apply the clip before any backward projected + // node is drawn. const Outline* mProjectedOutline = nullptr; - //mProjectedReceiverParentMatrix is valid when render node tree is traversed during the draw - //pass. Render nodes that have a child receiver node, will store their matrix in - //mProjectedReceiverParentMatrix. Child receiver node will set the matrix and then clip with the - //outline of their parent. + // mProjectedReceiverParentMatrix is valid when render node tree is traversed during the draw + // pass. Render nodes that have a child receiver node, will store their matrix in + // mProjectedReceiverParentMatrix. Child receiver node will set the matrix and then clip with + // the + // outline of their parent. SkMatrix mProjectedReceiverParentMatrix; }; -}; // namespace skiapipeline -}; // namespace uirenderer -}; // namespace android +}; // namespace skiapipeline +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/pipeline/skia/SkiaLayer.h b/libs/hwui/pipeline/skia/SkiaLayer.h index 904d57e073ca..82e6914e8b7f 100644 --- a/libs/hwui/pipeline/skia/SkiaLayer.h +++ b/libs/hwui/pipeline/skia/SkiaLayer.h @@ -26,14 +26,12 @@ namespace skiapipeline { /** * An offscreen rendering target used to contain the contents a RenderNode. */ -struct SkiaLayer -{ +struct SkiaLayer { sk_sp<SkSurface> layerSurface; Matrix4 inverseTransformInWindow; bool hasRenderedSinceRepaint = false; }; - } /* namespace skiapipeline */ } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp index 925db303461f..365d7403e046 100644 --- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp @@ -16,15 +16,15 @@ #include "SkiaOpenGLPipeline.h" -#include "hwui/Bitmap.h" #include "DeferredLayerUpdater.h" #include "GlLayer.h" #include "LayerDrawable.h" -#include "renderthread/EglManager.h" -#include "renderthread/Frame.h" -#include "renderstate/RenderState.h" #include "SkiaPipeline.h" #include "SkiaProfileRenderer.h" +#include "hwui/Bitmap.h" +#include "renderstate/RenderState.h" +#include "renderthread/EglManager.h" +#include "renderthread/Frame.h" #include "utils/TraceUtils.h" #include <GrBackendSurface.h> @@ -39,9 +39,7 @@ namespace uirenderer { namespace skiapipeline { SkiaOpenGLPipeline::SkiaOpenGLPipeline(RenderThread& thread) - : SkiaPipeline(thread) - , mEglManager(thread.eglManager()) { -} + : SkiaPipeline(thread), mEglManager(thread.eglManager()) {} MakeCurrentResult SkiaOpenGLPipeline::makeCurrent() { // TODO: Figure out why this workaround is needed, see b/13913604 @@ -55,27 +53,27 @@ MakeCurrentResult SkiaOpenGLPipeline::makeCurrent() { Frame SkiaOpenGLPipeline::getFrame() { LOG_ALWAYS_FATAL_IF(mEglSurface == EGL_NO_SURFACE, - "drawRenderNode called on a context with no surface!"); + "drawRenderNode called on a context with no surface!"); return mEglManager.beginFrame(mEglSurface); } -bool SkiaOpenGLPipeline::draw(const Frame& frame, const SkRect& screenDirty, - const SkRect& dirty, - const FrameBuilder::LightGeometry& lightGeometry, - LayerUpdateQueue* layerUpdateQueue, - const Rect& contentDrawBounds, bool opaque, bool wideColorGamut, - const BakedOpRenderer::LightInfo& lightInfo, - const std::vector<sp<RenderNode>>& renderNodes, - FrameInfoVisualizer* profiler) { - +bool SkiaOpenGLPipeline::draw(const Frame& frame, const SkRect& screenDirty, const SkRect& dirty, + const FrameBuilder::LightGeometry& lightGeometry, + LayerUpdateQueue* layerUpdateQueue, const Rect& contentDrawBounds, + bool opaque, bool wideColorGamut, + const BakedOpRenderer::LightInfo& lightInfo, + const std::vector<sp<RenderNode>>& renderNodes, + FrameInfoVisualizer* profiler) { mEglManager.damageFrame(frame, dirty); // setup surface for fbo0 GrGLFramebufferInfo fboInfo; fboInfo.fFBOID = 0; + GrPixelConfig pixelConfig = + wideColorGamut ? kRGBA_half_GrPixelConfig : kRGBA_8888_GrPixelConfig; GrBackendRenderTarget backendRT(frame.width(), frame.height(), 0, STENCIL_BUFFER_SIZE, - kRGBA_8888_GrPixelConfig, fboInfo); + pixelConfig, fboInfo); SkSurfaceProps props(0, kUnknown_SkPixelGeometry); @@ -84,13 +82,13 @@ bool SkiaOpenGLPipeline::draw(const Frame& frame, const SkRect& screenDirty, mRenderThread.getGrContext(), backendRT, kBottomLeft_GrSurfaceOrigin, nullptr, &props)); SkiaPipeline::updateLighting(lightGeometry, lightInfo); - renderFrame(*layerUpdateQueue, dirty, renderNodes, opaque, wideColorGamut, - contentDrawBounds, surface); + renderFrame(*layerUpdateQueue, dirty, renderNodes, opaque, wideColorGamut, contentDrawBounds, + surface); layerUpdateQueue->clear(); // Draw visual debugging features - if (CC_UNLIKELY(Properties::showDirtyRegions - || ProfileType::None != Properties::getProfileType())) { + if (CC_UNLIKELY(Properties::showDirtyRegions || + ProfileType::None != Properties::getProfileType())) { SkCanvas* profileCanvas = surface->getCanvas(); SkiaProfileRenderer profileRenderer(profileCanvas); profiler->draw(profileRenderer); @@ -105,9 +103,8 @@ bool SkiaOpenGLPipeline::draw(const Frame& frame, const SkRect& screenDirty, return true; } -bool SkiaOpenGLPipeline::swapBuffers(const Frame& frame, bool drew, - const SkRect& screenDirty, FrameInfo* currentFrameInfo, bool* requireSwap) { - +bool SkiaOpenGLPipeline::swapBuffers(const Frame& frame, bool drew, const SkRect& screenDirty, + FrameInfo* currentFrameInfo, bool* requireSwap) { GL_CHECKPOINT(LOW); // Even if we decided to cancel the frame, from the perspective of jank @@ -132,15 +129,30 @@ bool SkiaOpenGLPipeline::copyLayerInto(DeferredLayerUpdater* deferredLayer, SkBi deferredLayer->updateTexImage(); deferredLayer->apply(); - SkCanvas canvas(*bitmap); + /* This intermediate surface is present to work around a bug in SwiftShader that + * prevents us from reading the contents of the layer's texture directly. The + * workaround involves first rendering that texture into an intermediate buffer and + * then reading from the intermediate buffer into the bitmap. + */ + sk_sp<SkSurface> tmpSurface = SkSurface::MakeRenderTarget(mRenderThread.getGrContext(), + SkBudgeted::kYes, bitmap->info()); + Layer* layer = deferredLayer->backingLayer(); - return LayerDrawable::DrawLayer(mRenderThread.getGrContext(), &canvas, layer); + if (LayerDrawable::DrawLayer(mRenderThread.getGrContext(), tmpSurface->getCanvas(), layer)) { + sk_sp<SkImage> tmpImage = tmpSurface->makeImageSnapshot(); + if (tmpImage->readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(), 0, 0)) { + bitmap->notifyPixelsChanged(); + return true; + } + } + + return false; } static Layer* createLayer(RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight, - SkColorFilter* colorFilter, int alpha, SkBlendMode mode, bool blend) { - GlLayer* layer = new GlLayer(renderState, layerWidth, layerHeight, colorFilter, alpha, - mode, blend); + SkColorFilter* colorFilter, int alpha, SkBlendMode mode, bool blend) { + GlLayer* layer = + new GlLayer(renderState, layerWidth, layerHeight, colorFilter, alpha, mode, blend); layer->generateTexture(); return layer; } @@ -157,8 +169,7 @@ void SkiaOpenGLPipeline::onStop() { } bool SkiaOpenGLPipeline::setSurface(Surface* surface, SwapBehavior swapBehavior, - ColorMode colorMode) { - + ColorMode colorMode) { if (mEglSurface != EGL_NO_SURFACE) { mEglManager.destroySurface(mEglSurface); mEglSurface = EGL_NO_SURFACE; @@ -204,8 +215,7 @@ void SkiaOpenGLPipeline::invokeFunctor(const RenderThread& thread, Functor* func class AutoEglFence { public: - AutoEglFence(EGLDisplay display) - : mDisplay(display) { + AutoEglFence(EGLDisplay display) : mDisplay(display) { fence = eglCreateSyncKHR(mDisplay, EGL_SYNC_FENCE_KHR, NULL); } @@ -216,17 +226,17 @@ public: } EGLSyncKHR fence = EGL_NO_SYNC_KHR; + private: EGLDisplay mDisplay = EGL_NO_DISPLAY; }; class AutoEglImage { public: - AutoEglImage(EGLDisplay display, EGLClientBuffer clientBuffer) - : mDisplay(display) { - EGLint imageAttrs[] = { EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE }; - image = eglCreateImageKHR(display, EGL_NO_CONTEXT, - EGL_NATIVE_BUFFER_ANDROID, clientBuffer, imageAttrs); + AutoEglImage(EGLDisplay display, EGLClientBuffer clientBuffer) : mDisplay(display) { + EGLint imageAttrs[] = {EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE}; + image = eglCreateImageKHR(display, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, clientBuffer, + imageAttrs); } ~AutoEglImage() { @@ -236,6 +246,7 @@ public: } EGLImageKHR image = EGL_NO_IMAGE_KHR; + private: EGLDisplay mDisplay = EGL_NO_DISPLAY; }; @@ -247,16 +258,14 @@ public: glBindTexture(GL_TEXTURE_2D, mTexture); } - ~AutoSkiaGlTexture() { - glDeleteTextures(1, &mTexture); - } + ~AutoSkiaGlTexture() { glDeleteTextures(1, &mTexture); } private: GLuint mTexture = 0; }; sk_sp<Bitmap> SkiaOpenGLPipeline::allocateHardwareBitmap(renderthread::RenderThread& renderThread, - SkBitmap& skBitmap) { + SkBitmap& skBitmap) { renderThread.eglManager().initialize(); sk_sp<GrContext> grContext = sk_ref_sp(renderThread.getGrContext()); @@ -265,73 +274,67 @@ sk_sp<Bitmap> SkiaOpenGLPipeline::allocateHardwareBitmap(renderthread::RenderThr GLint format, type; bool isSupported = false; - //TODO: add support for linear blending (when ANDROID_ENABLE_LINEAR_BLENDING is defined) + // TODO: add support for linear blending (when ANDROID_ENABLE_LINEAR_BLENDING is defined) switch (info.colorType()) { - case kRGBA_8888_SkColorType: - isSupported = true; - // ARGB_4444 is upconverted to RGBA_8888 - case kARGB_4444_SkColorType: - pixelFormat = PIXEL_FORMAT_RGBA_8888; - format = GL_RGBA; - type = GL_UNSIGNED_BYTE; - break; - case kRGBA_F16_SkColorType: - isSupported = grContext->caps()->isConfigTexturable(kRGBA_half_GrPixelConfig); - if (isSupported) { - type = GL_HALF_FLOAT; - pixelFormat = PIXEL_FORMAT_RGBA_FP16; - } else { + case kRGBA_8888_SkColorType: + isSupported = true; + // ARGB_4444 is upconverted to RGBA_8888 + case kARGB_4444_SkColorType: + pixelFormat = PIXEL_FORMAT_RGBA_8888; + format = GL_RGBA; type = GL_UNSIGNED_BYTE; + break; + case kRGBA_F16_SkColorType: + isSupported = grContext->caps()->isConfigTexturable(kRGBA_half_GrPixelConfig); + if (isSupported) { + type = GL_HALF_FLOAT; + pixelFormat = PIXEL_FORMAT_RGBA_FP16; + } else { + type = GL_UNSIGNED_BYTE; + pixelFormat = PIXEL_FORMAT_RGBA_8888; + } + format = GL_RGBA; + break; + case kRGB_565_SkColorType: + isSupported = true; + pixelFormat = PIXEL_FORMAT_RGB_565; + format = GL_RGB; + type = GL_UNSIGNED_SHORT_5_6_5; + break; + case kGray_8_SkColorType: + isSupported = true; pixelFormat = PIXEL_FORMAT_RGBA_8888; - } - format = GL_RGBA; - break; - case kRGB_565_SkColorType: - isSupported = true; - pixelFormat = PIXEL_FORMAT_RGB_565; - format = GL_RGB; - type = GL_UNSIGNED_SHORT_5_6_5; - break; - case kGray_8_SkColorType: - isSupported = true; - pixelFormat = PIXEL_FORMAT_RGBA_8888; - format = GL_LUMINANCE; - type = GL_UNSIGNED_BYTE; - break; - default: - ALOGW("unable to create hardware bitmap of colortype: %d", info.colorType()); - return nullptr; - } - - auto colorSpace = info.colorSpace(); - bool convertToSRGB = false; - if (colorSpace && (!colorSpace->isSRGB())) { - isSupported = false; - convertToSRGB = true; + format = GL_LUMINANCE; + type = GL_UNSIGNED_BYTE; + break; + default: + ALOGW("unable to create hardware bitmap of colortype: %d", info.colorType()); + return nullptr; } SkBitmap bitmap; if (isSupported) { bitmap = skBitmap; } else { - bitmap.allocPixels(SkImageInfo::MakeN32(info.width(), info.height(), info.alphaType(), - nullptr)); + bitmap.allocPixels( + SkImageInfo::MakeN32(info.width(), info.height(), info.alphaType(), nullptr)); bitmap.eraseColor(0); - if (info.colorType() == kRGBA_F16_SkColorType || convertToSRGB) { + if (info.colorType() == kRGBA_F16_SkColorType) { // Drawing RGBA_F16 onto ARGB_8888 is not supported skBitmap.readPixels(bitmap.info().makeColorSpace(SkColorSpace::MakeSRGB()), - bitmap.getPixels(), bitmap.rowBytes(), 0, 0); + bitmap.getPixels(), bitmap.rowBytes(), 0, 0); } else { SkCanvas canvas(bitmap); canvas.drawBitmap(skBitmap, 0.0f, 0.0f, nullptr); } } - sp<GraphicBuffer> buffer = new GraphicBuffer(info.width(), info.height(), pixelFormat, - GraphicBuffer::USAGE_HW_TEXTURE | - GraphicBuffer::USAGE_SW_WRITE_NEVER | - GraphicBuffer::USAGE_SW_READ_NEVER, - std::string("Bitmap::allocateSkiaHardwareBitmap pid [") + std::to_string(getpid()) + "]"); + sp<GraphicBuffer> buffer = new GraphicBuffer( + info.width(), info.height(), pixelFormat, + GraphicBuffer::USAGE_HW_TEXTURE | GraphicBuffer::USAGE_SW_WRITE_NEVER | + GraphicBuffer::USAGE_SW_READ_NEVER, + std::string("Bitmap::allocateSkiaHardwareBitmap pid [") + std::to_string(getpid()) + + "]"); status_t error = buffer->initCheck(); if (error < 0) { @@ -339,18 +342,17 @@ sk_sp<Bitmap> SkiaOpenGLPipeline::allocateHardwareBitmap(renderthread::RenderThr return nullptr; } - //upload the bitmap into a texture + // upload the bitmap into a texture EGLDisplay display = eglGetCurrentDisplay(); - LOG_ALWAYS_FATAL_IF(display == EGL_NO_DISPLAY, - "Failed to get EGL_DEFAULT_DISPLAY! err=%s", - uirenderer::renderthread::EglManager::eglErrorString()); + LOG_ALWAYS_FATAL_IF(display == EGL_NO_DISPLAY, "Failed to get EGL_DEFAULT_DISPLAY! err=%s", + uirenderer::renderthread::EglManager::eglErrorString()); // We use an EGLImage to access the content of the GraphicBuffer // The EGL image is later bound to a 2D texture - EGLClientBuffer clientBuffer = (EGLClientBuffer) buffer->getNativeBuffer(); + EGLClientBuffer clientBuffer = (EGLClientBuffer)buffer->getNativeBuffer(); AutoEglImage autoImage(display, clientBuffer); if (autoImage.image == EGL_NO_IMAGE_KHR) { ALOGW("Could not create EGL image, err =%s", - uirenderer::renderthread::EglManager::eglErrorString()); + uirenderer::renderthread::EglManager::eglErrorString()); return nullptr; } AutoSkiaGlTexture glTexture; @@ -361,7 +363,7 @@ sk_sp<Bitmap> SkiaOpenGLPipeline::allocateHardwareBitmap(renderthread::RenderThr // But asynchronous in sense that driver may upload texture onto hardware buffer when we first // use it in drawing glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, info.width(), info.height(), format, type, - bitmap.getPixels()); + bitmap.getPixels()); GL_CHECKPOINT(MODERATE); // The fence is used to wait for the texture upload to finish @@ -375,7 +377,7 @@ sk_sp<Bitmap> SkiaOpenGLPipeline::allocateHardwareBitmap(renderthread::RenderThr // The flag EGL_SYNC_FLUSH_COMMANDS_BIT_KHR will trigger a // pipeline flush (similar to what a glFlush() would do.) EGLint waitStatus = eglClientWaitSyncKHR(display, autoFence.fence, - EGL_SYNC_FLUSH_COMMANDS_BIT_KHR, FENCE_TIMEOUT); + EGL_SYNC_FLUSH_COMMANDS_BIT_KHR, FENCE_TIMEOUT); if (waitStatus != EGL_CONDITION_SATISFIED_KHR) { LOG_ALWAYS_FATAL("Failed to wait for the fence %#x", eglGetError()); return nullptr; diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h index aa29c8e3babc..5e013b6697a7 100644 --- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h +++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h @@ -33,25 +33,24 @@ public: renderthread::MakeCurrentResult makeCurrent() override; renderthread::Frame getFrame() override; bool draw(const renderthread::Frame& frame, const SkRect& screenDirty, const SkRect& dirty, - const FrameBuilder::LightGeometry& lightGeometry, - LayerUpdateQueue* layerUpdateQueue, - const Rect& contentDrawBounds, bool opaque, bool wideColorGamut, - const BakedOpRenderer::LightInfo& lightInfo, - const std::vector< sp<RenderNode> >& renderNodes, - FrameInfoVisualizer* profiler) override; + const FrameBuilder::LightGeometry& lightGeometry, LayerUpdateQueue* layerUpdateQueue, + const Rect& contentDrawBounds, bool opaque, bool wideColorGamut, + const BakedOpRenderer::LightInfo& lightInfo, + const std::vector<sp<RenderNode> >& renderNodes, + FrameInfoVisualizer* profiler) override; bool swapBuffers(const renderthread::Frame& frame, bool drew, const SkRect& screenDirty, - FrameInfo* currentFrameInfo, bool* requireSwap) override; + FrameInfo* currentFrameInfo, bool* requireSwap) override; bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) override; DeferredLayerUpdater* createTextureLayer() override; bool setSurface(Surface* window, renderthread::SwapBehavior swapBehavior, - renderthread::ColorMode colorMode) override; + renderthread::ColorMode colorMode) override; void onStop() override; bool isSurfaceReady() override; bool isContextReady() override; static void invokeFunctor(const renderthread::RenderThread& thread, Functor* functor); static sk_sp<Bitmap> allocateHardwareBitmap(renderthread::RenderThread& thread, - SkBitmap& skBitmap); + SkBitmap& skBitmap); private: renderthread::EglManager& mEglManager; diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp index 311419dd2b65..107890e57a19 100644 --- a/libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp +++ b/libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp @@ -16,15 +16,16 @@ #include "SkiaOpenGLReadback.h" -#include "Matrix.h" -#include "Properties.h" +#include <GLES2/gl2.h> +#include <GLES2/gl2ext.h> +#include <GrBackendSurface.h> #include <SkCanvas.h> #include <SkSurface.h> -#include <GrBackendSurface.h> #include <gl/GrGLInterface.h> #include <gl/GrGLTypes.h> -#include <GLES2/gl2.h> -#include <GLES2/gl2ext.h> +#include "DeviceInfo.h" +#include "Matrix.h" +#include "Properties.h" using namespace android::uirenderer::renderthread; @@ -33,8 +34,8 @@ namespace uirenderer { namespace skiapipeline { CopyResult SkiaOpenGLReadback::copyImageInto(EGLImageKHR eglImage, const Matrix4& imgTransform, - int imgWidth, int imgHeight, const Rect& srcRect, SkBitmap* bitmap) { - + int imgWidth, int imgHeight, const Rect& srcRect, + SkBitmap* bitmap) { GLuint sourceTexId; glGenTextures(1, &sourceTexId); glBindTexture(GL_TEXTURE_EXTERNAL_OES, sourceTexId); @@ -44,8 +45,7 @@ CopyResult SkiaOpenGLReadback::copyImageInto(EGLImageKHR eglImage, const Matrix4 if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) { sk_sp<const GrGLInterface> glInterface(GrGLCreateNativeInterface()); LOG_ALWAYS_FATAL_IF(!glInterface.get()); - grContext.reset(GrContext::Create(GrBackend::kOpenGL_GrBackend, - (GrBackendContext)glInterface.get())); + grContext = GrContext::MakeGL(std::move(glInterface)); } else { grContext->resetContext(); } @@ -54,56 +54,76 @@ CopyResult SkiaOpenGLReadback::copyImageInto(EGLImageKHR eglImage, const Matrix4 externalTexture.fTarget = GL_TEXTURE_EXTERNAL_OES; externalTexture.fID = sourceTexId; - GrBackendTexture backendTexture(imgWidth, imgHeight, kRGBA_8888_GrPixelConfig, externalTexture); + GrPixelConfig pixelConfig; + switch (bitmap->colorType()) { + case kRGBA_F16_SkColorType: + pixelConfig = kRGBA_half_GrPixelConfig; + break; + case kN32_SkColorType: + default: + pixelConfig = kRGBA_8888_GrPixelConfig; + break; + } + + if (pixelConfig == kRGBA_half_GrPixelConfig && + !grContext->caps()->isConfigRenderable(kRGBA_half_GrPixelConfig, false)) { + ALOGW("Can't copy surface into bitmap, RGBA_F16 config is not supported"); + return CopyResult::DestinationInvalid; + } + + GrBackendTexture backendTexture(imgWidth, imgHeight, pixelConfig, externalTexture); CopyResult copyResult = CopyResult::UnknownError; sk_sp<SkImage> image(SkImage::MakeFromAdoptedTexture(grContext.get(), backendTexture, - kTopLeft_GrSurfaceOrigin)); + kTopLeft_GrSurfaceOrigin)); if (image) { - SkMatrix textureMatrix; - imgTransform.copyTo(textureMatrix); - - // remove the y-flip applied to the matrix - SkMatrix yFlip = SkMatrix::MakeScale(1, -1); - yFlip.postTranslate(0,1); - textureMatrix.preConcat(yFlip); - - // multiply by image size, because textureMatrix maps to [0..1] range - textureMatrix[SkMatrix::kMTransX] *= imgWidth; - textureMatrix[SkMatrix::kMTransY] *= imgHeight; - - // swap rotation and translation part of the matrix, because we convert from - // right-handed Cartesian to left-handed coordinate system. - std::swap(textureMatrix[SkMatrix::kMTransX], textureMatrix[SkMatrix::kMTransY]); - std::swap(textureMatrix[SkMatrix::kMSkewX], textureMatrix[SkMatrix::kMSkewY]); - - // convert to Skia data structures - SkRect skiaSrcRect = srcRect.toSkRect(); - SkMatrix textureMatrixInv; + int displayedWidth = imgWidth, displayedHeight = imgHeight; + // If this is a 90 or 270 degree rotation we need to swap width/height to get the device + // size. + if (imgTransform[Matrix4::kSkewX] >= 0.5f || imgTransform[Matrix4::kSkewX] <= -0.5f) { + std::swap(displayedWidth, displayedHeight); + } SkRect skiaDestRect = SkRect::MakeWH(bitmap->width(), bitmap->height()); - bool srcNotEmpty = false; - if (textureMatrix.invert(&textureMatrixInv)) { - if (skiaSrcRect.isEmpty()) { - skiaSrcRect = SkRect::MakeIWH(imgWidth, imgHeight); - srcNotEmpty = !skiaSrcRect.isEmpty(); - } else { - // src and dest rectangles need to be converted into texture coordinates before the - // rotation matrix is applied (because drawImageRect preconcat its matrix). - textureMatrixInv.mapRect(&skiaSrcRect); - srcNotEmpty = skiaSrcRect.intersect(SkRect::MakeIWH(imgWidth, imgHeight)); - } - textureMatrixInv.mapRect(&skiaDestRect); + SkRect skiaSrcRect = srcRect.toSkRect(); + if (skiaSrcRect.isEmpty()) { + skiaSrcRect = SkRect::MakeIWH(displayedWidth, displayedHeight); } + bool srcNotEmpty = skiaSrcRect.intersect(SkRect::MakeIWH(displayedWidth, displayedHeight)); if (srcNotEmpty) { + SkMatrix textureMatrixInv; + imgTransform.copyTo(textureMatrixInv); + // TODO: after skia bug https://bugs.chromium.org/p/skia/issues/detail?id=7075 is fixed + // use bottom left origin and remove flipV and invert transformations. + SkMatrix flipV; + flipV.setAll(1, 0, 0, 0, -1, 1, 0, 0, 1); + textureMatrixInv.preConcat(flipV); + textureMatrixInv.preScale(1.0f / displayedWidth, 1.0f / displayedHeight); + textureMatrixInv.postScale(imgWidth, imgHeight); + SkMatrix textureMatrix; + if (!textureMatrixInv.invert(&textureMatrix)) { + textureMatrix = textureMatrixInv; + } + + textureMatrixInv.mapRect(&skiaSrcRect); + textureMatrixInv.mapRect(&skiaDestRect); + // we render in an offscreen buffer to scale and to avoid an issue b/62262733 // with reading incorrect data from EGLImage backed SkImage (likely a driver bug) - sk_sp<SkSurface> scaledSurface = SkSurface::MakeRenderTarget( - grContext.get(), SkBudgeted::kYes, bitmap->info()); + sk_sp<SkSurface> scaledSurface = + SkSurface::MakeRenderTarget(grContext.get(), SkBudgeted::kYes, bitmap->info()); SkPaint paint; paint.setBlendMode(SkBlendMode::kSrc); + // Apply a filter, which is matching OpenGL pipeline readback behaviour. Filter usage + // is codified by tests using golden images like DecodeAccuracyTest. + if (skiaSrcRect.width() != bitmap->width() || + skiaSrcRect.height() != bitmap->height()) { + // TODO: apply filter always, but check if tests will be fine + paint.setFilterQuality(kLow_SkFilterQuality); + } scaledSurface->getCanvas()->concat(textureMatrix); - scaledSurface->getCanvas()->drawImageRect(image, skiaSrcRect, skiaDestRect, &paint); + scaledSurface->getCanvas()->drawImageRect(image, skiaSrcRect, skiaDestRect, &paint, + SkCanvas::kFast_SrcRectConstraint); image = scaledSurface->makeImageSnapshot(); diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLReadback.h b/libs/hwui/pipeline/skia/SkiaOpenGLReadback.h index d914409628d0..cc9fb3b0a5e0 100644 --- a/libs/hwui/pipeline/skia/SkiaOpenGLReadback.h +++ b/libs/hwui/pipeline/skia/SkiaOpenGLReadback.h @@ -25,9 +25,11 @@ namespace skiapipeline { class SkiaOpenGLReadback : public OpenGLReadback { public: SkiaOpenGLReadback(renderthread::RenderThread& thread) : OpenGLReadback(thread) {} + protected: virtual CopyResult copyImageInto(EGLImageKHR eglImage, const Matrix4& imgTransform, - int imgWidth, int imgHeight, const Rect& srcRect, SkBitmap* bitmap) override; + int imgWidth, int imgHeight, const Rect& srcRect, + SkBitmap* bitmap) override; }; } /* namespace skiapipeline */ diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp index 03792e0ed291..534782a5dc02 100644 --- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp @@ -16,16 +16,14 @@ #include "SkiaPipeline.h" -#include "utils/TraceUtils.h" #include <SkImageEncoder.h> #include <SkImagePriv.h> #include <SkOverdrawCanvas.h> #include <SkOverdrawColorFilter.h> #include <SkPicture.h> #include <SkPictureRecorder.h> -#include <SkPixelSerializer.h> -#include <SkStream.h> #include "VectorDrawable.h" +#include "utils/TraceUtils.h" #include <unistd.h> @@ -35,18 +33,23 @@ namespace android { namespace uirenderer { namespace skiapipeline { -float SkiaPipeline::mLightRadius = 0; +float SkiaPipeline::mLightRadius = 0; uint8_t SkiaPipeline::mAmbientShadowAlpha = 0; uint8_t SkiaPipeline::mSpotShadowAlpha = 0; Vector3 SkiaPipeline::mLightCenter = {FLT_MIN, FLT_MIN, FLT_MIN}; -SkiaPipeline::SkiaPipeline(RenderThread& thread) : mRenderThread(thread) { +SkiaPipeline::SkiaPipeline(RenderThread& thread) : mRenderThread(thread) { + mAnimatedImageDrawables.reserve(30); mVectorDrawables.reserve(30); } +SkiaPipeline::~SkiaPipeline() { + unpinImages(); +} + TaskManager* SkiaPipeline::getTaskManager() { - return &mTaskManager; + return mRenderThread.cacheManager().getTaskManager(); } void SkiaPipeline::onDestroyHardwareResources() { @@ -71,9 +74,15 @@ void SkiaPipeline::unpinImages() { mPinnedImages.clear(); } +void SkiaPipeline::onPrepareTree() { + // The only time mVectorDrawables is not empty is if prepare tree was called 2 times without + // a renderFrame in the middle. + mVectorDrawables.clear(); +} + void SkiaPipeline::renderLayers(const FrameBuilder::LightGeometry& lightGeometry, - LayerUpdateQueue* layerUpdateQueue, bool opaque, bool wideColorGamut, - const BakedOpRenderer::LightInfo& lightInfo) { + LayerUpdateQueue* layerUpdateQueue, bool opaque, + bool wideColorGamut, const BakedOpRenderer::LightInfo& lightInfo) { updateLighting(lightGeometry, lightInfo); ATRACE_NAME("draw layers"); renderVectorDrawableCache(); @@ -81,9 +90,10 @@ void SkiaPipeline::renderLayers(const FrameBuilder::LightGeometry& lightGeometry layerUpdateQueue->clear(); } -void SkiaPipeline::renderLayersImpl(const LayerUpdateQueue& layers, - bool opaque, bool wideColorGamut) { - // TODO: Handle wide color gamut +void SkiaPipeline::renderLayersImpl(const LayerUpdateQueue& layers, bool opaque, + bool wideColorGamut) { + sk_sp<GrContext> cachedContext; + // Render all layers that need to be updated, in order. for (size_t i = 0; i < layers.entries().size(); i++) { RenderNode* layerNode = layers.entries()[i].renderNode.get(); @@ -101,7 +111,7 @@ void SkiaPipeline::renderLayersImpl(const LayerUpdateQueue& layers, const Rect& layerDamage = layers.entries()[i].damage; - SkCanvas* layerCanvas = layerNode->getLayerSurface()->getCanvas(); + SkCanvas* layerCanvas = tryCapture(layerNode->getLayerSurface()); int saveCount = layerCanvas->save(); SkASSERT(saveCount == 1); @@ -118,29 +128,55 @@ void SkiaPipeline::renderLayersImpl(const LayerUpdateQueue& layers, return; } + ATRACE_FORMAT("drawLayer [%s] %.1f x %.1f", layerNode->getName(), bounds.width(), + bounds.height()); + layerNode->getSkiaLayer()->hasRenderedSinceRepaint = false; layerCanvas->clear(SK_ColorTRANSPARENT); RenderNodeDrawable root(layerNode, layerCanvas, false); root.forceDraw(layerCanvas); layerCanvas->restoreToCount(saveCount); - layerCanvas->flush(); mLightCenter = savedLightCenter; + + endCapture(layerNode->getLayerSurface()); + + // cache the current context so that we can defer flushing it until + // either all the layers have been rendered or the context changes + GrContext* currentContext = layerNode->getLayerSurface()->getCanvas()->getGrContext(); + if (cachedContext.get() != currentContext) { + if (cachedContext.get()) { + cachedContext->flush(); + } + cachedContext.reset(SkSafeRef(currentContext)); + } } } + + if (cachedContext.get()) { + cachedContext->flush(); + } } -bool SkiaPipeline::createOrUpdateLayer(RenderNode* node, - const DamageAccumulator& damageAccumulator, bool wideColorGamut) { +bool SkiaPipeline::createOrUpdateLayer(RenderNode* node, const DamageAccumulator& damageAccumulator, + bool wideColorGamut) { + // compute the size of the surface (i.e. texture) to be allocated for this layer + const int surfaceWidth = ceilf(node->getWidth() / float(LAYER_SIZE)) * LAYER_SIZE; + const int surfaceHeight = ceilf(node->getHeight() / float(LAYER_SIZE)) * LAYER_SIZE; + SkSurface* layer = node->getLayerSurface(); - if (!layer || layer->width() != node->getWidth() || layer->height() != node->getHeight()) { - SkImageInfo info = SkImageInfo::MakeN32Premul(node->getWidth(), node->getHeight()); + if (!layer || layer->width() != surfaceWidth || layer->height() != surfaceHeight) { + SkImageInfo info; + if (wideColorGamut) { + info = SkImageInfo::Make(surfaceWidth, surfaceHeight, kRGBA_F16_SkColorType, + kPremul_SkAlphaType); + } else { + info = SkImageInfo::MakeN32Premul(surfaceWidth, surfaceHeight); + } SkSurfaceProps props(0, kUnknown_SkPixelGeometry); SkASSERT(mRenderThread.getGrContext() != nullptr); - // TODO: Handle wide color gamut requests - node->setLayerSurface( - SkSurface::MakeRenderTarget(mRenderThread.getGrContext(), SkBudgeted::kYes, - info, 0, &props)); + node->setLayerSurface(SkSurface::MakeRenderTarget(mRenderThread.getGrContext(), + SkBudgeted::kYes, info, 0, &props)); if (node->getLayerSurface()) { // update the transform in window of the layer to reset its origin wrt light source // position @@ -161,7 +197,8 @@ void SkiaPipeline::prepareToDraw(const RenderThread& thread, Bitmap* bitmap) { GrContext* context = thread.getGrContext(); if (context) { ATRACE_FORMAT("Bitmap#prepareToDraw %dx%d", bitmap->width(), bitmap->height()); - auto image = bitmap->makeImage(); + sk_sp<SkColorFilter> colorFilter; + auto image = bitmap->makeImage(&colorFilter); if (image.get() && !bitmap->isHardware()) { SkImage_pinAsTexture(image.get(), context); SkImage_unpinAsTexture(image.get(), context); @@ -169,92 +206,149 @@ void SkiaPipeline::prepareToDraw(const RenderThread& thread, Bitmap* bitmap) { } } -// Encodes to PNG, unless there is already encoded data, in which case that gets -// used. -class PngPixelSerializer : public SkPixelSerializer { -public: - bool onUseEncodedData(const void*, size_t) override { return true; } - SkData* onEncode(const SkPixmap& pixmap) override { - SkDynamicMemoryWStream buf; - return SkEncodeImage(&buf, pixmap, SkEncodedImageFormat::kPNG, 100) - ? buf.detachAsData().release() - : nullptr; - } -}; - void SkiaPipeline::renderVectorDrawableCache() { if (!mVectorDrawables.empty()) { sp<VectorDrawableAtlas> atlas = mRenderThread.cacheManager().acquireVectorDrawableAtlas(); auto grContext = mRenderThread.getGrContext(); atlas->prepareForDraw(grContext); + ATRACE_NAME("Update VectorDrawables"); for (auto vd : mVectorDrawables) { vd->updateCache(atlas, grContext); } - grContext->flush(); mVectorDrawables.clear(); } } -void SkiaPipeline::renderFrame(const LayerUpdateQueue& layers, const SkRect& clip, - const std::vector<sp<RenderNode>>& nodes, bool opaque, bool wideColorGamut, - const Rect &contentDrawBounds, sk_sp<SkSurface> surface) { +class SkiaPipeline::SavePictureProcessor : public TaskProcessor<bool> { +public: + explicit SavePictureProcessor(TaskManager* taskManager) : TaskProcessor<bool>(taskManager) {} + + struct SavePictureTask : public Task<bool> { + sk_sp<SkData> data; + std::string filename; + }; + + void savePicture(const sk_sp<SkData>& data, const std::string& filename) { + sp<SavePictureTask> task(new SavePictureTask()); + task->data = data; + task->filename = filename; + TaskProcessor<bool>::add(task); + } - renderVectorDrawableCache(); + virtual void onProcess(const sp<Task<bool>>& task) override { + SavePictureTask* t = static_cast<SavePictureTask*>(task.get()); - // draw all layers up front - renderLayersImpl(layers, opaque, wideColorGamut); + if (0 == access(t->filename.c_str(), F_OK)) { + task->setResult(false); + return; + } + + SkFILEWStream stream(t->filename.c_str()); + if (stream.isValid()) { + stream.write(t->data->data(), t->data->size()); + stream.flush(); + SkDebugf("SKP Captured Drawing Output (%d bytes) for frame. %s", stream.bytesWritten(), + t->filename.c_str()); + } - // initialize the canvas for the current frame - SkCanvas* canvas = surface->getCanvas(); + task->setResult(true); + } +}; - std::unique_ptr<SkPictureRecorder> recorder; - bool recordingPicture = false; - char prop[PROPERTY_VALUE_MAX]; - if (skpCaptureEnabled()) { - property_get("debug.hwui.capture_frame_as_skp", prop, "0"); - recordingPicture = prop[0] != '0' && access(prop, F_OK) != 0; +SkCanvas* SkiaPipeline::tryCapture(SkSurface* surface) { + if (CC_UNLIKELY(Properties::skpCaptureEnabled)) { + bool recordingPicture = mCaptureSequence > 0; + char prop[PROPERTY_VALUE_MAX] = {'\0'}; + if (!recordingPicture) { + property_get(PROPERTY_CAPTURE_SKP_FILENAME, prop, "0"); + recordingPicture = prop[0] != '0' && + mCapturedFile != prop; // ensure we capture only once per filename + if (recordingPicture) { + mCapturedFile = prop; + mCaptureSequence = property_get_int32(PROPERTY_CAPTURE_SKP_FRAMES, 1); + } + } if (recordingPicture) { - recorder.reset(new SkPictureRecorder()); - canvas = recorder->beginRecording(surface->width(), surface->height(), - nullptr, SkPictureRecorder::kPlaybackDrawPicture_RecordFlag); + mRecorder.reset(new SkPictureRecorder()); + return mRecorder->beginRecording(surface->width(), surface->height(), nullptr, + SkPictureRecorder::kPlaybackDrawPicture_RecordFlag); } } + return surface->getCanvas(); +} - renderFrameImpl(layers, clip, nodes, opaque, wideColorGamut, contentDrawBounds, canvas); - - if (skpCaptureEnabled() && recordingPicture) { - sk_sp<SkPicture> picture = recorder->finishRecordingAsPicture(); +void SkiaPipeline::endCapture(SkSurface* surface) { + if (CC_UNLIKELY(mRecorder.get())) { + sk_sp<SkPicture> picture = mRecorder->finishRecordingAsPicture(); + surface->getCanvas()->drawPicture(picture); if (picture->approximateOpCount() > 0) { - SkFILEWStream stream(prop); - if (stream.isValid()) { - PngPixelSerializer serializer; - picture->serialize(&stream, &serializer); - stream.flush(); - SkDebugf("Captured Drawing Output (%d bytes) for frame. %s", stream.bytesWritten(), prop); + auto data = picture->serialize(); + + // offload saving to file in a different thread + if (!mSavePictureProcessor.get()) { + TaskManager* taskManager = getTaskManager(); + mSavePictureProcessor = new SavePictureProcessor( + taskManager->canRunTasks() ? taskManager : nullptr); } + if (1 == mCaptureSequence) { + mSavePictureProcessor->savePicture(data, mCapturedFile); + } else { + mSavePictureProcessor->savePicture( + data, + mCapturedFile + "_" + std::to_string(mCaptureSequence)); + } + mCaptureSequence--; } - surface->getCanvas()->drawPicture(picture); + mRecorder.reset(); } +} + +void SkiaPipeline::renderFrame(const LayerUpdateQueue& layers, const SkRect& clip, + const std::vector<sp<RenderNode>>& nodes, bool opaque, + bool wideColorGamut, const Rect& contentDrawBounds, + sk_sp<SkSurface> surface) { + renderVectorDrawableCache(); + + // draw all layers up front + renderLayersImpl(layers, opaque, wideColorGamut); + + // initialize the canvas for the current frame, that might be a recording canvas if SKP + // capture is enabled. + std::unique_ptr<SkPictureRecorder> recorder; + SkCanvas* canvas = tryCapture(surface.get()); + + renderFrameImpl(layers, clip, nodes, opaque, wideColorGamut, contentDrawBounds, canvas); + + endCapture(surface.get()); if (CC_UNLIKELY(Properties::debugOverdraw)) { renderOverdraw(layers, clip, nodes, contentDrawBounds, surface); } ATRACE_NAME("flush commands"); - canvas->flush(); + surface->getCanvas()->flush(); + + // TODO move to another method + if (!mAnimatedImageDrawables.empty()) { + ATRACE_NAME("Update AnimatedImageDrawables"); + for (auto animatedImage : mAnimatedImageDrawables) { + animatedImage->scheduleUpdate(getTaskManager()); + } + mAnimatedImageDrawables.clear(); + } } namespace { static Rect nodeBounds(RenderNode& node) { auto& props = node.properties(); - return Rect(props.getLeft(), props.getTop(), - props.getRight(), props.getBottom()); + return Rect(props.getLeft(), props.getTop(), props.getRight(), props.getBottom()); } } void SkiaPipeline::renderFrameImpl(const LayerUpdateQueue& layers, const SkRect& clip, - const std::vector<sp<RenderNode>>& nodes, bool opaque, bool wideColorGamut, - const Rect &contentDrawBounds, SkCanvas* canvas) { + const std::vector<sp<RenderNode>>& nodes, bool opaque, + bool wideColorGamut, const Rect& contentDrawBounds, + SkCanvas* canvas) { SkAutoCanvasRestore saver(canvas, true); canvas->androidFramework_setDeviceClipRestriction(clip.roundOut()); @@ -268,15 +362,18 @@ void SkiaPipeline::renderFrameImpl(const LayerUpdateQueue& layers, const SkRect& root.draw(canvas); } } else if (0 == nodes.size()) { - //nothing to draw + // nothing to draw } else { // It there are multiple render nodes, they are laid out as follows: // #0 - backdrop (content + caption) // #1 - content (local bounds are at (0,0), will be translated and clipped to backdrop) // #2 - additional overlay nodes - // Usually the backdrop cannot be seen since it will be entirely covered by the content. While - // resizing however it might become partially visible. The following render loop will crop the - // backdrop against the content and draw the remaining part of it. It will then draw the content + // Usually the backdrop cannot be seen since it will be entirely covered by the content. + // While + // resizing however it might become partially visible. The following render loop will crop + // the + // backdrop against the content and draw the remaining part of it. It will then draw the + // content // cropped to the backdrop (since that indicates a shrinking of the window). // // Additional nodes will be drawn on top with no particular clipping semantics. @@ -288,29 +385,31 @@ void SkiaPipeline::renderFrameImpl(const LayerUpdateQueue& layers, const SkRect& // Backdrop bounds in render target space const Rect backdrop = nodeBounds(*nodes[0]); - // Bounds that content will fill in render target space (note content node bounds may be bigger) + // Bounds that content will fill in render target space (note content node bounds may be + // bigger) Rect content(contentDrawBounds.getWidth(), contentDrawBounds.getHeight()); content.translate(backdrop.left, backdrop.top); if (!content.contains(backdrop) && !nodes[0]->nothingToDraw()) { // Content doesn't entirely overlap backdrop, so fill around content (right/bottom) // Note: in the future, if content doesn't snap to backdrop's left/top, this may need to - // also fill left/top. Currently, both 2up and freeform position content at the top/left of + // also fill left/top. Currently, both 2up and freeform position content at the top/left + // of // the backdrop, so this isn't necessary. RenderNodeDrawable backdropNode(nodes[0].get(), canvas); if (content.right < backdrop.right) { // draw backdrop to right side of content SkAutoCanvasRestore acr(canvas, true); - canvas->clipRect(SkRect::MakeLTRB(content.right, backdrop.top, - backdrop.right, backdrop.bottom)); + canvas->clipRect(SkRect::MakeLTRB(content.right, backdrop.top, backdrop.right, + backdrop.bottom)); backdropNode.draw(canvas); } if (content.bottom < backdrop.bottom) { // draw backdrop to bottom of content // Note: bottom fill uses content left/right, to avoid overdrawing left/right fill SkAutoCanvasRestore acr(canvas, true); - canvas->clipRect(SkRect::MakeLTRB(content.left, content.bottom, - content.right, backdrop.bottom)); + canvas->clipRect(SkRect::MakeLTRB(content.left, content.bottom, content.right, + backdrop.bottom)); backdropNode.draw(canvas); } } @@ -323,8 +422,9 @@ void SkiaPipeline::renderFrameImpl(const LayerUpdateQueue& layers, const SkRect& SkAutoCanvasRestore acr(canvas, true); canvas->translate(dx, dy); - const SkRect contentLocalClip = SkRect::MakeXYWH(contentDrawBounds.left, - contentDrawBounds.top, backdrop.getWidth(), backdrop.getHeight()); + const SkRect contentLocalClip = + SkRect::MakeXYWH(contentDrawBounds.left, contentDrawBounds.top, + backdrop.getWidth(), backdrop.getHeight()); canvas->clipRect(contentLocalClip); contentNode.draw(canvas); } else { @@ -351,8 +451,8 @@ void SkiaPipeline::dumpResourceCacheUsage() const { SkString log("Resource Cache Usage:\n"); log.appendf("%8d items out of %d maximum items\n", resources, maxResources); - log.appendf("%8zu bytes (%.2f MB) out of %.2f MB maximum\n", - bytes, bytes * (1.0f / (1024.0f * 1024.0f)), maxBytes * (1.0f / (1024.0f * 1024.0f))); + log.appendf("%8zu bytes (%.2f MB) out of %.2f MB maximum\n", bytes, + bytes * (1.0f / (1024.0f * 1024.0f)), maxBytes * (1.0f / (1024.0f * 1024.0f))); ALOGD("%s", log.c_str()); } @@ -365,13 +465,17 @@ void SkiaPipeline::dumpResourceCacheUsage() const { // (2) Requires premul colors (instead of unpremul). // (3) Requires RGBA colors (instead of BGRA). static const uint32_t kOverdrawColors[2][6] = { - { 0x00000000, 0x00000000, 0x2f2f0000, 0x2f002f00, 0x3f00003f, 0x7f00007f, }, - { 0x00000000, 0x00000000, 0x2f2f0000, 0x4f004f4f, 0x5f50335f, 0x7f00007f, }, + { + 0x00000000, 0x00000000, 0x2f2f0000, 0x2f002f00, 0x3f00003f, 0x7f00007f, + }, + { + 0x00000000, 0x00000000, 0x2f2f0000, 0x4f004f4f, 0x5f50335f, 0x7f00007f, + }, }; void SkiaPipeline::renderOverdraw(const LayerUpdateQueue& layers, const SkRect& clip, - const std::vector<sp<RenderNode>>& nodes, const Rect &contentDrawBounds, - sk_sp<SkSurface> surface) { + const std::vector<sp<RenderNode>>& nodes, + const Rect& contentDrawBounds, sk_sp<SkSurface> surface) { // Set up the overdraw canvas. SkImageInfo offscreenInfo = SkImageInfo::MakeA8(surface->width(), surface->height()); sk_sp<SkSurface> offscreen = surface->makeSurface(offscreenInfo); diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.h b/libs/hwui/pipeline/skia/SkiaPipeline.h index 19ffc463c121..cc75e9c5b38d 100644 --- a/libs/hwui/pipeline/skia/SkiaPipeline.h +++ b/libs/hwui/pipeline/skia/SkiaPipeline.h @@ -16,10 +16,13 @@ #pragma once -#include "renderthread/CanvasContext.h" +#include <SkSurface.h> #include "FrameBuilder.h" +#include "hwui/AnimatedImageDrawable.h" +#include "renderthread/CanvasContext.h" #include "renderthread/IRenderPipeline.h" -#include <SkSurface.h> + +class SkPictureRecorder; namespace android { namespace uirenderer { @@ -28,7 +31,7 @@ namespace skiapipeline { class SkiaPipeline : public renderthread::IRenderPipeline { public: SkiaPipeline(renderthread::RenderThread& thread); - virtual ~SkiaPipeline() {} + virtual ~SkiaPipeline(); TaskManager* getTaskManager() override; @@ -37,27 +40,32 @@ public: bool pinImages(std::vector<SkImage*>& mutableImages) override; bool pinImages(LsaVector<sk_sp<Bitmap>>& images) override { return false; } void unpinImages() override; + void onPrepareTree() override; void renderLayers(const FrameBuilder::LightGeometry& lightGeometry, - LayerUpdateQueue* layerUpdateQueue, bool opaque, bool wideColorGamut, - const BakedOpRenderer::LightInfo& lightInfo) override; + LayerUpdateQueue* layerUpdateQueue, bool opaque, bool wideColorGamut, + const BakedOpRenderer::LightInfo& lightInfo) override; - bool createOrUpdateLayer(RenderNode* node, - const DamageAccumulator& damageAccumulator, bool wideColorGamut) override; + bool createOrUpdateLayer(RenderNode* node, const DamageAccumulator& damageAccumulator, + bool wideColorGamut) override; void renderFrame(const LayerUpdateQueue& layers, const SkRect& clip, - const std::vector< sp<RenderNode> >& nodes, bool opaque, bool wideColorGamut, - const Rect &contentDrawBounds, sk_sp<SkSurface> surface); + const std::vector<sp<RenderNode>>& nodes, bool opaque, bool wideColorGamut, + const Rect& contentDrawBounds, sk_sp<SkSurface> surface); std::vector<VectorDrawableRoot*>* getVectorDrawables() { return &mVectorDrawables; } + void scheduleDeferredUpdate(AnimatedImageDrawable* imageDrawable) { + mAnimatedImageDrawables.push_back(imageDrawable); + } + + std::vector<AnimatedImageDrawable*>* getAnimatingImages() { return &mAnimatedImageDrawables; } + static void destroyLayer(RenderNode* node); static void prepareToDraw(const renderthread::RenderThread& thread, Bitmap* bitmap); - static void renderLayersImpl(const LayerUpdateQueue& layers, bool opaque, bool wideColorGamut); - - static bool skpCaptureEnabled() { return false; } + void renderLayersImpl(const LayerUpdateQueue& layers, bool opaque, bool wideColorGamut); static float getLightRadius() { if (CC_UNLIKELY(Properties::overrideLightRadius > 0)) { @@ -85,7 +93,7 @@ public: Vector3 adjustedLightCenter = mLightCenter; if (CC_UNLIKELY(Properties::overrideLightPosY > 0)) { // negated since this shifts up - adjustedLightCenter.y = - Properties::overrideLightPosY; + adjustedLightCenter.y = -Properties::overrideLightPosY; } if (CC_UNLIKELY(Properties::overrideLightPosZ > 0)) { adjustedLightCenter.z = Properties::overrideLightPosZ; @@ -96,7 +104,7 @@ public: } static void updateLighting(const FrameBuilder::LightGeometry& lightGeometry, - const BakedOpRenderer::LightInfo& lightInfo) { + const BakedOpRenderer::LightInfo& lightInfo) { mLightRadius = lightGeometry.radius; mAmbientShadowAlpha = lightInfo.ambientShadowAlpha; mSpotShadowAlpha = lightInfo.spotShadowAlpha; @@ -110,29 +118,58 @@ protected: private: void renderFrameImpl(const LayerUpdateQueue& layers, const SkRect& clip, - const std::vector< sp<RenderNode> >& nodes, bool opaque, bool wideColorGamut, - const Rect &contentDrawBounds, SkCanvas* canvas); + const std::vector<sp<RenderNode>>& nodes, bool opaque, bool wideColorGamut, + const Rect& contentDrawBounds, SkCanvas* canvas); /** * Debugging feature. Draws a semi-transparent overlay on each pixel, indicating * how many times it has been drawn. */ void renderOverdraw(const LayerUpdateQueue& layers, const SkRect& clip, - const std::vector< sp<RenderNode> >& nodes, const Rect &contentDrawBounds, - sk_sp<SkSurface>); + const std::vector<sp<RenderNode>>& nodes, const Rect& contentDrawBounds, + sk_sp<SkSurface>); /** * Render mVectorDrawables into offscreen buffers. */ void renderVectorDrawableCache(); - TaskManager mTaskManager; + SkCanvas* tryCapture(SkSurface* surface); + void endCapture(SkSurface* surface); + std::vector<sk_sp<SkImage>> mPinnedImages; /** * populated by prepareTree with dirty VDs */ std::vector<VectorDrawableRoot*> mVectorDrawables; + + /** + * populated by prepareTree with images with active animations + */ + std::vector<AnimatedImageDrawable*> mAnimatedImageDrawables; + + // Block of properties used only for debugging to record a SkPicture and save it in a file. + /** + * mCapturedFile is used to enforce we don't capture more than once for a given name (cause + * permissions don't allow to reset a property from render thread). + */ + std::string mCapturedFile; + /** + * mCaptureSequence counts how many frames are left to take in the sequence. + */ + int mCaptureSequence = 0; + /** + * mSavePictureProcessor is used to run the file saving code in a separate thread. + */ + class SavePictureProcessor; + sp<SavePictureProcessor> mSavePictureProcessor; + /** + * mRecorder holds the current picture recorder. We could store it on the stack to support + * parallel tryCapture calls (not really needed). + */ + std::unique_ptr<SkPictureRecorder> mRecorder; + static float mLightRadius; static uint8_t mAmbientShadowAlpha; static uint8_t mSpotShadowAlpha; diff --git a/libs/hwui/pipeline/skia/SkiaProfileRenderer.cpp b/libs/hwui/pipeline/skia/SkiaProfileRenderer.cpp index d97fb372fe0c..492c39f1288c 100644 --- a/libs/hwui/pipeline/skia/SkiaProfileRenderer.cpp +++ b/libs/hwui/pipeline/skia/SkiaProfileRenderer.cpp @@ -20,7 +20,7 @@ namespace android { namespace uirenderer { void SkiaProfileRenderer::drawRect(float left, float top, float right, float bottom, - const SkPaint& paint) { + const SkPaint& paint) { SkRect rect = SkRect::MakeLTRB(left, top, right, bottom); mCanvas->drawRect(rect, paint); } @@ -28,7 +28,7 @@ void SkiaProfileRenderer::drawRect(float left, float top, float right, float bot void SkiaProfileRenderer::drawRects(const float* rects, int count, const SkPaint& paint) { for (int index = 0; index + 4 <= count; index += 4) { SkRect rect = SkRect::MakeLTRB(rects[index + 0], rects[index + 1], rects[index + 2], - rects[index + 3]); + rects[index + 3]); mCanvas->drawRect(rect, paint); } } diff --git a/libs/hwui/pipeline/skia/SkiaProfileRenderer.h b/libs/hwui/pipeline/skia/SkiaProfileRenderer.h index e6b7f8307379..5ae7d6b0b607 100644 --- a/libs/hwui/pipeline/skia/SkiaProfileRenderer.h +++ b/libs/hwui/pipeline/skia/SkiaProfileRenderer.h @@ -23,8 +23,7 @@ namespace uirenderer { class SkiaProfileRenderer : public IProfileRenderer { public: - SkiaProfileRenderer(SkCanvas* canvas) - : mCanvas(canvas) {} + SkiaProfileRenderer(SkCanvas* canvas) : mCanvas(canvas) {} void drawRect(float left, float top, float right, float bottom, const SkPaint& paint) override; void drawRects(const float* rects, int count, const SkPaint& paint) override; diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp index a0cce98c8d57..eabe2e87fc49 100644 --- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp +++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp @@ -16,12 +16,12 @@ #include "SkiaRecordingCanvas.h" +#include <SkImagePriv.h> #include "Layer.h" -#include "RenderNode.h" #include "LayerDrawable.h" #include "NinePatchUtils.h" +#include "RenderNode.h" #include "pipeline/skia/AnimatedDrawables.h" -#include <SkImagePriv.h> namespace android { namespace uirenderer { @@ -32,7 +32,7 @@ namespace skiapipeline { // ---------------------------------------------------------------------------- void SkiaRecordingCanvas::initDisplayList(uirenderer::RenderNode* renderNode, int width, - int height) { + int height) { mCurrentBarrier = nullptr; SkASSERT(mDisplayList.get() == nullptr); @@ -59,17 +59,21 @@ uirenderer::DisplayList* SkiaRecordingCanvas::finishRecording() { // ---------------------------------------------------------------------------- void SkiaRecordingCanvas::drawRoundRect(uirenderer::CanvasPropertyPrimitive* left, - uirenderer::CanvasPropertyPrimitive* top, uirenderer::CanvasPropertyPrimitive* right, - uirenderer::CanvasPropertyPrimitive* bottom, uirenderer::CanvasPropertyPrimitive* rx, - uirenderer::CanvasPropertyPrimitive* ry, uirenderer::CanvasPropertyPaint* paint) { + uirenderer::CanvasPropertyPrimitive* top, + uirenderer::CanvasPropertyPrimitive* right, + uirenderer::CanvasPropertyPrimitive* bottom, + uirenderer::CanvasPropertyPrimitive* rx, + uirenderer::CanvasPropertyPrimitive* ry, + uirenderer::CanvasPropertyPaint* paint) { // Destructor of drawables created with allocateDrawable, will be invoked by ~LinearAllocator. - drawDrawable(mDisplayList->allocateDrawable<AnimatedRoundRect>(left, top, right, bottom, - rx, ry, paint)); + drawDrawable(mDisplayList->allocateDrawable<AnimatedRoundRect>(left, top, right, bottom, rx, ry, + paint)); } void SkiaRecordingCanvas::drawCircle(uirenderer::CanvasPropertyPrimitive* x, - uirenderer::CanvasPropertyPrimitive* y, uirenderer::CanvasPropertyPrimitive* radius, - uirenderer::CanvasPropertyPaint* paint) { + uirenderer::CanvasPropertyPrimitive* y, + uirenderer::CanvasPropertyPrimitive* radius, + uirenderer::CanvasPropertyPaint* paint) { drawDrawable(mDisplayList->allocateDrawable<AnimatedCircle>(x, y, radius, paint)); } @@ -77,24 +81,22 @@ void SkiaRecordingCanvas::insertReorderBarrier(bool enableReorder) { if (nullptr != mCurrentBarrier) { // finish off the existing chunk SkDrawable* drawable = - mDisplayList->allocateDrawable<EndReorderBarrierDrawable>( - mCurrentBarrier); + mDisplayList->allocateDrawable<EndReorderBarrierDrawable>(mCurrentBarrier); mCurrentBarrier = nullptr; drawDrawable(drawable); } if (enableReorder) { mCurrentBarrier = (StartReorderBarrierDrawable*) - mDisplayList->allocateDrawable<StartReorderBarrierDrawable>( - mDisplayList.get()); + mDisplayList->allocateDrawable<StartReorderBarrierDrawable>( + mDisplayList.get()); drawDrawable(mCurrentBarrier); } } void SkiaRecordingCanvas::drawLayer(uirenderer::DeferredLayerUpdater* layerUpdater) { - if (layerUpdater != nullptr && layerUpdater->backingLayer() != nullptr) { - uirenderer::Layer* layer = layerUpdater->backingLayer(); + if (layerUpdater != nullptr) { // Create a ref-counted drawable, which is kept alive by sk_sp in SkLiteDL. - sk_sp<SkDrawable> drawable(new LayerDrawable(layer)); + sk_sp<SkDrawable> drawable(new LayerDrawable(layerUpdater)); drawDrawable(drawable.get()); } } @@ -114,25 +116,21 @@ void SkiaRecordingCanvas::drawRenderNode(uirenderer::RenderNode* renderNode) { } void SkiaRecordingCanvas::callDrawGLFunction(Functor* functor, - uirenderer::GlFunctorLifecycleListener* listener) { + uirenderer::GlFunctorLifecycleListener* listener) { // Drawable dtor will be invoked when mChildFunctors deque is cleared. mDisplayList->mChildFunctors.emplace_back(functor, listener, asSkCanvas()); drawDrawable(&mDisplayList->mChildFunctors.back()); } class VectorDrawable : public SkDrawable { - public: +public: VectorDrawable(VectorDrawableRoot* tree) : mRoot(tree) {} - protected: - virtual SkRect onGetBounds() override { - return SkRect::MakeLargest(); - } - virtual void onDraw(SkCanvas* canvas) override { - mRoot->draw(canvas); - } +protected: + virtual SkRect onGetBounds() override { return SkRect::MakeLargest(); } + virtual void onDraw(SkCanvas* canvas) override { mRoot->draw(canvas); } - private: +private: sp<VectorDrawableRoot> mRoot; }; @@ -145,9 +143,25 @@ void SkiaRecordingCanvas::drawVectorDrawable(VectorDrawableRoot* tree) { // Recording Canvas draw operations: Bitmaps // ---------------------------------------------------------------------------- -inline static const SkPaint* nonAAPaint(const SkPaint* origPaint, SkPaint* tmpPaint) { - if (origPaint && origPaint->isAntiAlias()) { - *tmpPaint = *origPaint; +inline static const SkPaint* bitmapPaint(const SkPaint* origPaint, SkPaint* tmpPaint, + sk_sp<SkColorFilter> colorSpaceFilter) { + if ((origPaint && origPaint->isAntiAlias()) || colorSpaceFilter) { + if (origPaint) { + *tmpPaint = *origPaint; + } + + if (colorSpaceFilter) { + if (tmpPaint->getColorFilter()) { + tmpPaint->setColorFilter( + SkColorFilter::MakeComposeFilter(tmpPaint->refColorFilter(), colorSpaceFilter)); + } else { + tmpPaint->setColorFilter(colorSpaceFilter); + } + LOG_ALWAYS_FATAL_IF(!tmpPaint->getColorFilter()); + } + + + // disabling AA on bitmap draws matches legacy HWUI behavior tmpPaint->setAntiAlias(false); return tmpPaint; } else { @@ -156,9 +170,10 @@ inline static const SkPaint* nonAAPaint(const SkPaint* origPaint, SkPaint* tmpPa } void SkiaRecordingCanvas::drawBitmap(Bitmap& bitmap, float left, float top, const SkPaint* paint) { - sk_sp<SkImage> image = bitmap.makeImage(); SkPaint tmpPaint; - mRecorder.drawImage(image, left, top, nonAAPaint(paint, &tmpPaint)); + sk_sp<SkColorFilter> colorFilter; + sk_sp<SkImage> image = bitmap.makeImage(&colorFilter); + mRecorder.drawImage(image, left, top, bitmapPaint(paint, &tmpPaint, colorFilter)); // if image->unique() is true, then mRecorder.drawImage failed for some reason. It also means // it is not safe to store a raw SkImage pointer, because the image object will be destroyed // when this function ends. @@ -167,38 +182,44 @@ void SkiaRecordingCanvas::drawBitmap(Bitmap& bitmap, float left, float top, cons } } -void SkiaRecordingCanvas::drawBitmap(Bitmap& hwuiBitmap, const SkMatrix& matrix, - const SkPaint* paint) { +void SkiaRecordingCanvas::drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, const SkPaint* paint) { SkAutoCanvasRestore acr(&mRecorder, true); concat(matrix); - sk_sp<SkImage> image = hwuiBitmap.makeImage(); + SkPaint tmpPaint; - mRecorder.drawImage(image, 0, 0, nonAAPaint(paint, &tmpPaint)); - if (!hwuiBitmap.isImmutable() && image.get() && !image->unique()) { + sk_sp<SkColorFilter> colorFilter; + sk_sp<SkImage> image = bitmap.makeImage(&colorFilter); + mRecorder.drawImage(image, 0, 0, bitmapPaint(paint, &tmpPaint, colorFilter)); + if (!bitmap.isImmutable() && image.get() && !image->unique()) { mDisplayList->mMutableImages.push_back(image.get()); } } -void SkiaRecordingCanvas::drawBitmap(Bitmap& hwuiBitmap, float srcLeft, float srcTop, - float srcRight, float srcBottom, float dstLeft, float dstTop, float dstRight, - float dstBottom, const SkPaint* paint) { +void SkiaRecordingCanvas::drawBitmap(Bitmap& bitmap, float srcLeft, float srcTop, float srcRight, + float srcBottom, float dstLeft, float dstTop, float dstRight, + float dstBottom, const SkPaint* paint) { SkRect srcRect = SkRect::MakeLTRB(srcLeft, srcTop, srcRight, srcBottom); SkRect dstRect = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom); - sk_sp<SkImage> image = hwuiBitmap.makeImage(); + SkPaint tmpPaint; - mRecorder.drawImageRect(image, srcRect, dstRect, nonAAPaint(paint, &tmpPaint)); - if (!hwuiBitmap.isImmutable() && image.get() && !image->unique() && !srcRect.isEmpty() - && !dstRect.isEmpty()) { + sk_sp<SkColorFilter> colorFilter; + sk_sp<SkImage> image = bitmap.makeImage(&colorFilter); + mRecorder.drawImageRect(image, srcRect, dstRect, bitmapPaint(paint, &tmpPaint, colorFilter), + SkCanvas::kFast_SrcRectConstraint); + if (!bitmap.isImmutable() && image.get() && !image->unique() && !srcRect.isEmpty() && + !dstRect.isEmpty()) { mDisplayList->mMutableImages.push_back(image.get()); } } -void SkiaRecordingCanvas::drawNinePatch(Bitmap& hwuiBitmap, const Res_png_9patch& chunk, - float dstLeft, float dstTop, float dstRight, float dstBottom, const SkPaint* paint) { +void SkiaRecordingCanvas::drawNinePatch(Bitmap& bitmap, const Res_png_9patch& chunk, float dstLeft, + float dstTop, float dstRight, float dstBottom, + const SkPaint* paint) { SkCanvas::Lattice lattice; - NinePatchUtils::SetLatticeDivs(&lattice, chunk, hwuiBitmap.width(), hwuiBitmap.height()); + NinePatchUtils::SetLatticeDivs(&lattice, chunk, bitmap.width(), bitmap.height()); - lattice.fFlags = nullptr; + lattice.fRectTypes = nullptr; + lattice.fColors = nullptr; int numFlags = 0; if (chunk.numColors > 0 && chunk.numColors == NinePatchUtils::NumDistinctRects(lattice)) { // We can expect the framework to give us a color for every distinct rect. @@ -206,22 +227,31 @@ void SkiaRecordingCanvas::drawNinePatch(Bitmap& hwuiBitmap, const Res_png_9patch numFlags = (lattice.fXCount + 1) * (lattice.fYCount + 1); } - SkAutoSTMalloc<25, SkCanvas::Lattice::Flags> flags(numFlags); + SkAutoSTMalloc<25, SkCanvas::Lattice::RectType> flags(numFlags); + SkAutoSTMalloc<25, SkColor> colors(numFlags); if (numFlags > 0) { - NinePatchUtils::SetLatticeFlags(&lattice, flags.get(), numFlags, chunk); + NinePatchUtils::SetLatticeFlags(&lattice, flags.get(), numFlags, chunk, colors.get()); } lattice.fBounds = nullptr; SkRect dst = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom); - sk_sp<SkImage> image = hwuiBitmap.makeImage(); SkPaint tmpPaint; - mRecorder.drawImageLattice(image.get(), lattice, dst, nonAAPaint(paint, &tmpPaint)); - if (!hwuiBitmap.isImmutable() && image.get() && !image->unique() && !dst.isEmpty()) { + sk_sp<SkColorFilter> colorFilter; + sk_sp<SkImage> image = bitmap.makeImage(&colorFilter); + mRecorder.drawImageLattice(image.get(), lattice, dst, + bitmapPaint(paint, &tmpPaint, colorFilter)); + if (!bitmap.isImmutable() && image.get() && !image->unique() && !dst.isEmpty()) { mDisplayList->mMutableImages.push_back(image.get()); } } -}; // namespace skiapipeline -}; // namespace uirenderer -}; // namespace android +double SkiaRecordingCanvas::drawAnimatedImage(AnimatedImageDrawable* animatedImage) { + drawDrawable(animatedImage); + mDisplayList->mAnimatedImages.push_back(animatedImage); + return 0; +} + +}; // namespace skiapipeline +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h index 10829f87efb9..0e5dbdbab078 100644 --- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h +++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h @@ -15,10 +15,10 @@ */ #pragma once +#include <SkLiteRecorder.h> +#include "ReorderBarrierDrawables.h" #include "SkiaCanvas.h" #include "SkiaDisplayList.h" -#include "ReorderBarrierDrawables.h" -#include <SkLiteRecorder.h> namespace android { namespace uirenderer { @@ -29,7 +29,7 @@ namespace skiapipeline { * SkLiteRecorder and a SkiaDisplayList. */ class SkiaRecordingCanvas : public SkiaCanvas { - public: +public: explicit SkiaRecordingCanvas(uirenderer::RenderNode* renderNode, int width, int height) { initDisplayList(renderNode, width, height); } @@ -39,31 +39,33 @@ class SkiaRecordingCanvas : public SkiaCanvas { } virtual void resetRecording(int width, int height, - uirenderer::RenderNode* renderNode) override { + uirenderer::RenderNode* renderNode) override { initDisplayList(renderNode, width, height); } virtual uirenderer::DisplayList* finishRecording() override; - virtual void drawBitmap(Bitmap& bitmap, float left, float top, - const SkPaint* paint) override; - virtual void drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, - const SkPaint* paint) override; - virtual void drawBitmap(Bitmap& bitmap, float srcLeft, float srcTop, - float srcRight, float srcBottom, float dstLeft, float dstTop, - float dstRight, float dstBottom, const SkPaint* paint) override; + virtual void drawBitmap(Bitmap& bitmap, float left, float top, const SkPaint* paint) override; + virtual void drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, const SkPaint* paint) override; + virtual void drawBitmap(Bitmap& bitmap, float srcLeft, float srcTop, float srcRight, + float srcBottom, float dstLeft, float dstTop, float dstRight, + float dstBottom, const SkPaint* paint) override; virtual void drawNinePatch(Bitmap& hwuiBitmap, const android::Res_png_9patch& chunk, - float dstLeft, float dstTop, float dstRight, float dstBottom, - const SkPaint* paint) override; + float dstLeft, float dstTop, float dstRight, float dstBottom, + const SkPaint* paint) override; + virtual double drawAnimatedImage(AnimatedImageDrawable* animatedImage) override; virtual void drawRoundRect(uirenderer::CanvasPropertyPrimitive* left, - uirenderer::CanvasPropertyPrimitive* top, uirenderer::CanvasPropertyPrimitive* right, - uirenderer::CanvasPropertyPrimitive* bottom, uirenderer::CanvasPropertyPrimitive* rx, - uirenderer::CanvasPropertyPrimitive* ry, - uirenderer::CanvasPropertyPaint* paint) override; + uirenderer::CanvasPropertyPrimitive* top, + uirenderer::CanvasPropertyPrimitive* right, + uirenderer::CanvasPropertyPrimitive* bottom, + uirenderer::CanvasPropertyPrimitive* rx, + uirenderer::CanvasPropertyPrimitive* ry, + uirenderer::CanvasPropertyPaint* paint) override; virtual void drawCircle(uirenderer::CanvasPropertyPrimitive* x, - uirenderer::CanvasPropertyPrimitive* y, uirenderer::CanvasPropertyPrimitive* radius, - uirenderer::CanvasPropertyPaint* paint) override; + uirenderer::CanvasPropertyPrimitive* y, + uirenderer::CanvasPropertyPrimitive* radius, + uirenderer::CanvasPropertyPaint* paint) override; virtual void drawVectorDrawable(VectorDrawableRoot* vectorDrawable) override; @@ -88,6 +90,6 @@ private: void initDisplayList(uirenderer::RenderNode* renderNode, int width, int height); }; -}; // namespace skiapipeline -}; // namespace uirenderer -}; // namespace android +}; // namespace skiapipeline +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp index e1ef71f7d3ab..653007482c0a 100644 --- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp @@ -17,12 +17,12 @@ #include "SkiaVulkanPipeline.h" #include "DeferredLayerUpdater.h" -#include "renderthread/Frame.h" #include "Readback.h" -#include "renderstate/RenderState.h" #include "SkiaPipeline.h" #include "SkiaProfileRenderer.h" #include "VkLayer.h" +#include "renderstate/RenderState.h" +#include "renderthread/Frame.h" #include <SkSurface.h> #include <SkTypes.h> @@ -41,8 +41,7 @@ namespace uirenderer { namespace skiapipeline { SkiaVulkanPipeline::SkiaVulkanPipeline(renderthread::RenderThread& thread) - : SkiaPipeline(thread) - , mVkManager(thread.vulkanManager()) {} + : SkiaPipeline(thread), mVkManager(thread.vulkanManager()) {} MakeCurrentResult SkiaVulkanPipeline::makeCurrent() { return MakeCurrentResult::AlreadyCurrent; @@ -50,7 +49,7 @@ MakeCurrentResult SkiaVulkanPipeline::makeCurrent() { Frame SkiaVulkanPipeline::getFrame() { LOG_ALWAYS_FATAL_IF(mVkSurface == nullptr, - "drawRenderNode called on a context with no surface!"); + "drawRenderNode called on a context with no surface!"); SkSurface* backBuffer = mVkManager.getBackbufferSurface(mVkSurface); if (backBuffer == nullptr) { @@ -62,27 +61,25 @@ Frame SkiaVulkanPipeline::getFrame() { return frame; } -bool SkiaVulkanPipeline::draw(const Frame& frame, const SkRect& screenDirty, - const SkRect& dirty, - const FrameBuilder::LightGeometry& lightGeometry, - LayerUpdateQueue* layerUpdateQueue, - const Rect& contentDrawBounds, bool opaque, bool wideColorGamut, - const BakedOpRenderer::LightInfo& lightInfo, - const std::vector<sp<RenderNode>>& renderNodes, - FrameInfoVisualizer* profiler) { - +bool SkiaVulkanPipeline::draw(const Frame& frame, const SkRect& screenDirty, const SkRect& dirty, + const FrameBuilder::LightGeometry& lightGeometry, + LayerUpdateQueue* layerUpdateQueue, const Rect& contentDrawBounds, + bool opaque, bool wideColorGamut, + const BakedOpRenderer::LightInfo& lightInfo, + const std::vector<sp<RenderNode>>& renderNodes, + FrameInfoVisualizer* profiler) { sk_sp<SkSurface> backBuffer = mVkSurface->getBackBufferSurface(); if (backBuffer.get() == nullptr) { return false; } SkiaPipeline::updateLighting(lightGeometry, lightInfo); - renderFrame(*layerUpdateQueue, dirty, renderNodes, opaque, wideColorGamut, - contentDrawBounds, backBuffer); + renderFrame(*layerUpdateQueue, dirty, renderNodes, opaque, wideColorGamut, contentDrawBounds, + backBuffer); layerUpdateQueue->clear(); // Draw visual debugging features - if (CC_UNLIKELY(Properties::showDirtyRegions - || ProfileType::None != Properties::getProfileType())) { + if (CC_UNLIKELY(Properties::showDirtyRegions || + ProfileType::None != Properties::getProfileType())) { SkCanvas* profileCanvas = backBuffer->getCanvas(); SkiaProfileRenderer profileRenderer(profileCanvas); profiler->draw(profileRenderer); @@ -97,9 +94,8 @@ bool SkiaVulkanPipeline::draw(const Frame& frame, const SkRect& screenDirty, return true; } -bool SkiaVulkanPipeline::swapBuffers(const Frame& frame, bool drew, - const SkRect& screenDirty, FrameInfo* currentFrameInfo, bool* requireSwap) { - +bool SkiaVulkanPipeline::swapBuffers(const Frame& frame, bool drew, const SkRect& screenDirty, + FrameInfo* currentFrameInfo, bool* requireSwap) { *requireSwap = drew; // Even if we decided to cancel the frame, from the perspective of jank @@ -119,7 +115,7 @@ bool SkiaVulkanPipeline::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bi } static Layer* createLayer(RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight, - SkColorFilter* colorFilter, int alpha, SkBlendMode mode, bool blend) { + SkColorFilter* colorFilter, int alpha, SkBlendMode mode, bool blend) { return new VkLayer(renderState, layerWidth, layerHeight, colorFilter, alpha, mode, blend); } @@ -129,11 +125,10 @@ DeferredLayerUpdater* SkiaVulkanPipeline::createTextureLayer() { return new DeferredLayerUpdater(mRenderThread.renderState(), createLayer, Layer::Api::Vulkan); } -void SkiaVulkanPipeline::onStop() { -} +void SkiaVulkanPipeline::onStop() {} bool SkiaVulkanPipeline::setSurface(Surface* surface, SwapBehavior swapBehavior, - ColorMode colorMode) { + ColorMode colorMode) { if (mVkSurface) { mVkManager.destroySurface(mVkSurface); mVkSurface = nullptr; @@ -162,16 +157,15 @@ void SkiaVulkanPipeline::invokeFunctor(const RenderThread& thread, Functor* func } sk_sp<Bitmap> SkiaVulkanPipeline::allocateHardwareBitmap(renderthread::RenderThread& renderThread, - SkBitmap& skBitmap) { - //TODO: implement this function for Vulkan pipeline - //code below is a hack to avoid crashing because of missing HW Bitmap support - sp<GraphicBuffer> buffer = new GraphicBuffer(skBitmap.info().width(), skBitmap.info().height(), - PIXEL_FORMAT_RGBA_8888, - GraphicBuffer::USAGE_HW_TEXTURE | - GraphicBuffer::USAGE_SW_WRITE_NEVER | - GraphicBuffer::USAGE_SW_READ_NEVER, - std::string("SkiaVulkanPipeline::allocateHardwareBitmap pid [") - + std::to_string(getpid()) + "]"); + SkBitmap& skBitmap) { + // TODO: implement this function for Vulkan pipeline + // code below is a hack to avoid crashing because of missing HW Bitmap support + sp<GraphicBuffer> buffer = new GraphicBuffer( + skBitmap.info().width(), skBitmap.info().height(), PIXEL_FORMAT_RGBA_8888, + GraphicBuffer::USAGE_HW_TEXTURE | GraphicBuffer::USAGE_SW_WRITE_NEVER | + GraphicBuffer::USAGE_SW_READ_NEVER, + std::string("SkiaVulkanPipeline::allocateHardwareBitmap pid [") + + std::to_string(getpid()) + "]"); status_t error = buffer->initCheck(); if (error < 0) { ALOGW("SkiaVulkanPipeline::allocateHardwareBitmap() failed in GraphicBuffer.create()"); diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h index 263206d97571..03b4c79f2beb 100644 --- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h +++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h @@ -31,25 +31,24 @@ public: renderthread::MakeCurrentResult makeCurrent() override; renderthread::Frame getFrame() override; bool draw(const renderthread::Frame& frame, const SkRect& screenDirty, const SkRect& dirty, - const FrameBuilder::LightGeometry& lightGeometry, - LayerUpdateQueue* layerUpdateQueue, - const Rect& contentDrawBounds, bool opaque, bool wideColorGamut, - const BakedOpRenderer::LightInfo& lightInfo, - const std::vector< sp<RenderNode> >& renderNodes, - FrameInfoVisualizer* profiler) override; + const FrameBuilder::LightGeometry& lightGeometry, LayerUpdateQueue* layerUpdateQueue, + const Rect& contentDrawBounds, bool opaque, bool wideColorGamut, + const BakedOpRenderer::LightInfo& lightInfo, + const std::vector<sp<RenderNode> >& renderNodes, + FrameInfoVisualizer* profiler) override; bool swapBuffers(const renderthread::Frame& frame, bool drew, const SkRect& screenDirty, - FrameInfo* currentFrameInfo, bool* requireSwap) override; + FrameInfo* currentFrameInfo, bool* requireSwap) override; bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) override; DeferredLayerUpdater* createTextureLayer() override; bool setSurface(Surface* window, renderthread::SwapBehavior swapBehavior, - renderthread::ColorMode colorMode) override; + renderthread::ColorMode colorMode) override; void onStop() override; bool isSurfaceReady() override; bool isContextReady() override; static void invokeFunctor(const renderthread::RenderThread& thread, Functor* functor); static sk_sp<Bitmap> allocateHardwareBitmap(renderthread::RenderThread& thread, - SkBitmap& skBitmap); + SkBitmap& skBitmap); private: renderthread::VulkanManager& mVkManager; diff --git a/libs/hwui/pipeline/skia/SkiaVulkanReadback.h b/libs/hwui/pipeline/skia/SkiaVulkanReadback.h new file mode 100644 index 000000000000..65b89d617f7b --- /dev/null +++ b/libs/hwui/pipeline/skia/SkiaVulkanReadback.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "Readback.h" + +namespace android { +namespace uirenderer { +namespace skiapipeline { + +class SkiaVulkanReadback : public Readback { +public: + SkiaVulkanReadback(renderthread::RenderThread& thread) : Readback(thread) {} + + virtual CopyResult copySurfaceInto(Surface& surface, const Rect& srcRect, + SkBitmap* bitmap) override { + //TODO: implement Vulkan readback. + return CopyResult::UnknownError; + } + + virtual CopyResult copyGraphicBufferInto(GraphicBuffer* graphicBuffer, + SkBitmap* bitmap) override { + //TODO: implement Vulkan readback. + return CopyResult::UnknownError; + } +}; + +} /* namespace skiapipeline */ +} /* namespace uirenderer */ +} /* namespace android */ diff --git a/libs/hwui/pipeline/skia/VectorDrawableAtlas.cpp b/libs/hwui/pipeline/skia/VectorDrawableAtlas.cpp index 437653a8dfa8..8fb621d24866 100644 --- a/libs/hwui/pipeline/skia/VectorDrawableAtlas.cpp +++ b/libs/hwui/pipeline/skia/VectorDrawableAtlas.cpp @@ -19,8 +19,9 @@ #include <GrRectanizer_pow2.h> #include <SkCanvas.h> #include <cmath> -#include "utils/TraceUtils.h" #include "renderthread/RenderProxy.h" +#include "renderthread/RenderThread.h" +#include "utils/TraceUtils.h" namespace android { namespace uirenderer { @@ -29,8 +30,7 @@ namespace skiapipeline { VectorDrawableAtlas::VectorDrawableAtlas(size_t surfaceArea, StorageMode storageMode) : mWidth((int)std::sqrt(surfaceArea)) , mHeight((int)std::sqrt(surfaceArea)) - , mStorageMode(storageMode) { -} + , mStorageMode(storageMode) {} void VectorDrawableAtlas::prepareForDraw(GrContext* context) { if (StorageMode::allowSharedSurface == mStorageMode) { @@ -54,8 +54,8 @@ void VectorDrawableAtlas::prepareForDraw(GrContext* context) { #define MAX_UNUSED_RATIO 2.0f bool VectorDrawableAtlas::isFragmented() { - return mConsecutiveFailures > MAX_CONSECUTIVE_FAILURES - && mPixelUsedByVDs*MAX_UNUSED_RATIO < mPixelAllocated; + return mConsecutiveFailures > MAX_CONSECUTIVE_FAILURES && + mPixelUsedByVDs * MAX_UNUSED_RATIO < mPixelAllocated; } void VectorDrawableAtlas::repackIfNeeded(GrContext* context) { @@ -68,9 +68,9 @@ void VectorDrawableAtlas::repackIfNeeded(GrContext* context) { } // compare to CacheEntry objects based on VD area. -bool VectorDrawableAtlas::compareCacheEntry(const CacheEntry& first, const CacheEntry& second) -{ - return first.VDrect.width()*first.VDrect.height() < second.VDrect.width()*second.VDrect.height(); +bool VectorDrawableAtlas::compareCacheEntry(const CacheEntry& first, const CacheEntry& second) { + return first.VDrect.width() * first.VDrect.height() < + second.VDrect.width() * second.VDrect.height(); } void VectorDrawableAtlas::repack(GrContext* context) { @@ -87,7 +87,7 @@ void VectorDrawableAtlas::repack(GrContext* context) { mRectanizer = std::make_unique<GrRectanizerPow2>(mWidth, mHeight); } else { if (!mSurface) { - return; //nothing to repack + return; // nothing to repack } mRectanizer.reset(); } @@ -105,20 +105,20 @@ void VectorDrawableAtlas::repack(GrContext* context) { for (CacheEntry& entry : mRects) { SkRect currentVDRect = entry.VDrect; - SkImage* sourceImage; //copy either from the atlas or from a standalone surface + SkImage* sourceImage; // copy either from the atlas or from a standalone surface if (entry.surface) { if (!fitInAtlas(currentVDRect.width(), currentVDRect.height())) { - continue; //don't even try to repack huge VD + continue; // don't even try to repack huge VD } sourceImage = entry.surface->makeImageSnapshot().get(); } else { sourceImage = sourceImageAtlas; } - size_t VDRectArea = currentVDRect.width()*currentVDRect.height(); + size_t VDRectArea = currentVDRect.width() * currentVDRect.height(); SkIPoint16 pos; if (canvas && mRectanizer->addRect(currentVDRect.width(), currentVDRect.height(), &pos)) { - SkRect newRect = SkRect::MakeXYWH(pos.fX, pos.fY, currentVDRect.width(), - currentVDRect.height()); + SkRect newRect = + SkRect::MakeXYWH(pos.fX, pos.fY, currentVDRect.width(), currentVDRect.height()); canvas->drawImageRect(sourceImage, currentVDRect, newRect, nullptr); entry.VDrect = newRect; entry.rect = newRect; @@ -133,8 +133,7 @@ void VectorDrawableAtlas::repack(GrContext* context) { if (!entry.surface) { // A rectangle moved from an atlas to a standalone surface. mPixelUsedByVDs -= VDRectArea; - SkRect newRect = SkRect::MakeWH(currentVDRect.width(), - currentVDRect.height()); + SkRect newRect = SkRect::MakeWH(currentVDRect.width(), currentVDRect.height()); entry.surface = createSurface(newRect.width(), newRect.height(), context); auto tempCanvas = entry.surface->getCanvas(); tempCanvas->clear(SK_ColorTRANSPARENT); @@ -157,7 +156,7 @@ AtlasEntry VectorDrawableAtlas::requestNewEntry(int width, int height, GrContext } if (mSurface) { - const size_t area = width*height; + const size_t area = width * height; // Use a rectanizer to allocate unused space from the atlas surface. bool notTooBig = fitInAtlas(width, height); @@ -228,15 +227,24 @@ AtlasEntry VectorDrawableAtlas::getEntry(AtlasKey atlasKey) { void VectorDrawableAtlas::releaseEntry(AtlasKey atlasKey) { if (INVALID_ATLAS_KEY != atlasKey) { + if (!renderthread::RenderThread::isCurrent()) { + { + AutoMutex _lock(mReleaseKeyLock); + mKeysForRelease.push_back(atlasKey); + } + // invoke releaseEntry on the renderthread + renderthread::RenderProxy::releaseVDAtlasEntries(); + return; + } CacheEntry* entry = reinterpret_cast<CacheEntry*>(atlasKey); if (!entry->surface) { // Store freed atlas rectangles in "mFreeRects" and try to reuse them later, when atlas // is full. SkRect& removedRect = entry->rect; - size_t rectArea = removedRect.width()*removedRect.height(); + size_t rectArea = removedRect.width() * removedRect.height(); mFreeRects.emplace(rectArea, removedRect); SkRect& removedVDRect = entry->VDrect; - size_t VDRectArea = removedVDRect.width()*removedVDRect.height(); + size_t VDRectArea = removedVDRect.width() * removedVDRect.height(); mPixelUsedByVDs -= VDRectArea; mConsecutiveFailures = 0; } @@ -245,6 +253,14 @@ void VectorDrawableAtlas::releaseEntry(AtlasKey atlasKey) { } } +void VectorDrawableAtlas::delayedReleaseEntries() { + AutoMutex _lock(mReleaseKeyLock); + for (auto key : mKeysForRelease) { + releaseEntry(key); + } + mKeysForRelease.clear(); +} + sk_sp<SkSurface> VectorDrawableAtlas::createSurface(int width, int height, GrContext* context) { #ifndef ANDROID_ENABLE_LINEAR_BLENDING sk_sp<SkColorSpace> colorSpace = nullptr; @@ -252,7 +268,10 @@ sk_sp<SkSurface> VectorDrawableAtlas::createSurface(int width, int height, GrCon sk_sp<SkColorSpace> colorSpace = SkColorSpace::MakeSRGB(); #endif SkImageInfo info = SkImageInfo::MakeN32(width, height, kPremul_SkAlphaType, colorSpace); - return SkSurface::MakeRenderTarget(context, SkBudgeted::kYes, info); + // This must have a top-left origin so that calls to surface->canvas->writePixels + // performs a basic texture upload instead of a more complex drawing operation + return SkSurface::MakeRenderTarget(context, SkBudgeted::kYes, info, 0, kTopLeft_GrSurfaceOrigin, + nullptr); } void VectorDrawableAtlas::setStorageMode(StorageMode mode) { diff --git a/libs/hwui/pipeline/skia/VectorDrawableAtlas.h b/libs/hwui/pipeline/skia/VectorDrawableAtlas.h index 496c55742748..74e48cea2a87 100644 --- a/libs/hwui/pipeline/skia/VectorDrawableAtlas.h +++ b/libs/hwui/pipeline/skia/VectorDrawableAtlas.h @@ -16,11 +16,12 @@ #pragma once -#include <map> #include <SkSurface.h> #include <utils/FatVector.h> #include <utils/RefBase.h> +#include <utils/Thread.h> #include <list> +#include <map> class GrRectanizer; @@ -54,18 +55,15 @@ struct AtlasEntry { * When a VectorDrawable is deleted, it invokes VectorDrawableAtlas::releaseEntry, which is keeping * track of free spaces and allow to reuse the surface for another VD. */ - //TODO: Check if not using atlas for AnimatedVD is more efficient. - //TODO: For low memory situations, when there are no paint effects in VD, we may render without an - //TODO: offscreen surface. +// TODO: Check if not using atlas for AnimatedVD is more efficient. +// TODO: For low memory situations, when there are no paint effects in VD, we may render without an +// TODO: offscreen surface. class VectorDrawableAtlas : public virtual RefBase { public: - enum class StorageMode { - allowSharedSurface, - disallowSharedSurface - }; + enum class StorageMode { allowSharedSurface, disallowSharedSurface }; VectorDrawableAtlas(size_t surfaceArea, - StorageMode storageMode = StorageMode::allowSharedSurface); + StorageMode storageMode = StorageMode::allowSharedSurface); /** * "prepareForDraw" may allocate a new surface if needed. It may schedule to repack the @@ -103,19 +101,24 @@ public: /** * "releaseEntry" is invoked when a VectorDrawable is deleted. Passing a non-existing "atlasKey" - * is causing an undefined behaviour. + * is causing an undefined behaviour. This is the only function in the class that can be + * invoked from any thread. It will marshal internally to render thread if needed. */ void releaseEntry(AtlasKey atlasKey); void setStorageMode(StorageMode mode); + /** + * "delayedReleaseEntries" is indirectly invoked by "releaseEntry", when "releaseEntry" is + * invoked from a non render thread. + */ + void delayedReleaseEntries(); + private: struct CacheEntry { CacheEntry(const SkRect& newVDrect, const SkRect& newRect, - const sk_sp<SkSurface>& newSurface) - : VDrect(newVDrect) - , rect(newRect) - , surface(newSurface) { } + const sk_sp<SkSurface>& newSurface) + : VDrect(newVDrect), rect(newRect), surface(newSurface) {} /** * size and position of VectorDrawable into the atlas or in "this.surface" @@ -182,10 +185,21 @@ private: */ StorageMode mStorageMode; + /** + * mKeysForRelease is used by releaseEntry implementation to pass atlas keys from an arbitrary + * calling thread to the render thread. + */ + std::vector<AtlasKey> mKeysForRelease; + + /** + * A lock used to protect access to mKeysForRelease. + */ + Mutex mReleaseKeyLock; + sk_sp<SkSurface> createSurface(int width, int height, GrContext* context); inline bool fitInAtlas(int width, int height) { - return 2*width < mWidth && 2*height < mHeight; + return 2 * width < mWidth && 2 * height < mHeight; } void repack(GrContext* context); diff --git a/libs/hwui/private/hwui/DrawGlInfo.h b/libs/hwui/private/hwui/DrawGlInfo.h index f57851322716..efa9da27199d 100644 --- a/libs/hwui/private/hwui/DrawGlInfo.h +++ b/libs/hwui/private/hwui/DrawGlInfo.h @@ -81,9 +81,9 @@ struct DrawGlInfo { // commands are issued. kStatusDrew = 0x4 }; -}; // struct DrawGlInfo +}; // struct DrawGlInfo -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android -#endif // ANDROID_HWUI_DRAW_GL_INFO_H +#endif // ANDROID_HWUI_DRAW_GL_INFO_H diff --git a/libs/hwui/protos/ProtoHelpers.h b/libs/hwui/protos/ProtoHelpers.h index 832e31200eb6..833c77f2b8cb 100644 --- a/libs/hwui/protos/ProtoHelpers.h +++ b/libs/hwui/protos/ProtoHelpers.h @@ -35,7 +35,7 @@ void set(std::string* dest, const SkPath& src) { src.writeToMemory(&*dest->begin()); } -} // namespace uirenderer -} // namespace android +} // namespace uirenderer +} // namespace android -#endif // PROTOHELPERS_H +#endif // PROTOHELPERS_H diff --git a/libs/hwui/renderstate/Blend.cpp b/libs/hwui/renderstate/Blend.cpp index b1ca4a248a80..5bef01c0ec3c 100644 --- a/libs/hwui/renderstate/Blend.cpp +++ b/libs/hwui/renderstate/Blend.cpp @@ -52,49 +52,42 @@ static_assert(17 == static_cast<int>(SkBlendMode::kLighten), "SkBlendMode enums // In this array, the index of each Blender equals the value of the first // entry. For instance, gBlends[1] == gBlends[SkBlendMode::kSrc] -const Blender kBlends[] = { - { SkBlendMode::kClear, GL_ZERO, GL_ONE_MINUS_SRC_ALPHA }, - { SkBlendMode::kSrc, GL_ONE, GL_ZERO }, - { SkBlendMode::kDst, GL_ZERO, GL_ONE }, - { SkBlendMode::kSrcOver, GL_ONE, GL_ONE_MINUS_SRC_ALPHA }, - { SkBlendMode::kDstOver, GL_ONE_MINUS_DST_ALPHA, GL_ONE }, - { SkBlendMode::kSrcIn, GL_DST_ALPHA, GL_ZERO }, - { SkBlendMode::kDstIn, GL_ZERO, GL_SRC_ALPHA }, - { SkBlendMode::kSrcOut, GL_ONE_MINUS_DST_ALPHA, GL_ZERO }, - { SkBlendMode::kDstOut, GL_ZERO, GL_ONE_MINUS_SRC_ALPHA }, - { SkBlendMode::kSrcATop, GL_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA }, - { SkBlendMode::kDstATop, GL_ONE_MINUS_DST_ALPHA, GL_SRC_ALPHA }, - { SkBlendMode::kXor, GL_ONE_MINUS_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA }, - { SkBlendMode::kPlus, GL_ONE, GL_ONE }, - { SkBlendMode::kModulate, GL_ZERO, GL_SRC_COLOR }, - { SkBlendMode::kScreen, GL_ONE, GL_ONE_MINUS_SRC_COLOR } -}; +const Blender kBlends[] = {{SkBlendMode::kClear, GL_ZERO, GL_ONE_MINUS_SRC_ALPHA}, + {SkBlendMode::kSrc, GL_ONE, GL_ZERO}, + {SkBlendMode::kDst, GL_ZERO, GL_ONE}, + {SkBlendMode::kSrcOver, GL_ONE, GL_ONE_MINUS_SRC_ALPHA}, + {SkBlendMode::kDstOver, GL_ONE_MINUS_DST_ALPHA, GL_ONE}, + {SkBlendMode::kSrcIn, GL_DST_ALPHA, GL_ZERO}, + {SkBlendMode::kDstIn, GL_ZERO, GL_SRC_ALPHA}, + {SkBlendMode::kSrcOut, GL_ONE_MINUS_DST_ALPHA, GL_ZERO}, + {SkBlendMode::kDstOut, GL_ZERO, GL_ONE_MINUS_SRC_ALPHA}, + {SkBlendMode::kSrcATop, GL_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA}, + {SkBlendMode::kDstATop, GL_ONE_MINUS_DST_ALPHA, GL_SRC_ALPHA}, + {SkBlendMode::kXor, GL_ONE_MINUS_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA}, + {SkBlendMode::kPlus, GL_ONE, GL_ONE}, + {SkBlendMode::kModulate, GL_ZERO, GL_SRC_COLOR}, + {SkBlendMode::kScreen, GL_ONE, GL_ONE_MINUS_SRC_COLOR}}; // This array contains the swapped version of each SkBlendMode. For instance // this array's SrcOver blending mode is actually DstOver. You can refer to // createLayer() for more information on the purpose of this array. -const Blender kBlendsSwap[] = { - { SkBlendMode::kClear, GL_ONE_MINUS_DST_ALPHA, GL_ZERO }, - { SkBlendMode::kSrc, GL_ZERO, GL_ONE }, - { SkBlendMode::kDst, GL_ONE, GL_ZERO }, - { SkBlendMode::kSrcOver, GL_ONE_MINUS_DST_ALPHA, GL_ONE }, - { SkBlendMode::kDstOver, GL_ONE, GL_ONE_MINUS_SRC_ALPHA }, - { SkBlendMode::kSrcIn, GL_ZERO, GL_SRC_ALPHA }, - { SkBlendMode::kDstIn, GL_DST_ALPHA, GL_ZERO }, - { SkBlendMode::kSrcOut, GL_ZERO, GL_ONE_MINUS_SRC_ALPHA }, - { SkBlendMode::kDstOut, GL_ONE_MINUS_DST_ALPHA, GL_ZERO }, - { SkBlendMode::kSrcATop, GL_ONE_MINUS_DST_ALPHA, GL_SRC_ALPHA }, - { SkBlendMode::kDstATop, GL_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA }, - { SkBlendMode::kXor, GL_ONE_MINUS_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA }, - { SkBlendMode::kPlus, GL_ONE, GL_ONE }, - { SkBlendMode::kModulate, GL_DST_COLOR, GL_ZERO }, - { SkBlendMode::kScreen, GL_ONE_MINUS_DST_COLOR, GL_ONE } -}; +const Blender kBlendsSwap[] = {{SkBlendMode::kClear, GL_ONE_MINUS_DST_ALPHA, GL_ZERO}, + {SkBlendMode::kSrc, GL_ZERO, GL_ONE}, + {SkBlendMode::kDst, GL_ONE, GL_ZERO}, + {SkBlendMode::kSrcOver, GL_ONE_MINUS_DST_ALPHA, GL_ONE}, + {SkBlendMode::kDstOver, GL_ONE, GL_ONE_MINUS_SRC_ALPHA}, + {SkBlendMode::kSrcIn, GL_ZERO, GL_SRC_ALPHA}, + {SkBlendMode::kDstIn, GL_DST_ALPHA, GL_ZERO}, + {SkBlendMode::kSrcOut, GL_ZERO, GL_ONE_MINUS_SRC_ALPHA}, + {SkBlendMode::kDstOut, GL_ONE_MINUS_DST_ALPHA, GL_ZERO}, + {SkBlendMode::kSrcATop, GL_ONE_MINUS_DST_ALPHA, GL_SRC_ALPHA}, + {SkBlendMode::kDstATop, GL_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA}, + {SkBlendMode::kXor, GL_ONE_MINUS_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA}, + {SkBlendMode::kPlus, GL_ONE, GL_ONE}, + {SkBlendMode::kModulate, GL_DST_COLOR, GL_ZERO}, + {SkBlendMode::kScreen, GL_ONE_MINUS_DST_COLOR, GL_ONE}}; -Blend::Blend() - : mEnabled(false) - , mSrcMode(GL_ZERO) - , mDstMode(GL_ZERO) { +Blend::Blend() : mEnabled(false), mSrcMode(GL_ZERO), mDstMode(GL_ZERO) { // gl blending off by default } @@ -145,4 +138,3 @@ void Blend::dump() { } /* namespace uirenderer */ } /* namespace android */ - diff --git a/libs/hwui/renderstate/Blend.h b/libs/hwui/renderstate/Blend.h index a9de24631340..7e559bace3f2 100644 --- a/libs/hwui/renderstate/Blend.h +++ b/libs/hwui/renderstate/Blend.h @@ -28,6 +28,7 @@ namespace uirenderer { class Blend { friend class RenderState; + public: // dictates whether to swap src/dst enum class ModeOrderSwap { @@ -36,19 +37,18 @@ public: }; void syncEnabled(); - static void getFactors(SkBlendMode mode, ModeOrderSwap modeUsage, - GLenum* outSrc, GLenum* outDst); + static void getFactors(SkBlendMode mode, ModeOrderSwap modeUsage, GLenum* outSrc, + GLenum* outDst); void setFactors(GLenum src, GLenum dst); - bool getEnabled() { - return mEnabled; - } + bool getEnabled() { return mEnabled; } void getFactors(GLenum* src, GLenum* dst) { *src = mSrcMode; *dst = mDstMode; } void dump(); + private: Blend(); void invalidate(); @@ -60,4 +60,4 @@ private: } /* namespace uirenderer */ } /* namespace android */ -#endif // RENDERSTATE_BLEND_H +#endif // RENDERSTATE_BLEND_H diff --git a/libs/hwui/renderstate/MeshState.cpp b/libs/hwui/renderstate/MeshState.cpp index 6d0293695412..4f6c49e67b99 100644 --- a/libs/hwui/renderstate/MeshState.cpp +++ b/libs/hwui/renderstate/MeshState.cpp @@ -38,12 +38,12 @@ MeshState::MeshState() for (uint32_t i = 0; i < kMaxNumberOfQuads; i++) { uint16_t quad = i * 4; int index = i * 6; - regionIndices[index ] = quad; // top-left - regionIndices[index + 1] = quad + 1; // top-right - regionIndices[index + 2] = quad + 2; // bottom-left - regionIndices[index + 3] = quad + 2; // bottom-left - regionIndices[index + 4] = quad + 1; // top-right - regionIndices[index + 5] = quad + 3; // bottom-right + regionIndices[index] = quad; // top-left + regionIndices[index + 1] = quad + 1; // top-right + regionIndices[index + 2] = quad + 2; // bottom-left + regionIndices[index + 3] = quad + 2; // bottom-left + regionIndices[index + 4] = quad + 1; // top-right + regionIndices[index + 5] = quad + 3; // bottom-right } glGenBuffers(1, &mQuadListIndices); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mQuadListIndices); @@ -65,10 +65,10 @@ MeshState::~MeshState() { void MeshState::dump() { ALOGD("MeshState VBOs: unitQuad %d, current %d", mUnitQuadBuffer, mCurrentBuffer); ALOGD("MeshState IBOs: quadList %d, current %d", mQuadListIndices, mCurrentIndicesBuffer); - ALOGD("MeshState vertices: vertex data %p, stride %d", - mCurrentPositionPointer, mCurrentPositionStride); - ALOGD("MeshState texCoord: data %p, stride %d", - mCurrentTexCoordsPointer, mCurrentTexCoordsStride); + ALOGD("MeshState vertices: vertex data %p, stride %d", mCurrentPositionPointer, + mCurrentPositionStride); + ALOGD("MeshState texCoord: data %p, stride %d", mCurrentTexCoordsPointer, + mCurrentTexCoordsStride); } /////////////////////////////////////////////////////////////////////////////// @@ -89,8 +89,8 @@ void MeshState::unbindMeshBuffer() { return bindMeshBuffer(0); } -void MeshState::genOrUpdateMeshBuffer(GLuint* buffer, GLsizeiptr size, - const void* data, GLenum usage) { +void MeshState::genOrUpdateMeshBuffer(GLuint* buffer, GLsizeiptr size, const void* data, + GLenum usage) { if (!*buffer) { glGenBuffers(1, buffer); } @@ -98,8 +98,8 @@ void MeshState::genOrUpdateMeshBuffer(GLuint* buffer, GLsizeiptr size, glBufferData(GL_ARRAY_BUFFER, size, data, usage); } -void MeshState::updateMeshBufferSubData(GLuint buffer, GLintptr offset, - GLsizeiptr size, const void* data) { +void MeshState::updateMeshBufferSubData(GLuint buffer, GLintptr offset, GLsizeiptr size, + const void* data) { bindMeshBuffer(buffer); glBufferSubData(GL_ARRAY_BUFFER, offset, size, data); } @@ -119,9 +119,8 @@ void MeshState::deleteMeshBuffer(GLuint buffer) { void MeshState::bindPositionVertexPointer(const GLvoid* vertices, GLsizei stride) { // update pos coords if !current vbo, since vertices may point into mutable memory (e.g. stack) - if (mCurrentBuffer == 0 - || vertices != mCurrentPositionPointer - || stride != mCurrentPositionStride) { + if (mCurrentBuffer == 0 || vertices != mCurrentPositionPointer || + stride != mCurrentPositionStride) { glVertexAttribPointer(Program::kBindingPosition, 2, GL_FLOAT, GL_FALSE, stride, vertices); mCurrentPositionPointer = vertices; mCurrentPositionStride = stride; @@ -130,9 +129,8 @@ void MeshState::bindPositionVertexPointer(const GLvoid* vertices, GLsizei stride void MeshState::bindTexCoordsVertexPointer(const GLvoid* vertices, GLsizei stride) { // update tex coords if !current vbo, since vertices may point into mutable memory (e.g. stack) - if (mCurrentBuffer == 0 - || vertices != mCurrentTexCoordsPointer - || stride != mCurrentTexCoordsStride) { + if (mCurrentBuffer == 0 || vertices != mCurrentTexCoordsPointer || + stride != mCurrentTexCoordsStride) { glVertexAttribPointer(Program::kBindingTexCoords, 2, GL_FLOAT, GL_FALSE, stride, vertices); mCurrentTexCoordsPointer = vertices; mCurrentTexCoordsStride = stride; @@ -179,4 +177,3 @@ void MeshState::unbindIndicesBuffer() { } /* namespace uirenderer */ } /* namespace android */ - diff --git a/libs/hwui/renderstate/MeshState.h b/libs/hwui/renderstate/MeshState.h index 17ad4622e44a..95faf1ebfb02 100644 --- a/libs/hwui/renderstate/MeshState.h +++ b/libs/hwui/renderstate/MeshState.h @@ -33,10 +33,7 @@ const uint32_t kMaxNumberOfQuads = 2048; // This array is never used directly but used as a memcpy source in the // OpenGLRenderer constructor const TextureVertex kUnitQuadVertices[] = { - { 0, 0, 0, 0 }, - { 1, 0, 1, 0 }, - { 0, 1, 0, 1 }, - { 1, 1, 1, 1 }, + {0, 0, 0, 0}, {1, 0, 1, 0}, {0, 1, 0, 1}, {1, 1, 1, 1}, }; const GLsizei kVertexStride = sizeof(Vertex); @@ -82,15 +79,13 @@ public: * Binds an attrib to the specified float vertex pointer. * Assumes a stride of gTextureVertexStride and a size of 2. */ - void bindPositionVertexPointer(const GLvoid* vertices, - GLsizei stride = kTextureVertexStride); + void bindPositionVertexPointer(const GLvoid* vertices, GLsizei stride = kTextureVertexStride); /** * Binds an attrib to the specified float vertex pointer. * Assumes a stride of gTextureVertexStride and a size of 2. */ - void bindTexCoordsVertexPointer(const GLvoid* vertices, - GLsizei stride = kTextureVertexStride); + void bindTexCoordsVertexPointer(const GLvoid* vertices, GLsizei stride = kTextureVertexStride); /** * Resets the vertex pointers. @@ -111,6 +106,7 @@ public: /////////////////////////////////////////////////////////////////////////////// GLuint getUnitQuadVBO() { return mUnitQuadBuffer; } GLuint getQuadListIBO() { return mQuadListIndices; } + private: MeshState(); @@ -134,4 +130,4 @@ private: } /* namespace uirenderer */ } /* namespace android */ -#endif // RENDERSTATE_MESHSTATE_H +#endif // RENDERSTATE_MESHSTATE_H diff --git a/libs/hwui/renderstate/OffscreenBufferPool.cpp b/libs/hwui/renderstate/OffscreenBufferPool.cpp index ea292d678c67..a0f5cb9d4e09 100644 --- a/libs/hwui/renderstate/OffscreenBufferPool.cpp +++ b/libs/hwui/renderstate/OffscreenBufferPool.cpp @@ -33,8 +33,8 @@ namespace uirenderer { // OffscreenBuffer //////////////////////////////////////////////////////////////////////////////// -OffscreenBuffer::OffscreenBuffer(RenderState& renderState, Caches& caches, - uint32_t viewportWidth, uint32_t viewportHeight, bool wideColorGamut) +OffscreenBuffer::OffscreenBuffer(RenderState& renderState, Caches& caches, uint32_t viewportWidth, + uint32_t viewportHeight, bool wideColorGamut) : GpuMemoryTracker(GpuObjectType::OffscreenBuffer) , renderState(renderState) , viewportWidth(viewportWidth) @@ -45,8 +45,8 @@ OffscreenBuffer::OffscreenBuffer(RenderState& renderState, Caches& caches, uint32_t height = computeIdealDimension(viewportHeight); ATRACE_FORMAT("Allocate %ux%u HW Layer", width, height); caches.textureState().activateTexture(0); - texture.resize(width, height, - wideColorGamut ? GL_RGBA16F : caches.rgbaInternalFormat(), GL_RGBA); + texture.resize(width, height, wideColorGamut ? GL_RGBA16F : caches.rgbaInternalFormat(), + GL_RGBA); texture.blend = true; texture.setWrap(GL_CLAMP_TO_EDGE); // not setting filter on texture, since it's set when drawing, based on transform @@ -61,8 +61,8 @@ Rect OffscreenBuffer::getTextureCoordinates() { void OffscreenBuffer::dirty(Rect dirtyArea) { dirtyArea.doIntersect(0, 0, viewportWidth, viewportHeight); if (!dirtyArea.isEmpty()) { - region.orSelf(android::Rect(dirtyArea.left, dirtyArea.top, - dirtyArea.right, dirtyArea.bottom)); + region.orSelf( + android::Rect(dirtyArea.left, dirtyArea.top, dirtyArea.right, dirtyArea.bottom)); } } @@ -78,7 +78,8 @@ void OffscreenBuffer::updateMeshFromRegion() { const float texX = 1.0f / float(texture.width()); const float texY = 1.0f / float(texture.height()); - FatVector<TextureVertex, 64> meshVector(count * 4); // uses heap if more than 64 vertices needed + FatVector<TextureVertex, 64> meshVector(count * + 4); // uses heap if more than 64 vertices needed TextureVertex* mesh = &meshVector[0]; for (size_t i = 0; i < count; i++) { const android::Rect* r = &rects[i]; @@ -94,10 +95,9 @@ void OffscreenBuffer::updateMeshFromRegion() { TextureVertex::set(mesh++, r->right, r->bottom, u2, v2); } elementCount = count * 6; - renderState.meshState().genOrUpdateMeshBuffer(&vbo, - sizeof(TextureVertex) * count * 4, - &meshVector[0], - GL_DYNAMIC_DRAW); // TODO: GL_STATIC_DRAW if savelayer + renderState.meshState().genOrUpdateMeshBuffer( + &vbo, sizeof(TextureVertex) * count * 4, &meshVector[0], + GL_DYNAMIC_DRAW); // TODO: GL_STATIC_DRAW if savelayer } uint32_t OffscreenBuffer::computeIdealDimension(uint32_t dimension) { @@ -117,12 +117,11 @@ OffscreenBuffer::~OffscreenBuffer() { /////////////////////////////////////////////////////////////////////////////// OffscreenBufferPool::OffscreenBufferPool() - // 4 screen-sized RGBA_8888 textures - : mMaxSize(DeviceInfo::multiplyByResolution(4 * 4)) { -} + // 4 screen-sized RGBA_8888 textures + : mMaxSize(DeviceInfo::multiplyByResolution(4 * 4)) {} OffscreenBufferPool::~OffscreenBufferPool() { - clear(); // TODO: unique_ptr? + clear(); // TODO: unique_ptr? } int OffscreenBufferPool::Entry::compare(const Entry& lhs, const Entry& rhs) { @@ -143,8 +142,8 @@ void OffscreenBufferPool::clear() { mSize = 0; } -OffscreenBuffer* OffscreenBufferPool::get(RenderState& renderState, - const uint32_t width, const uint32_t height, bool wideColorGamut) { +OffscreenBuffer* OffscreenBufferPool::get(RenderState& renderState, const uint32_t width, + const uint32_t height, bool wideColorGamut) { OffscreenBuffer* layer = nullptr; Entry entry(width, height, wideColorGamut); @@ -159,18 +158,18 @@ OffscreenBuffer* OffscreenBufferPool::get(RenderState& renderState, layer->viewportHeight = height; mSize -= layer->getSizeInBytes(); } else { - layer = new OffscreenBuffer(renderState, Caches::getInstance(), - width, height, wideColorGamut); + layer = new OffscreenBuffer(renderState, Caches::getInstance(), width, height, + wideColorGamut); } return layer; } -OffscreenBuffer* OffscreenBufferPool::resize(OffscreenBuffer* layer, - const uint32_t width, const uint32_t height) { +OffscreenBuffer* OffscreenBufferPool::resize(OffscreenBuffer* layer, const uint32_t width, + const uint32_t height) { RenderState& renderState = layer->renderState; - if (layer->texture.width() == OffscreenBuffer::computeIdealDimension(width) - && layer->texture.height() == OffscreenBuffer::computeIdealDimension(height)) { + if (layer->texture.width() == OffscreenBuffer::computeIdealDimension(width) && + layer->texture.height() == OffscreenBuffer::computeIdealDimension(height)) { // resize in place layer->viewportWidth = width; layer->viewportHeight = height; @@ -214,5 +213,5 @@ void OffscreenBufferPool::putOrDelete(OffscreenBuffer* layer) { } } -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/renderstate/OffscreenBufferPool.h b/libs/hwui/renderstate/OffscreenBufferPool.h index d9422c9edd69..08ae052da391 100644 --- a/libs/hwui/renderstate/OffscreenBufferPool.h +++ b/libs/hwui/renderstate/OffscreenBufferPool.h @@ -18,10 +18,10 @@ #define ANDROID_HWUI_OFFSCREEN_BUFFER_POOL_H #include <GpuMemoryTracker.h> +#include <ui/Region.h> #include "Caches.h" #include "Texture.h" #include "utils/Macros.h" -#include <ui/Region.h> #include <set> @@ -42,8 +42,8 @@ class RenderState; */ class OffscreenBuffer : GpuMemoryTracker { public: - OffscreenBuffer(RenderState& renderState, Caches& caches, - uint32_t viewportWidth, uint32_t viewportHeight, bool wideColorGamut = false); + OffscreenBuffer(RenderState& renderState, Caches& caches, uint32_t viewportWidth, + uint32_t viewportHeight, bool wideColorGamut = false); ~OffscreenBuffer(); Rect getTextureCoordinates(); @@ -91,11 +91,11 @@ public: OffscreenBufferPool(); ~OffscreenBufferPool(); - WARN_UNUSED_RESULT OffscreenBuffer* get(RenderState& renderState, - const uint32_t width, const uint32_t height, bool wideColorGamut = false); + WARN_UNUSED_RESULT OffscreenBuffer* get(RenderState& renderState, const uint32_t width, + const uint32_t height, bool wideColorGamut = false); - WARN_UNUSED_RESULT OffscreenBuffer* resize(OffscreenBuffer* layer, - const uint32_t width, const uint32_t height); + WARN_UNUSED_RESULT OffscreenBuffer* resize(OffscreenBuffer* layer, const uint32_t width, + const uint32_t height); void putOrDelete(OffscreenBuffer* layer); @@ -120,6 +120,7 @@ public: * Prints out the content of the pool. */ void dump(); + private: struct Entry { Entry() {} @@ -133,36 +134,29 @@ private: : layer(layer) , width(layer->texture.width()) , height(layer->texture.height()) - , wideColorGamut(layer->wideColorGamut) { - } + , wideColorGamut(layer->wideColorGamut) {} static int compare(const Entry& lhs, const Entry& rhs); - bool operator==(const Entry& other) const { - return compare(*this, other) == 0; - } + bool operator==(const Entry& other) const { return compare(*this, other) == 0; } - bool operator!=(const Entry& other) const { - return compare(*this, other) != 0; - } + bool operator!=(const Entry& other) const { return compare(*this, other) != 0; } - bool operator<(const Entry& other) const { - return Entry::compare(*this, other) < 0; - } + bool operator<(const Entry& other) const { return Entry::compare(*this, other) < 0; } OffscreenBuffer* layer = nullptr; uint32_t width = 0; uint32_t height = 0; bool wideColorGamut = false; - }; // struct Entry + }; // struct Entry std::multiset<Entry> mPool; uint32_t mSize = 0; uint32_t mMaxSize; -}; // class OffscreenBufferCache +}; // class OffscreenBufferCache -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android -#endif // ANDROID_HWUI_OFFSCREEN_BUFFER_POOL_H +#endif // ANDROID_HWUI_OFFSCREEN_BUFFER_POOL_H diff --git a/libs/hwui/renderstate/PixelBufferState.cpp b/libs/hwui/renderstate/PixelBufferState.cpp index c23af5216592..3a6efb833c47 100644 --- a/libs/hwui/renderstate/PixelBufferState.cpp +++ b/libs/hwui/renderstate/PixelBufferState.cpp @@ -18,9 +18,7 @@ namespace android { namespace uirenderer { -PixelBufferState::PixelBufferState() - : mCurrentPixelBuffer(0) { -} +PixelBufferState::PixelBufferState() : mCurrentPixelBuffer(0) {} bool PixelBufferState::bind(GLuint buffer) { if (mCurrentPixelBuffer != buffer) { @@ -42,4 +40,3 @@ bool PixelBufferState::unbind() { } /* namespace uirenderer */ } /* namespace android */ - diff --git a/libs/hwui/renderstate/PixelBufferState.h b/libs/hwui/renderstate/PixelBufferState.h index 8dab21db176c..f7ae6c575f6a 100644 --- a/libs/hwui/renderstate/PixelBufferState.h +++ b/libs/hwui/renderstate/PixelBufferState.h @@ -22,10 +22,11 @@ namespace android { namespace uirenderer { class PixelBufferState { - friend class Caches; // TODO: move to RenderState + friend class Caches; // TODO: move to RenderState public: bool bind(GLuint buffer); bool unbind(); + private: PixelBufferState(); GLuint mCurrentPixelBuffer; @@ -34,4 +35,4 @@ private: } /* namespace uirenderer */ } /* namespace android */ -#endif // RENDERSTATE_PIXELBUFFERSTATE_H +#endif // RENDERSTATE_PIXELBUFFERSTATE_H diff --git a/libs/hwui/renderstate/RenderState.cpp b/libs/hwui/renderstate/RenderState.cpp index 5fc5cb275741..5e33353c3ac6 100644 --- a/libs/hwui/renderstate/RenderState.cpp +++ b/libs/hwui/renderstate/RenderState.cpp @@ -13,11 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#include "renderstate/RenderState.h" +#include <GpuMemoryTracker.h> #include "DeferredLayerUpdater.h" #include "GlLayer.h" #include "VkLayer.h" -#include <GpuMemoryTracker.h> -#include "renderstate/RenderState.h" #include "renderthread/CanvasContext.h" #include "renderthread/EglManager.h" @@ -31,21 +31,18 @@ namespace android { namespace uirenderer { RenderState::RenderState(renderthread::RenderThread& thread) - : mRenderThread(thread) - , mViewportWidth(0) - , mViewportHeight(0) - , mFramebuffer(0) { + : mRenderThread(thread), mViewportWidth(0), mViewportHeight(0), mFramebuffer(0) { mThreadId = pthread_self(); } RenderState::~RenderState() { LOG_ALWAYS_FATAL_IF(mBlend || mMeshState || mScissor || mStencil, - "State object lifecycle not managed correctly"); + "State object lifecycle not managed correctly"); } void RenderState::onGLContextCreated() { LOG_ALWAYS_FATAL_IF(mBlend || mMeshState || mScissor || mStencil, - "State object lifecycle not managed correctly"); + "State object lifecycle not managed correctly"); GpuMemoryTracker::onGpuContextCreated(); mBlend = new Blend(); @@ -67,7 +64,7 @@ void RenderState::onGLContextCreated() { static void layerLostGlContext(Layer* layer) { LOG_ALWAYS_FATAL_IF(layer->getApi() != Layer::Api::OpenGL, - "layerLostGlContext on non GL layer"); + "layerLostGlContext on non GL layer"); static_cast<GlLayer*>(layer)->onGlContextLost(); } @@ -94,7 +91,7 @@ void RenderState::onGLContextDestroyed() { void RenderState::onVkContextCreated() { LOG_ALWAYS_FATAL_IF(mBlend || mMeshState || mScissor || mStencil, - "State object lifecycle not managed correctly"); + "State object lifecycle not managed correctly"); GpuMemoryTracker::onGpuContextCreated(); } @@ -105,8 +102,8 @@ static void layerDestroyedVkContext(Layer* layer) { } void RenderState::onVkContextDestroyed() { - mLayerPool->clear(); std::for_each(mActiveLayers.begin(), mActiveLayers.end(), layerDestroyedVkContext); + destroyLayersInUpdater(); GpuMemoryTracker::onGpuContextDestroyed(); } @@ -117,9 +114,9 @@ GrContext* RenderState::getGrContext() const { void RenderState::flush(Caches::FlushMode mode) { switch (mode) { case Caches::FlushMode::Full: - // fall through + // fall through case Caches::FlushMode::Moderate: - // fall through + // fall through case Caches::FlushMode::Layers: if (mLayerPool) mLayerPool->clear(); break; @@ -140,7 +137,6 @@ void RenderState::setViewport(GLsizei width, GLsizei height) { glViewport(0, 0, mViewportWidth, mViewportHeight); } - void RenderState::getViewport(GLsizei* outWidth, GLsizei* outHeight) { *outWidth = mViewportWidth; *outHeight = mViewportHeight; @@ -189,15 +185,13 @@ void RenderState::interruptForFunctorInvoke() { meshState().disableTexCoordsVertexArray(); debugOverdraw(false, false); // TODO: We need a way to know whether the functor is sRGB aware (b/32072673) - if (mCaches->extensions().hasLinearBlending() && - mCaches->extensions().hasSRGBWriteControl()) { + if (mCaches->extensions().hasLinearBlending() && mCaches->extensions().hasSRGBWriteControl()) { glDisable(GL_FRAMEBUFFER_SRGB_EXT); } } void RenderState::resumeFromFunctorInvoke() { - if (mCaches->extensions().hasLinearBlending() && - mCaches->extensions().hasSRGBWriteControl()) { + if (mCaches->extensions().hasLinearBlending() && mCaches->extensions().hasSRGBWriteControl()) { glEnable(GL_FRAMEBUFFER_SRGB_EXT); } @@ -236,25 +230,11 @@ void RenderState::destroyLayersInUpdater() { std::for_each(mActiveLayerUpdaters.begin(), mActiveLayerUpdaters.end(), destroyLayerInUpdater); } -class DecStrongTask : public renderthread::RenderTask { -public: - explicit DecStrongTask(VirtualLightRefBase* object) : mObject(object) {} - - virtual void run() override { - mObject->decStrong(nullptr); - mObject = nullptr; - delete this; - } - -private: - VirtualLightRefBase* mObject; -}; - void RenderState::postDecStrong(VirtualLightRefBase* object) { if (pthread_equal(mThreadId, pthread_self())) { object->decStrong(nullptr); } else { - mRenderThread.queue(new DecStrongTask(object)); + mRenderThread.queue().post([object]() { object->decStrong(nullptr); }); } } @@ -263,7 +243,7 @@ void RenderState::postDecStrong(VirtualLightRefBase* object) { /////////////////////////////////////////////////////////////////////////////// void RenderState::render(const Glop& glop, const Matrix4& orthoMatrix, - bool overrideDisableBlending) { + bool overrideDisableBlending) { const Glop::Mesh& mesh = glop.mesh; const Glop::Mesh::Vertices& vertices = mesh.vertices; const Glop::Mesh::Indices& indices = mesh.indices; @@ -280,21 +260,19 @@ void RenderState::render(const Glop& glop, const Matrix4& orthoMatrix, fill.program->setColor(fill.color); } - fill.program->set(orthoMatrix, - glop.transform.modelView, - glop.transform.meshTransform(), - glop.transform.transformFlags & TransformFlags::OffsetByFudgeFactor); + fill.program->set(orthoMatrix, glop.transform.modelView, glop.transform.meshTransform(), + glop.transform.transformFlags & TransformFlags::OffsetByFudgeFactor); // Color filter uniforms if (fill.filterMode == ProgramDescription::ColorFilterMode::Blend) { const FloatColor& color = fill.filter.color; - glUniform4f(mCaches->program().getUniform("colorBlend"), - color.r, color.g, color.b, color.a); + glUniform4f(mCaches->program().getUniform("colorBlend"), color.r, color.g, color.b, + color.a); } else if (fill.filterMode == ProgramDescription::ColorFilterMode::Matrix) { glUniformMatrix4fv(mCaches->program().getUniform("colorMatrix"), 1, GL_FALSE, - fill.filter.matrix.matrix); + fill.filter.matrix.matrix); glUniform4fv(mCaches->program().getUniform("colorMatrixVector"), 1, - fill.filter.matrix.vector); + fill.filter.matrix.vector); } // Round rect clipping uniforms @@ -309,15 +287,14 @@ void RenderState::render(const Glop& glop, const Matrix4& orthoMatrix, // Divide by the radius to simplify the calculations in the fragment shader // roundRectPos is also passed from vertex shader relative to top/left & radius glUniform4f(fill.program->getUniform("roundRectInnerRectLTWH"), - innerRect.left / roundedOutRadius, innerRect.top / roundedOutRadius, - (innerRect.right - innerRect.left) / roundedOutRadius, - (innerRect.bottom - innerRect.top) / roundedOutRadius); + innerRect.left / roundedOutRadius, innerRect.top / roundedOutRadius, + (innerRect.right - innerRect.left) / roundedOutRadius, + (innerRect.bottom - innerRect.top) / roundedOutRadius); - glUniformMatrix4fv(fill.program->getUniform("roundRectInvTransform"), - 1, GL_FALSE, &state->matrix.data[0]); + glUniformMatrix4fv(fill.program->getUniform("roundRectInvTransform"), 1, GL_FALSE, + &state->matrix.data[0]); - glUniform1f(fill.program->getUniform("roundRectRadius"), - roundedOutRadius); + glUniform1f(fill.program->getUniform("roundRectRadius"), roundedOutRadius); } GL_CHECKPOINT(MODERATE); @@ -347,8 +324,8 @@ void RenderState::render(const Glop& glop, const Matrix4& orthoMatrix, } if (texture.textureTransform) { - glUniformMatrix4fv(fill.program->getUniform("mainTextureTransform"), 1, - GL_FALSE, &texture.textureTransform->data[0]); + glUniformMatrix4fv(fill.program->getUniform("mainTextureTransform"), 1, GL_FALSE, + &texture.textureTransform->data[0]); } } @@ -363,12 +340,13 @@ void RenderState::render(const Glop& glop, const Matrix4& orthoMatrix, if (vertices.attribFlags & VertexAttribFlags::Color) { colorLocation = fill.program->getAttrib("colors"); glEnableVertexAttribArray(colorLocation); - glVertexAttribPointer(colorLocation, 4, GL_FLOAT, GL_FALSE, vertices.stride, vertices.color); + glVertexAttribPointer(colorLocation, 4, GL_FLOAT, GL_FALSE, vertices.stride, + vertices.color); } int alphaLocation = -1; if (vertices.attribFlags & VertexAttribFlags::Alpha) { // NOTE: alpha vertex position is computed assuming no VBO - const void* alphaCoords = ((const GLbyte*) vertices.position) + kVertexAlphaOffset; + const void* alphaCoords = ((const GLbyte*)vertices.position) + kVertexAlphaOffset; alphaLocation = fill.program->getAttrib("vtxAlpha"); glEnableVertexAttribArray(alphaLocation); glVertexAttribPointer(alphaLocation, 1, GL_FLOAT, GL_FALSE, vertices.stride, alphaCoords); @@ -377,8 +355,9 @@ void RenderState::render(const Glop& glop, const Matrix4& orthoMatrix, SkiaShader::apply(*mCaches, fill.skiaShaderData, mViewportWidth, mViewportHeight); GL_CHECKPOINT(MODERATE); - Texture* texture = (fill.skiaShaderData.skiaShaderType & kBitmap_SkiaShaderType) ? - fill.skiaShaderData.bitmapData.bitmapTexture : nullptr; + Texture* texture = (fill.skiaShaderData.skiaShaderType & kBitmap_SkiaShaderType) + ? fill.skiaShaderData.bitmapData.bitmapTexture + : nullptr; const AutoTexture autoCleanup(texture); // If we have a shader and a base texture, the base texture is assumed to be an alpha mask @@ -387,8 +366,8 @@ void RenderState::render(const Glop& glop, const Matrix4& orthoMatrix, if (colorSpaceTexture != nullptr) { if (colorSpaceTexture->hasColorSpaceConversion()) { const ColorSpaceConnector* connector = colorSpaceTexture->getColorSpaceConnector(); - glUniformMatrix3fv(fill.program->getUniform("colorSpaceMatrix"), 1, - GL_FALSE, connector->getTransform().asArray()); + glUniformMatrix3fv(fill.program->getUniform("colorSpaceMatrix"), 1, GL_FALSE, + connector->getTransform().asArray()); } TransferFunctionType transferFunction = colorSpaceTexture->getTransferFunctionType(); @@ -401,15 +380,15 @@ void RenderState::render(const Glop& glop, const Matrix4& orthoMatrix, break; case TransferFunctionType::Full: glUniform1fv(fill.program->getUniform("transferFunction"), 7, - reinterpret_cast<const float*>(&source.getTransferParameters().g)); + reinterpret_cast<const float*>(&source.getTransferParameters().g)); break; case TransferFunctionType::Limited: glUniform1fv(fill.program->getUniform("transferFunction"), 5, - reinterpret_cast<const float*>(&source.getTransferParameters().g)); + reinterpret_cast<const float*>(&source.getTransferParameters().g)); break; case TransferFunctionType::Gamma: glUniform1f(fill.program->getUniform("transferFunctionGamma"), - source.getTransferParameters().g); + source.getTransferParameters().g); break; } } @@ -435,16 +414,17 @@ void RenderState::render(const Glop& glop, const Matrix4& orthoMatrix, GLsizei elementsCount = mesh.elementCount; const GLbyte* vertexData = static_cast<const GLbyte*>(vertices.position); while (elementsCount > 0) { - GLsizei drawCount = std::min(elementsCount, (GLsizei) kMaxNumberOfQuads * 6); + GLsizei drawCount = std::min(elementsCount, (GLsizei)kMaxNumberOfQuads * 6); GLsizei vertexCount = (drawCount / 6) * 4; meshState().bindPositionVertexPointer(vertexData, vertices.stride); if (vertices.attribFlags & VertexAttribFlags::TextureCoord) { - meshState().bindTexCoordsVertexPointer( - vertexData + kMeshTextureOffset, vertices.stride); + meshState().bindTexCoordsVertexPointer(vertexData + kMeshTextureOffset, + vertices.stride); } if (mCaches->extensions().getMajorGlVersion() >= 3) { - glDrawRangeElements(mesh.primitiveMode, 0, vertexCount-1, drawCount, GL_UNSIGNED_SHORT, nullptr); + glDrawRangeElements(mesh.primitiveMode, 0, vertexCount - 1, drawCount, + GL_UNSIGNED_SHORT, nullptr); } else { glDrawElements(mesh.primitiveMode, drawCount, GL_UNSIGNED_SHORT, nullptr); } @@ -453,10 +433,13 @@ void RenderState::render(const Glop& glop, const Matrix4& orthoMatrix, } } else if (indices.bufferObject || indices.indices) { if (mCaches->extensions().getMajorGlVersion() >= 3) { - // use glDrawRangeElements to reduce CPU overhead (otherwise the driver has to determine the min/max index values) - glDrawRangeElements(mesh.primitiveMode, 0, mesh.vertexCount-1, mesh.elementCount, GL_UNSIGNED_SHORT, indices.indices); + // use glDrawRangeElements to reduce CPU overhead (otherwise the driver has to determine + // the min/max index values) + glDrawRangeElements(mesh.primitiveMode, 0, mesh.vertexCount - 1, mesh.elementCount, + GL_UNSIGNED_SHORT, indices.indices); } else { - glDrawElements(mesh.primitiveMode, mesh.elementCount, GL_UNSIGNED_SHORT, indices.indices); + glDrawElements(mesh.primitiveMode, mesh.elementCount, GL_UNSIGNED_SHORT, + indices.indices); } } else { glDrawArrays(mesh.primitiveMode, 0, mesh.elementCount); diff --git a/libs/hwui/renderstate/RenderState.h b/libs/hwui/renderstate/RenderState.h index 315fa2db6878..e033cf2c5046 100644 --- a/libs/hwui/renderstate/RenderState.h +++ b/libs/hwui/renderstate/RenderState.h @@ -26,14 +26,14 @@ #include "renderstate/Stencil.h" #include "utils/Macros.h" -#include <set> #include <GLES2/gl2.h> #include <GLES2/gl2ext.h> +#include <private/hwui/DrawGlInfo.h> #include <ui/Region.h> -#include <utils/Mutex.h> #include <utils/Functor.h> +#include <utils/Mutex.h> #include <utils/RefBase.h> -#include <private/hwui/DrawGlInfo.h> +#include <set> class GrContext; @@ -57,6 +57,7 @@ class RenderState { friend class renderthread::RenderThread; friend class Caches; friend class renderthread::CacheManager; + public: void onGLContextCreated(); void onGLContextDestroyed(); @@ -79,12 +80,8 @@ public: void debugOverdraw(bool enable, bool clear); - void registerLayer(Layer* layer) { - mActiveLayers.insert(layer); - } - void unregisterLayer(Layer* layer) { - mActiveLayers.erase(layer); - } + void registerLayer(Layer* layer) { mActiveLayers.insert(layer); } + void unregisterLayer(Layer* layer) { mActiveLayers.erase(layer); } void registerCanvasContext(renderthread::CanvasContext* context) { mRegisteredContexts.insert(context); @@ -127,7 +124,6 @@ private: explicit RenderState(renderthread::RenderThread& thread); ~RenderState(); - renderthread::RenderThread& mRenderThread; Caches* mCaches = nullptr; diff --git a/libs/hwui/renderstate/Scissor.cpp b/libs/hwui/renderstate/Scissor.cpp index 61dd8c3200a4..e37ed029542b 100644 --- a/libs/hwui/renderstate/Scissor.cpp +++ b/libs/hwui/renderstate/Scissor.cpp @@ -23,12 +23,7 @@ namespace android { namespace uirenderer { Scissor::Scissor() - : mEnabled(false) - , mScissorX(0) - , mScissorY(0) - , mScissorWidth(0) - , mScissorHeight(0) { -} + : mEnabled(false), mScissorX(0), mScissorY(0), mScissorWidth(0), mScissorHeight(0) {} bool Scissor::setEnabled(bool enabled) { if (mEnabled != enabled) { @@ -44,9 +39,8 @@ bool Scissor::setEnabled(bool enabled) { } bool Scissor::set(GLint x, GLint y, GLint width, GLint height) { - if (mEnabled && (x != mScissorX || y != mScissorY - || width != mScissorWidth || height != mScissorHeight)) { - + if (mEnabled && + (x != mScissorX || y != mScissorY || width != mScissorWidth || height != mScissorHeight)) { if (x < 0) { width += x; x = 0; @@ -80,10 +74,7 @@ void Scissor::set(int viewportHeight, const Rect& clip) { GLint width = std::max(0, ((int)clip.right) - x); GLint height = std::max(0, (viewportHeight - (int)clip.top) - y); - if (x != mScissorX - || y != mScissorY - || width != mScissorWidth - || height != mScissorHeight) { + if (x != mScissorX || y != mScissorY || width != mScissorWidth || height != mScissorHeight) { glScissor(x, y, width, height); mScissorX = x; @@ -104,10 +95,9 @@ void Scissor::invalidate() { } void Scissor::dump() { - ALOGD("Scissor: enabled %d, %d %d %d %d", - mEnabled, mScissorX, mScissorY, mScissorWidth, mScissorHeight); + ALOGD("Scissor: enabled %d, %d %d %d %d", mEnabled, mScissorX, mScissorY, mScissorWidth, + mScissorHeight); } } /* namespace uirenderer */ } /* namespace android */ - diff --git a/libs/hwui/renderstate/Scissor.h b/libs/hwui/renderstate/Scissor.h index f30224470059..2b04f4e1384a 100644 --- a/libs/hwui/renderstate/Scissor.h +++ b/libs/hwui/renderstate/Scissor.h @@ -26,6 +26,7 @@ class Rect; class Scissor { friend class RenderState; + public: bool setEnabled(bool enabled); bool set(GLint x, GLint y, GLint width, GLint height); @@ -33,6 +34,7 @@ public: void reset(); bool isEnabled() { return mEnabled; } void dump(); + private: Scissor(); void invalidate(); @@ -46,4 +48,4 @@ private: } /* namespace uirenderer */ } /* namespace android */ -#endif // RENDERSTATE_SCISSOR_H +#endif // RENDERSTATE_SCISSOR_H diff --git a/libs/hwui/renderstate/Stencil.cpp b/libs/hwui/renderstate/Stencil.cpp index f59442196af1..dc465fc7a6f8 100644 --- a/libs/hwui/renderstate/Stencil.cpp +++ b/libs/hwui/renderstate/Stencil.cpp @@ -136,5 +136,5 @@ void Stencil::dump() { ALOGD("Stencil: state %d", mState); } -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/renderstate/Stencil.h b/libs/hwui/renderstate/Stencil.h index 5f7d4056b51d..95f372344ee4 100644 --- a/libs/hwui/renderstate/Stencil.h +++ b/libs/hwui/renderstate/Stencil.h @@ -78,36 +78,26 @@ public: /** * Indicates whether either test or write is enabled. */ - bool isEnabled() { - return mState != StencilState::Disabled; - } + bool isEnabled() { return mState != StencilState::Disabled; } /** * Indicates whether testing only is enabled. */ - bool isTestEnabled() { - return mState == StencilState::Test; - } + bool isTestEnabled() { return mState == StencilState::Test; } - bool isWriteEnabled() { - return mState == StencilState::Write; - } + bool isWriteEnabled() { return mState == StencilState::Write; } void dump(); private: - enum class StencilState { - Disabled, - Test, - Write - }; + enum class StencilState { Disabled, Test, Write }; void enable(); StencilState mState = StencilState::Disabled; -}; // class Stencil +}; // class Stencil -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android -#endif // ANDROID_HWUI_STENCIL_H +#endif // ANDROID_HWUI_STENCIL_H diff --git a/libs/hwui/renderstate/TextureState.cpp b/libs/hwui/renderstate/TextureState.cpp index f9a1f8c38895..470b4f5de97f 100644 --- a/libs/hwui/renderstate/TextureState.cpp +++ b/libs/hwui/renderstate/TextureState.cpp @@ -19,9 +19,9 @@ #include "utils/TraceUtils.h" #include <GLES3/gl3.h> -#include <memory> -#include <SkCanvas.h> #include <SkBitmap.h> +#include <SkCanvas.h> +#include <memory> namespace android { namespace uirenderer { @@ -30,22 +30,16 @@ namespace uirenderer { static const int SHADOW_LUT_SIZE = 128; // Must define as many texture units as specified by kTextureUnitsCount -const GLenum kTextureUnits[] = { - GL_TEXTURE0, - GL_TEXTURE1, - GL_TEXTURE2, - GL_TEXTURE3 -}; - -TextureState::TextureState() - : mTextureUnit(0) { +const GLenum kTextureUnits[] = {GL_TEXTURE0, GL_TEXTURE1, GL_TEXTURE2, GL_TEXTURE3}; + +TextureState::TextureState() : mTextureUnit(0) { glActiveTexture(kTextureUnits[0]); resetBoundTextures(); GLint maxTextureUnits; glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxTextureUnits); LOG_ALWAYS_FATAL_IF(maxTextureUnits < kTextureUnitsCount, - "At least %d texture units are required!", kTextureUnitsCount); + "At least %d texture units are required!", kTextureUnitsCount); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); } @@ -86,8 +80,8 @@ void TextureState::constructTexture(Caches& caches) { void TextureState::activateTexture(GLuint textureUnit) { LOG_ALWAYS_FATAL_IF(textureUnit >= kTextureUnitsCount, - "Tried to use texture unit index %d, only %d exist", - textureUnit, kTextureUnitsCount); + "Tried to use texture unit index %d, only %d exist", textureUnit, + kTextureUnitsCount); if (mTextureUnit != textureUnit) { glActiveTexture(kTextureUnits[textureUnit]); mTextureUnit = textureUnit; @@ -151,4 +145,3 @@ void TextureState::unbindTexture(GLuint texture) { } /* namespace uirenderer */ } /* namespace android */ - diff --git a/libs/hwui/renderstate/TextureState.h b/libs/hwui/renderstate/TextureState.h index 7296fd39a705..f1996d431fa2 100644 --- a/libs/hwui/renderstate/TextureState.h +++ b/libs/hwui/renderstate/TextureState.h @@ -16,8 +16,8 @@ #ifndef RENDERSTATE_TEXTURESTATE_H #define RENDERSTATE_TEXTURESTATE_H -#include "Vertex.h" #include "Texture.h" +#include "Vertex.h" #include <GLES2/gl2.h> #include <GLES2/gl2ext.h> @@ -29,9 +29,8 @@ namespace uirenderer { class Texture; class TextureState { - friend class Caches; // TODO: move to RenderState + friend class Caches; // TODO: move to RenderState public: - void constructTexture(Caches& caches); /** @@ -96,4 +95,4 @@ private: } /* namespace uirenderer */ } /* namespace android */ -#endif // RENDERSTATE_BLEND_H +#endif // RENDERSTATE_BLEND_H diff --git a/libs/hwui/renderthread/CacheManager.cpp b/libs/hwui/renderthread/CacheManager.cpp index 55694d046c2f..907f2d2d398f 100644 --- a/libs/hwui/renderthread/CacheManager.cpp +++ b/libs/hwui/renderthread/CacheManager.cpp @@ -17,11 +17,14 @@ #include "CacheManager.h" #include "Layer.h" +#include "Properties.h" #include "RenderThread.h" +#include "pipeline/skia/ShaderCache.h" #include "renderstate/RenderState.h" -#include <gui/Surface.h> #include <GrContextOptions.h> +#include <SkExecutor.h> +#include <gui/Surface.h> #include <math.h> #include <set> @@ -41,19 +44,22 @@ namespace renderthread { #define FONT_CACHE_MIN_MB (0.5f) #define FONT_CACHE_MAX_MB (4.0f) -CacheManager::CacheManager(const DisplayInfo& display) - : mMaxSurfaceArea(display.w * display.h) { - mVectorDrawableAtlas = new skiapipeline::VectorDrawableAtlas(mMaxSurfaceArea/2, - skiapipeline::VectorDrawableAtlas::StorageMode::allowSharedSurface); +CacheManager::CacheManager(const DisplayInfo& display) : mMaxSurfaceArea(display.w * display.h) { + mVectorDrawableAtlas = new skiapipeline::VectorDrawableAtlas( + mMaxSurfaceArea / 2, + skiapipeline::VectorDrawableAtlas::StorageMode::disallowSharedSurface); + if (Properties::isSkiaEnabled()) { + skiapipeline::ShaderCache::get().initShaderDiskCache(); + } } -void CacheManager::reset(GrContext* context) { - if (context != mGrContext.get()) { +void CacheManager::reset(sk_sp<GrContext> context) { + if (context != mGrContext) { destroy(); } if (context) { - mGrContext = sk_ref_sp(context); + mGrContext = std::move(context); mGrContext->getResourceCacheLimits(&mMaxResources, nullptr); updateContextCacheSizes(); } @@ -62,7 +68,9 @@ void CacheManager::reset(GrContext* context) { void CacheManager::destroy() { // cleanup any caches here as the GrContext is about to go away... mGrContext.reset(nullptr); - mVectorDrawableAtlas = new skiapipeline::VectorDrawableAtlas(mMaxSurfaceArea/2); + mVectorDrawableAtlas = new skiapipeline::VectorDrawableAtlas( + mMaxSurfaceArea / 2, + skiapipeline::VectorDrawableAtlas::StorageMode::disallowSharedSurface); } void CacheManager::updateContextCacheSizes() { @@ -72,6 +80,29 @@ void CacheManager::updateContextCacheSizes() { mGrContext->setResourceCacheLimits(mMaxResources, mMaxResourceBytes); } +class CacheManager::SkiaTaskProcessor : public TaskProcessor<bool>, public SkExecutor { +public: + explicit SkiaTaskProcessor(TaskManager* taskManager) : TaskProcessor<bool>(taskManager) {} + + // This is really a Task<void> but that doesn't really work when Future<> + // expects to be able to get/set a value + struct SkiaTask : public Task<bool> { + std::function<void()> func; + }; + + virtual void add(std::function<void(void)> func) override { + sp<SkiaTask> task(new SkiaTask()); + task->func = func; + TaskProcessor<bool>::add(task); + } + + virtual void onProcess(const sp<Task<bool> >& task) override { + SkiaTask* t = static_cast<SkiaTask*>(task.get()); + t->func(); + task->setResult(true); + } +}; + void CacheManager::configureContext(GrContextOptions* contextOptions) { contextOptions->fAllowPathMaskCaching = true; @@ -94,6 +125,15 @@ void CacheManager::configureContext(GrContextOptions* contextOptions) { // Skia's implementation doesn't provide a mechanism to resize the font cache due to // the potential cost of recreating the glyphs. contextOptions->fGlyphCacheTextureMaximumBytes = fontCacheMB * 1024 * 1024; + + if (mTaskManager.canRunTasks()) { + if (!mTaskProcessor.get()) { + mTaskProcessor = new SkiaTaskProcessor(&mTaskManager); + } + contextOptions->fExecutor = mTaskProcessor.get(); + } + + contextOptions->fPersistentCache = &skiapipeline::ShaderCache::get(); } void CacheManager::trimMemory(TrimMemoryMode mode) { @@ -105,7 +145,7 @@ void CacheManager::trimMemory(TrimMemoryMode mode) { switch (mode) { case TrimMemoryMode::Complete: - mVectorDrawableAtlas = new skiapipeline::VectorDrawableAtlas(mMaxSurfaceArea/2); + mVectorDrawableAtlas = new skiapipeline::VectorDrawableAtlas(mMaxSurfaceArea / 2); mGrContext->freeGpuResources(); break; case TrimMemoryMode::UiHidden: @@ -143,8 +183,8 @@ void CacheManager::dumpMemoryUsage(String8& log, const RenderState* renderState) log.appendFormat("Caches:\n"); log.appendFormat(" Current / Maximum\n"); - log.appendFormat(" VectorDrawableAtlas %6.2f kB / %6.2f kB (entries = %zu)\n", - 0.0f, 0.0f, (size_t)0); + log.appendFormat(" VectorDrawableAtlas %6.2f kB / %6.2f kB (entries = %zu)\n", 0.0f, 0.0f, + (size_t)0); if (renderState) { if (renderState->mActiveLayers.size() > 0) { @@ -153,24 +193,21 @@ void CacheManager::dumpMemoryUsage(String8& log, const RenderState* renderState) size_t layerMemoryTotal = 0; for (std::set<Layer*>::iterator it = renderState->mActiveLayers.begin(); - it != renderState->mActiveLayers.end(); it++) { + it != renderState->mActiveLayers.end(); it++) { const Layer* layer = *it; const char* layerType = layer->getApi() == Layer::Api::OpenGL ? "GlLayer" : "VkLayer"; - log.appendFormat(" %s size %dx%d\n", layerType, - layer->getWidth(), layer->getHeight()); + log.appendFormat(" %s size %dx%d\n", layerType, layer->getWidth(), + layer->getHeight()); layerMemoryTotal += layer->getWidth() * layer->getHeight() * 4; } log.appendFormat(" Layers Total %6.2f kB (numLayers = %zu)\n", layerMemoryTotal / 1024.0f, renderState->mActiveLayers.size()); } - log.appendFormat("Total memory usage:\n"); - log.appendFormat(" %zu bytes, %.2f MB (%.2f MB is purgeable)\n", - bytesCached, bytesCached / 1024.0f / 1024.0f, + log.appendFormat(" %zu bytes, %.2f MB (%.2f MB is purgeable)\n", bytesCached, + bytesCached / 1024.0f / 1024.0f, mGrContext->getResourceCachePurgeableBytes() / 1024.0f / 1024.0f); - - } } /* namespace renderthread */ diff --git a/libs/hwui/renderthread/CacheManager.h b/libs/hwui/renderthread/CacheManager.h index 90362f33358d..7d733525194f 100644 --- a/libs/hwui/renderthread/CacheManager.h +++ b/libs/hwui/renderthread/CacheManager.h @@ -22,7 +22,10 @@ #include <ui/DisplayInfo.h> #include <utils/String8.h> #include <vector> + #include "pipeline/skia/VectorDrawableAtlas.h" +#include "thread/TaskManager.h" +#include "thread/TaskProcessor.h" namespace android { @@ -39,10 +42,7 @@ class RenderThread; class CacheManager { public: - enum class TrimMemoryMode { - Complete, - UiHidden - }; + enum class TrimMemoryMode { Complete, UiHidden }; void configureContext(GrContextOptions* context); void trimMemory(TrimMemoryMode mode); @@ -54,13 +54,14 @@ public: size_t getCacheSize() const { return mMaxResourceBytes; } size_t getBackgroundCacheSize() const { return mBackgroundResourceBytes; } + TaskManager* getTaskManager() { return &mTaskManager; } + private: friend class RenderThread; CacheManager(const DisplayInfo& display); - - void reset(GrContext* grContext); + void reset(sk_sp<GrContext> grContext); void destroy(); void updateContextCacheSizes(); @@ -77,6 +78,10 @@ private: }; sp<skiapipeline::VectorDrawableAtlas> mVectorDrawableAtlas; + + class SkiaTaskProcessor; + sp<SkiaTaskProcessor> mTaskProcessor; + TaskManager mTaskManager; }; } /* namespace renderthread */ @@ -84,4 +89,3 @@ private: } /* namespace android */ #endif /* CACHEMANAGER_H */ - diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index 5d7f5948b0ec..820789dd055d 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -14,35 +14,36 @@ * limitations under the License. */ -#include <GpuMemoryTracker.h> #include "CanvasContext.h" +#include <GpuMemoryTracker.h> #include "AnimationContext.h" #include "Caches.h" #include "EglManager.h" #include "Frame.h" #include "LayerUpdateQueue.h" +#include "OpenGLPipeline.h" #include "Properties.h" #include "RenderThread.h" #include "hwui/Canvas.h" -#include "renderstate/RenderState.h" -#include "renderstate/Stencil.h" -#include "protos/hwui.pb.h" -#include "OpenGLPipeline.h" #include "pipeline/skia/SkiaOpenGLPipeline.h" #include "pipeline/skia/SkiaPipeline.h" #include "pipeline/skia/SkiaVulkanPipeline.h" +#include "protos/hwui.pb.h" +#include "renderstate/RenderState.h" +#include "renderstate/Stencil.h" #include "utils/GLUtils.h" #include "utils/TimeUtils.h" +#include "../Properties.h" #include <cutils/properties.h> #include <google/protobuf/io/zero_copy_stream_impl.h> #include <private/hwui/DrawGlInfo.h> #include <strings.h> -#include <algorithm> #include <fcntl.h> #include <sys/stat.h> +#include <algorithm> #include <cstdlib> @@ -63,23 +64,22 @@ namespace android { namespace uirenderer { namespace renderthread { -CanvasContext* CanvasContext::create(RenderThread& thread, - bool translucent, RenderNode* rootRenderNode, IContextFactory* contextFactory) { - +CanvasContext* CanvasContext::create(RenderThread& thread, bool translucent, + RenderNode* rootRenderNode, IContextFactory* contextFactory) { auto renderType = Properties::getRenderPipelineType(); switch (renderType) { case RenderPipelineType::OpenGL: return new CanvasContext(thread, translucent, rootRenderNode, contextFactory, - std::make_unique<OpenGLPipeline>(thread)); + std::make_unique<OpenGLPipeline>(thread)); case RenderPipelineType::SkiaGL: return new CanvasContext(thread, translucent, rootRenderNode, contextFactory, - std::make_unique<skiapipeline::SkiaOpenGLPipeline>(thread)); + std::make_unique<skiapipeline::SkiaOpenGLPipeline>(thread)); case RenderPipelineType::SkiaVulkan: return new CanvasContext(thread, translucent, rootRenderNode, contextFactory, - std::make_unique<skiapipeline::SkiaVulkanPipeline>(thread)); + std::make_unique<skiapipeline::SkiaVulkanPipeline>(thread)); default: - LOG_ALWAYS_FATAL("canvas context type %d not supported", (int32_t) renderType); + LOG_ALWAYS_FATAL("canvas context type %d not supported", (int32_t)renderType); break; } return nullptr; @@ -96,7 +96,7 @@ void CanvasContext::destroyLayer(RenderNode* node) { skiapipeline::SkiaPipeline::destroyLayer(node); break; default: - LOG_ALWAYS_FATAL("canvas context type %d not supported", (int32_t) renderType); + LOG_ALWAYS_FATAL("canvas context type %d not supported", (int32_t)renderType); break; } } @@ -115,7 +115,7 @@ void CanvasContext::invokeFunctor(const RenderThread& thread, Functor* functor) skiapipeline::SkiaVulkanPipeline::invokeFunctor(thread, functor); break; default: - LOG_ALWAYS_FATAL("canvas context type %d not supported", (int32_t) renderType); + LOG_ALWAYS_FATAL("canvas context type %d not supported", (int32_t)renderType); break; } } @@ -131,14 +131,14 @@ void CanvasContext::prepareToDraw(const RenderThread& thread, Bitmap* bitmap) { skiapipeline::SkiaPipeline::prepareToDraw(thread, bitmap); break; default: - LOG_ALWAYS_FATAL("canvas context type %d not supported", (int32_t) renderType); + LOG_ALWAYS_FATAL("canvas context type %d not supported", (int32_t)renderType); break; } } -CanvasContext::CanvasContext(RenderThread& thread, bool translucent, - RenderNode* rootRenderNode, IContextFactory* contextFactory, - std::unique_ptr<IRenderPipeline> renderPipeline) +CanvasContext::CanvasContext(RenderThread& thread, bool translucent, RenderNode* rootRenderNode, + IContextFactory* contextFactory, + std::unique_ptr<IRenderPipeline> renderPipeline) : mRenderThread(thread) , mOpaque(!translucent) , mAnimationContext(contextFactory->createAnimationContext(mRenderThread.timeLord())) @@ -170,7 +170,7 @@ void CanvasContext::addRenderNode(RenderNode* node, bool placeFront) { void CanvasContext::removeRenderNode(RenderNode* node) { node->clearRoot(); mRenderNodes.erase(std::remove(mRenderNodes.begin(), mRenderNodes.end(), node), - mRenderNodes.end()); + mRenderNodes.end()); } void CanvasContext::destroy() { @@ -181,21 +181,21 @@ void CanvasContext::destroy() { mAnimationContext->destroy(); } -void CanvasContext::setSurface(Surface* surface) { +void CanvasContext::setSurface(sp<Surface>&& surface) { ATRACE_CALL(); - mNativeSurface = surface; + mNativeSurface = std::move(surface); ColorMode colorMode = mWideColorGamut ? ColorMode::WideColorGamut : ColorMode::Srgb; - bool hasSurface = mRenderPipeline->setSurface(surface, mSwapBehavior, colorMode); + bool hasSurface = mRenderPipeline->setSurface(mNativeSurface.get(), mSwapBehavior, colorMode); mFrameNumber = -1; if (hasSurface) { - mHaveNewSurface = true; - mSwapHistory.clear(); + mHaveNewSurface = true; + mSwapHistory.clear(); } else { - mRenderThread.removeFrameCallback(this); + mRenderThread.removeFrameCallback(this); } } @@ -203,15 +203,7 @@ void CanvasContext::setSwapBehavior(SwapBehavior swapBehavior) { mSwapBehavior = swapBehavior; } -void CanvasContext::initialize(Surface* surface) { - setSurface(surface); -} - -void CanvasContext::updateSurface(Surface* surface) { - setSurface(surface); -} - -bool CanvasContext::pauseSurface(Surface* surface) { +bool CanvasContext::pauseSurface() { return mRenderThread.removeFrameCallback(this); } @@ -227,8 +219,7 @@ void CanvasContext::setStopped(bool stopped) { } } -void CanvasContext::setup(float lightRadius, - uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha) { +void CanvasContext::setup(float lightRadius, uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha) { mLightGeometry.radius = lightRadius; mLightInfo.ambientShadowAlpha = ambientShadowAlpha; mLightInfo.spotShadowAlpha = spotShadowAlpha; @@ -262,7 +253,7 @@ bool CanvasContext::makeCurrent() { return true; default: LOG_ALWAYS_FATAL("unexpected result %d from IRenderPipeline::makeCurrent", - (int32_t) result); + (int32_t)result); } return true; @@ -285,8 +276,7 @@ bool CanvasContext::isSwapChainStuffed() { // Was there a happy queue & dequeue time? If so, don't // consider it stuffed - if (swapA.dequeueDuration < SLOW_THRESHOLD - && swapA.queueDuration < SLOW_THRESHOLD) { + if (swapA.dequeueDuration < SLOW_THRESHOLD && swapA.queueDuration < SLOW_THRESHOLD) { return false; } @@ -301,8 +291,7 @@ bool CanvasContext::isSwapChainStuffed() { // Was there a happy queue & dequeue time? If so, don't // consider it stuffed - if (swapB.dequeueDuration < SLOW_THRESHOLD - && swapB.queueDuration < SLOW_THRESHOLD) { + if (swapB.dequeueDuration < SLOW_THRESHOLD && swapB.queueDuration < SLOW_THRESHOLD) { return false; } @@ -314,8 +303,8 @@ bool CanvasContext::isSwapChainStuffed() { return true; } -void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo, - int64_t syncQueued, RenderNode* target) { +void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo, int64_t syncQueued, + RenderNode* target) { mRenderThread.removeFrameCallback(this); // If the previous frame was dropped we don't need to hold onto it, so @@ -331,6 +320,7 @@ void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo, info.layerUpdateQueue = &mLayerUpdateQueue; mAnimationContext->startFrame(info.mode); + mRenderPipeline->onPrepareTree(); for (const sp<RenderNode>& node : mRenderNodes) { // Only the primary target node will be drawn full - all other nodes would get drawn in // real time mode. In case of a window, the primary node is the window content and the other @@ -365,8 +355,8 @@ void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo, // Already drew for this vsync pulse, UI draw request missed // the deadline for RT animations info.out.canDrawThisFrame = false; - } else if (vsyncDelta >= mRenderThread.timeLord().frameIntervalNanos() * 3 - || (latestVsync - mLastDropVsync) < 500_ms) { + } else if (vsyncDelta >= mRenderThread.timeLord().frameIntervalNanos() * 3 || + (latestVsync - mLastDropVsync) < 500_ms) { // It's been several frame intervals, assume the buffer queue is fine // or the last drop was too recent info.out.canDrawThisFrame = true; @@ -386,6 +376,9 @@ void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo, } if (info.out.hasAnimations || !info.out.canDrawThisFrame) { + if (CC_UNLIKELY(!Properties::enableRTAnimations)) { + info.out.requiresUiRedraw = true; + } if (!info.out.requiresUiRedraw) { // If animationsNeedsRedraw is set don't bother posting for an RT anim // as we will just end up fighting the UI thread. @@ -409,10 +402,10 @@ void CanvasContext::draw() { mDamageAccumulator.finish(&dirty); // TODO: Re-enable after figuring out cause of b/22592975 -// if (dirty.isEmpty() && Properties::skipEmptyFrames) { -// mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame); -// return; -// } + // if (dirty.isEmpty() && Properties::skipEmptyFrames) { + // mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame); + // return; + // } mCurrentFrameInfo->markIssueDrawCommandsStart(); @@ -421,18 +414,19 @@ void CanvasContext::draw() { SkRect windowDirty = computeDirtyRect(frame, &dirty); bool drew = mRenderPipeline->draw(frame, windowDirty, dirty, mLightGeometry, &mLayerUpdateQueue, - mContentDrawBounds, mOpaque, mWideColorGamut, mLightInfo, mRenderNodes, &(profiler())); + mContentDrawBounds, mOpaque, mWideColorGamut, mLightInfo, + mRenderNodes, &(profiler())); waitOnFences(); bool requireSwap = false; - bool didSwap = mRenderPipeline->swapBuffers(frame, drew, windowDirty, mCurrentFrameInfo, - &requireSwap); + bool didSwap = + mRenderPipeline->swapBuffers(frame, drew, windowDirty, mCurrentFrameInfo, &requireSwap); mIsDirty = false; if (requireSwap) { - if (!didSwap) { //some error happened + if (!didSwap) { // some error happened setSurface(nullptr); } SwapHistory& swap = mSwapHistory.next(); @@ -456,10 +450,8 @@ void CanvasContext::draw() { swap.dequeueDuration = 0; swap.queueDuration = 0; } - mCurrentFrameInfo->set(FrameInfoIndex::DequeueBufferDuration) - = swap.dequeueDuration; - mCurrentFrameInfo->set(FrameInfoIndex::QueueBufferDuration) - = swap.queueDuration; + mCurrentFrameInfo->set(FrameInfoIndex::DequeueBufferDuration) = swap.dequeueDuration; + mCurrentFrameInfo->set(FrameInfoIndex::QueueBufferDuration) = swap.queueDuration; mHaveNewSurface = false; mFrameNumber = -1; } else { @@ -471,9 +463,9 @@ void CanvasContext::draw() { mCurrentFrameInfo->markFrameCompleted(); #if LOG_FRAMETIME_MMA - float thisFrame = mCurrentFrameInfo->duration( - FrameInfoIndex::IssueDrawCommandsStart, - FrameInfoIndex::FrameCompleted) / NANOS_PER_MILLIS_F; + float thisFrame = mCurrentFrameInfo->duration(FrameInfoIndex::IssueDrawCommandsStart, + FrameInfoIndex::FrameCompleted) / + NANOS_PER_MILLIS_F; if (sFrameCount) { sBenchMma = ((9 * sBenchMma) + thisFrame) / 10; } else { @@ -498,7 +490,6 @@ void CanvasContext::draw() { caches.fontRenderer.getFontRenderer().historyTracker().frameCompleted(); } #endif - } // Called by choreographer to do an RT-driven animation @@ -512,9 +503,7 @@ void CanvasContext::prepareAndDraw(RenderNode* node) { nsecs_t vsync = mRenderThread.timeLord().computeFrameTimeNanos(); int64_t frameInfo[UI_THREAD_FRAME_INFO_SIZE]; - UiFrameInfoBuilder(frameInfo) - .addFlag(FrameInfoFlags::RTAnimation) - .setVsync(vsync, vsync); + UiFrameInfoBuilder(frameInfo).addFlag(FrameInfoFlags::RTAnimation).setVsync(vsync, vsync); TreeInfo info(TreeInfo::MODE_RT_ONLY, *this); prepareTree(info, frameInfo, systemTime(CLOCK_MONOTONIC), node); @@ -536,7 +525,7 @@ void CanvasContext::freePrefetchedLayers() { if (mPrefetchedLayers.size()) { for (auto& node : mPrefetchedLayers) { ALOGW("Incorrectly called buildLayer on View: %s, destroying layer...", - node->getName()); + node->getName()); node->destroyLayers(); node->decStrong(nullptr); } @@ -562,8 +551,8 @@ void CanvasContext::buildLayer(RenderNode* node) { // purposes when the frame is actually drawn node->setPropertyFieldsDirty(RenderNode::GENERIC); - mRenderPipeline->renderLayers(mLightGeometry, &mLayerUpdateQueue, - mOpaque, mWideColorGamut, mLightInfo); + mRenderPipeline->renderLayers(mLightGeometry, &mLayerUpdateQueue, mOpaque, mWideColorGamut, + mLightInfo); node->incStrong(nullptr); mPrefetchedLayers.insert(node); @@ -614,7 +603,7 @@ void CanvasContext::trimMemory(RenderThread& thread, int level) { break; } default: - LOG_ALWAYS_FATAL("canvas context type %d not supported", (int32_t) renderType); + LOG_ALWAYS_FATAL("canvas context type %d not supported", (int32_t)renderType); break; } } @@ -641,7 +630,7 @@ void CanvasContext::serializeDisplayListTree() { using namespace google::protobuf::io; char package[128]; // Check whether tracing is enabled for this process. - FILE * file = fopen("/proc/self/cmdline", "r"); + FILE* file = fopen("/proc/self/cmdline", "r"); if (file) { if (!fgets(package, 128, file)) { ALOGE("Error reading cmdline: %s (%d)", strerror(errno), errno); @@ -650,8 +639,7 @@ void CanvasContext::serializeDisplayListTree() { } fclose(file); } else { - ALOGE("Error opening /proc/self/cmdline: %s (%d)", strerror(errno), - errno); + ALOGE("Error opening /proc/self/cmdline: %s (%d)", strerror(errno), errno); return; } char path[1024]; @@ -682,8 +670,7 @@ void CanvasContext::waitOnFences() { class CanvasContext::FuncTaskProcessor : public TaskProcessor<bool> { public: - explicit FuncTaskProcessor(TaskManager* taskManager) - : TaskProcessor<bool>(taskManager) {} + explicit FuncTaskProcessor(TaskManager* taskManager) : TaskProcessor<bool>(taskManager) {} virtual void onProcess(const sp<Task<bool> >& task) override { FuncTask* t = static_cast<FuncTask*>(task.get()); @@ -721,8 +708,8 @@ SkRect CanvasContext::computeDirtyRect(const Frame& frame, SkRect* dirty) { dirty->setEmpty(); } else { if (!dirty->isEmpty() && !dirty->intersect(0, 0, frame.width(), frame.height())) { - ALOGW("Dirty " RECT_STRING " doesn't intersect with 0 0 %d %d ?", - SK_RECT_ARGS(*dirty), frame.width(), frame.height()); + ALOGW("Dirty " RECT_STRING " doesn't intersect with 0 0 %d %d ?", SK_RECT_ARGS(*dirty), + frame.width(), frame.height()); dirty->setEmpty(); } profiler().unionDirty(dirty); @@ -742,7 +729,7 @@ SkRect CanvasContext::computeDirtyRect(const Frame& frame, SkRect* dirty) { // last frame so there's nothing to union() against // Therefore we only care about the > 1 case. if (frame.bufferAge() > 1) { - if (frame.bufferAge() > (int) mSwapHistory.size()) { + if (frame.bufferAge() > (int)mSwapHistory.size()) { // We don't have enough history to handle this old of a buffer // Just do a full-draw dirty->set(0, 0, frame.width(), frame.height()); @@ -751,7 +738,7 @@ SkRect CanvasContext::computeDirtyRect(const Frame& frame, SkRect* dirty) { // to the damage history (happens below) // So we need to damage for (int i = mSwapHistory.size() - 1; - i > ((int) mSwapHistory.size()) - frame.bufferAge(); i--) { + i > ((int)mSwapHistory.size()) - frame.bufferAge(); i--) { dirty->join(mSwapHistory[i].damage); } } diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h index 4a5b2c72b02a..d80a24737a7a 100644 --- a/libs/hwui/renderthread/CanvasContext.h +++ b/libs/hwui/renderthread/CanvasContext.h @@ -27,17 +27,17 @@ #include "IRenderPipeline.h" #include "LayerUpdateQueue.h" #include "RenderNode.h" -#include "thread/Task.h" -#include "thread/TaskProcessor.h" #include "renderthread/RenderTask.h" #include "renderthread/RenderThread.h" +#include "thread/Task.h" +#include "thread/TaskProcessor.h" -#include <cutils/compiler.h> #include <EGL/egl.h> #include <SkBitmap.h> #include <SkRect.h> -#include <utils/Functor.h> +#include <cutils/compiler.h> #include <gui/Surface.h> +#include <utils/Functor.h> #include <functional> #include <set> @@ -63,8 +63,8 @@ class Frame; // TODO: Rename to Renderer or some other per-window, top-level manager class CanvasContext : public IFrameCallback { public: - static CanvasContext* create(RenderThread& thread, bool translucent, - RenderNode* rootRenderNode, IContextFactory* contextFactory); + static CanvasContext* create(RenderThread& thread, bool translucent, RenderNode* rootRenderNode, + IContextFactory* contextFactory); virtual ~CanvasContext(); /** @@ -89,9 +89,7 @@ public: bool pinImages(std::vector<SkImage*>& mutableImages) { return mRenderPipeline->pinImages(mutableImages); } - bool pinImages(LsaVector<sk_sp<Bitmap>>& images) { - return mRenderPipeline->pinImages(images); - } + bool pinImages(LsaVector<sk_sp<Bitmap>>& images) { return mRenderPipeline->pinImages(images); } /** * Unpin any image that had be previously pinned to the GPU cache @@ -117,20 +115,17 @@ public: // Won't take effect until next EGLSurface creation void setSwapBehavior(SwapBehavior swapBehavior); - void initialize(Surface* surface); - void updateSurface(Surface* surface); - bool pauseSurface(Surface* surface); + void setSurface(sp<Surface>&& surface); + bool pauseSurface(); void setStopped(bool stopped); bool hasSurface() { return mNativeSurface.get(); } - void setup(float lightRadius, - uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha); + void setup(float lightRadius, uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha); void setLightCenter(const Vector3& lightCenter); void setOpaque(bool opaque); void setWideGamut(bool wideGamut); bool makeCurrent(); - void prepareTree(TreeInfo& info, int64_t* uiFrameInfo, - int64_t syncQueued, RenderNode* target); + void prepareTree(TreeInfo& info, int64_t* uiFrameInfo, int64_t syncQueued, RenderNode* target); void draw(); void destroy(); @@ -162,13 +157,9 @@ public: void addRenderNode(RenderNode* node, bool placeFront); void removeRenderNode(RenderNode* node); - void setContentDrawBounds(const Rect& bounds) { - mContentDrawBounds = bounds; - } + void setContentDrawBounds(const Rect& bounds) { mContentDrawBounds = bounds; } - RenderState& getRenderState() { - return mRenderThread.renderState(); - } + RenderState& getRenderState() { return mRenderThread.renderState(); } void addFrameMetricsObserver(FrameMetricsObserver* observer) { if (mFrameMetricsReporter.get() == nullptr) { @@ -198,15 +189,13 @@ public: private: CanvasContext(RenderThread& thread, bool translucent, RenderNode* rootRenderNode, - IContextFactory* contextFactory, std::unique_ptr<IRenderPipeline> renderPipeline); + IContextFactory* contextFactory, std::unique_ptr<IRenderPipeline> renderPipeline); friend class RegisterFrameCallbackTask; // TODO: Replace with something better for layer & other GL object // lifecycle tracking friend class android::uirenderer::RenderState; - void setSurface(Surface* window); - void freePrefetchedLayers(); bool isSwapChainStuffed(); @@ -242,14 +231,14 @@ private: bool mOpaque; bool mWideColorGamut = false; BakedOpRenderer::LightInfo mLightInfo; - FrameBuilder::LightGeometry mLightGeometry = { {0, 0, 0}, 0 }; + FrameBuilder::LightGeometry mLightGeometry = {{0, 0, 0}, 0}; bool mHaveNewSurface = false; DamageAccumulator mDamageAccumulator; LayerUpdateQueue mLayerUpdateQueue; std::unique_ptr<AnimationContext> mAnimationContext; - std::vector< sp<RenderNode> > mRenderNodes; + std::vector<sp<RenderNode>> mRenderNodes; FrameInfo* mCurrentFrameInfo = nullptr; std::string mName; @@ -269,8 +258,8 @@ private: }; class FuncTaskProcessor; - std::vector< sp<FuncTask> > mFrameFences; - sp<TaskProcessor<bool> > mFrameWorkProcessor; + std::vector<sp<FuncTask>> mFrameFences; + sp<TaskProcessor<bool>> mFrameWorkProcessor; std::unique_ptr<IRenderPipeline> mRenderPipeline; }; diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp index a097272df359..8372331c0352 100644 --- a/libs/hwui/renderthread/DrawFrameTask.cpp +++ b/libs/hwui/renderthread/DrawFrameTask.cpp @@ -33,21 +33,20 @@ DrawFrameTask::DrawFrameTask() : mRenderThread(nullptr) , mContext(nullptr) , mContentDrawBounds(0, 0, 0, 0) - , mSyncResult(SyncResult::OK) { -} + , mSyncResult(SyncResult::OK) {} -DrawFrameTask::~DrawFrameTask() { -} +DrawFrameTask::~DrawFrameTask() {} void DrawFrameTask::setContext(RenderThread* thread, CanvasContext* context, - RenderNode* targetNode) { + RenderNode* targetNode) { mRenderThread = thread; mContext = context; mTargetNode = targetNode; } void DrawFrameTask::pushLayerUpdate(DeferredLayerUpdater* layer) { - LOG_ALWAYS_FATAL_IF(!mContext, "Lifecycle violation, there's no context to pushLayerUpdate with!"); + LOG_ALWAYS_FATAL_IF(!mContext, + "Lifecycle violation, there's no context to pushLayerUpdate with!"); for (size_t i = 0; i < mLayers.size(); i++) { if (mLayers[i].get() == layer) { @@ -78,7 +77,7 @@ int DrawFrameTask::drawFrame() { void DrawFrameTask::postAndWait() { AutoMutex _lock(mLock); - mRenderThread->queue(this); + mRenderThread->queue().post([this]() { run(); }); mSignal.wait(mLock); } diff --git a/libs/hwui/renderthread/DrawFrameTask.h b/libs/hwui/renderthread/DrawFrameTask.h index 83ecb98f548f..ea51ae4a42b7 100644 --- a/libs/hwui/renderthread/DrawFrameTask.h +++ b/libs/hwui/renderthread/DrawFrameTask.h @@ -24,8 +24,8 @@ #include "RenderTask.h" -#include "../Rect.h" #include "../FrameInfo.h" +#include "../Rect.h" #include "../TreeInfo.h" namespace android { @@ -55,7 +55,7 @@ enum { * tracked across many frames not just a single frame. * It is the sync-state task, and will kick off the post-sync draw */ -class DrawFrameTask : public RenderTask { +class DrawFrameTask { public: DrawFrameTask(); virtual ~DrawFrameTask(); @@ -72,7 +72,7 @@ public: int64_t* frameInfo() { return mFrameInfo; } - virtual void run() override; + void run(); private: void postAndWait(); @@ -90,7 +90,7 @@ private: /********************************************* * Single frame data *********************************************/ - std::vector< sp<DeferredLayerUpdater> > mLayers; + std::vector<sp<DeferredLayerUpdater> > mLayers; int mSyncResult; int64_t mSyncQueued; diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp index bd4708da562a..5b87e1013baf 100644 --- a/libs/hwui/renderthread/EglManager.cpp +++ b/libs/hwui/renderthread/EglManager.cpp @@ -18,17 +18,17 @@ #include <string> -#include "utils/StringUtils.h" #include <cutils/properties.h> #include <log/log.h> +#include "utils/StringUtils.h" #include "Caches.h" #include "DeviceInfo.h" #include "Frame.h" #include "Properties.h" #include "RenderThread.h" -#include "renderstate/RenderState.h" #include "Texture.h" +#include "renderstate/RenderState.h" #include <EGL/eglext.h> #include <GrContextOptions.h> @@ -47,7 +47,9 @@ namespace android { namespace uirenderer { namespace renderthread { -#define ERROR_CASE(x) case x: return #x; +#define ERROR_CASE(x) \ + case x: \ + return #x; static const char* egl_error_str(EGLint error) { switch (error) { ERROR_CASE(EGL_SUCCESS) @@ -65,8 +67,8 @@ static const char* egl_error_str(EGLint error) { ERROR_CASE(EGL_BAD_PARAMETER) ERROR_CASE(EGL_BAD_SURFACE) ERROR_CASE(EGL_CONTEXT_LOST) - default: - return "Unknown error"; + default: + return "Unknown error"; } } const char* EglManager::eglErrorString() { @@ -89,8 +91,7 @@ EglManager::EglManager(RenderThread& thread) , mEglConfigWideGamut(nullptr) , mEglContext(EGL_NO_CONTEXT) , mPBufferSurface(EGL_NO_SURFACE) - , mCurrentSurface(EGL_NO_SURFACE) { -} + , mCurrentSurface(EGL_NO_SURFACE) {} void EglManager::initialize() { if (hasEglContext()) return; @@ -98,12 +99,12 @@ void EglManager::initialize() { ATRACE_NAME("Creating EGLContext"); mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); - LOG_ALWAYS_FATAL_IF(mEglDisplay == EGL_NO_DISPLAY, - "Failed to get EGL_DEFAULT_DISPLAY! err=%s", eglErrorString()); + LOG_ALWAYS_FATAL_IF(mEglDisplay == EGL_NO_DISPLAY, "Failed to get EGL_DEFAULT_DISPLAY! err=%s", + eglErrorString()); EGLint major, minor; LOG_ALWAYS_FATAL_IF(eglInitialize(mEglDisplay, &major, &minor) == EGL_FALSE, - "Failed to initialize display %p! err=%s", mEglDisplay, eglErrorString()); + "Failed to initialize display %p! err=%s", mEglDisplay, eglErrorString()); ALOGI("Initialized EGL, version %d.%d", (int)major, (int)minor); @@ -138,26 +139,26 @@ void EglManager::initialize() { LOG_ALWAYS_FATAL_IF(!glInterface.get()); GrContextOptions options; - options.fGpuPathRenderers &= ~GrContextOptions::GpuPathRenderers::kDistanceField; + options.fDisableDistanceFieldPaths = true; mRenderThread.cacheManager().configureContext(&options); - mRenderThread.setGrContext(GrContext::Create(GrBackend::kOpenGL_GrBackend, - (GrBackendContext)glInterface.get(), options)); + sk_sp<GrContext> grContext(GrContext::MakeGL(std::move(glInterface), options)); + LOG_ALWAYS_FATAL_IF(!grContext.get()); + mRenderThread.setGrContext(grContext); } } void EglManager::initExtensions() { - auto extensions = StringUtils::split( - eglQueryString(mEglDisplay, EGL_EXTENSIONS)); + auto extensions = StringUtils::split(eglQueryString(mEglDisplay, EGL_EXTENSIONS)); // For our purposes we don't care if EGL_BUFFER_AGE is a result of // EGL_EXT_buffer_age or EGL_KHR_partial_update as our usage is covered // under EGL_KHR_partial_update and we don't need the expanded scope // that EGL_EXT_buffer_age provides. - EglExtensions.bufferAge = extensions.has("EGL_EXT_buffer_age") - || extensions.has("EGL_KHR_partial_update"); + EglExtensions.bufferAge = + extensions.has("EGL_EXT_buffer_age") || extensions.has("EGL_KHR_partial_update"); EglExtensions.setDamage = extensions.has("EGL_KHR_partial_update"); LOG_ALWAYS_FATAL_IF(!extensions.has("EGL_KHR_swap_buffers_with_damage"), - "Missing required extension EGL_KHR_swap_buffers_with_damage"); + "Missing required extension EGL_KHR_swap_buffers_with_damage"); EglExtensions.glColorSpace = extensions.has("EGL_KHR_gl_colorspace"); EglExtensions.noConfigContext = extensions.has("EGL_KHR_no_config_context"); @@ -175,30 +176,37 @@ bool EglManager::hasEglContext() { void EglManager::loadConfigs() { ALOGD("Swap behavior %d", static_cast<int>(mSwapBehavior)); - EGLint swapBehavior = (mSwapBehavior == SwapBehavior::Preserved) - ? EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0; - EGLint attribs[] = { - EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, - EGL_RED_SIZE, 8, - EGL_GREEN_SIZE, 8, - EGL_BLUE_SIZE, 8, - EGL_ALPHA_SIZE, 8, - EGL_DEPTH_SIZE, 0, - EGL_CONFIG_CAVEAT, EGL_NONE, - EGL_STENCIL_SIZE, Stencil::getStencilSize(), - EGL_SURFACE_TYPE, EGL_WINDOW_BIT | swapBehavior, - EGL_NONE - }; + EGLint swapBehavior = + (mSwapBehavior == SwapBehavior::Preserved) ? EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0; + EGLint attribs[] = {EGL_RENDERABLE_TYPE, + EGL_OPENGL_ES2_BIT, + EGL_RED_SIZE, + 8, + EGL_GREEN_SIZE, + 8, + EGL_BLUE_SIZE, + 8, + EGL_ALPHA_SIZE, + 8, + EGL_DEPTH_SIZE, + 0, + EGL_CONFIG_CAVEAT, + EGL_NONE, + EGL_STENCIL_SIZE, + Stencil::getStencilSize(), + EGL_SURFACE_TYPE, + EGL_WINDOW_BIT | swapBehavior, + EGL_NONE}; EGLint numConfigs = 1; - if (!eglChooseConfig(mEglDisplay, attribs, &mEglConfig, numConfigs, &numConfigs) - || numConfigs != 1) { + if (!eglChooseConfig(mEglDisplay, attribs, &mEglConfig, numConfigs, &numConfigs) || + numConfigs != 1) { if (mSwapBehavior == SwapBehavior::Preserved) { // Try again without dirty regions enabled ALOGW("Failed to choose config with EGL_SWAP_BEHAVIOR_PRESERVED, retrying without..."); mSwapBehavior = SwapBehavior::Discard; loadConfigs(); - return; // the call to loadConfigs() we just made picks the wide gamut config + return; // the call to loadConfigs() we just made picks the wide gamut config } else { // Failed to get a valid config LOG_ALWAYS_FATAL("Failed to choose config, error = %s", eglErrorString()); @@ -207,22 +215,30 @@ void EglManager::loadConfigs() { if (EglExtensions.pixelFormatFloat) { // If we reached this point, we have a valid swap behavior - EGLint attribs16F[] = { - EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, - EGL_COLOR_COMPONENT_TYPE_EXT, EGL_COLOR_COMPONENT_TYPE_FLOAT_EXT, - EGL_RED_SIZE, 16, - EGL_GREEN_SIZE, 16, - EGL_BLUE_SIZE, 16, - EGL_ALPHA_SIZE, 16, - EGL_DEPTH_SIZE, 0, - EGL_STENCIL_SIZE, Stencil::getStencilSize(), - EGL_SURFACE_TYPE, EGL_WINDOW_BIT | swapBehavior, - EGL_NONE - }; + EGLint attribs16F[] = {EGL_RENDERABLE_TYPE, + EGL_OPENGL_ES2_BIT, + EGL_COLOR_COMPONENT_TYPE_EXT, + EGL_COLOR_COMPONENT_TYPE_FLOAT_EXT, + EGL_RED_SIZE, + 16, + EGL_GREEN_SIZE, + 16, + EGL_BLUE_SIZE, + 16, + EGL_ALPHA_SIZE, + 16, + EGL_DEPTH_SIZE, + 0, + EGL_STENCIL_SIZE, + Stencil::getStencilSize(), + EGL_SURFACE_TYPE, + EGL_WINDOW_BIT | swapBehavior, + EGL_NONE}; numConfigs = 1; - if (!eglChooseConfig(mEglDisplay, attribs16F, &mEglConfigWideGamut, numConfigs, &numConfigs) - || numConfigs != 1) { + if (!eglChooseConfig(mEglDisplay, attribs16F, &mEglConfigWideGamut, numConfigs, + &numConfigs) || + numConfigs != 1) { ALOGE("Device claims wide gamut support, cannot find matching config, error = %s", eglErrorString()); EglExtensions.pixelFormatFloat = false; @@ -231,23 +247,20 @@ void EglManager::loadConfigs() { } void EglManager::createContext() { - EGLint attribs[] = { - EGL_CONTEXT_CLIENT_VERSION, GLES_VERSION, - EGL_NONE - }; - mEglContext = eglCreateContext(mEglDisplay, - EglExtensions.noConfigContext ? ((EGLConfig) nullptr) : mEglConfig, + EGLint attribs[] = {EGL_CONTEXT_CLIENT_VERSION, GLES_VERSION, EGL_NONE}; + mEglContext = eglCreateContext( + mEglDisplay, EglExtensions.noConfigContext ? ((EGLConfig) nullptr) : mEglConfig, EGL_NO_CONTEXT, attribs); - LOG_ALWAYS_FATAL_IF(mEglContext == EGL_NO_CONTEXT, - "Failed to create context, error = %s", eglErrorString()); + LOG_ALWAYS_FATAL_IF(mEglContext == EGL_NO_CONTEXT, "Failed to create context, error = %s", + eglErrorString()); } void EglManager::createPBufferSurface() { LOG_ALWAYS_FATAL_IF(mEglDisplay == EGL_NO_DISPLAY, - "usePBufferSurface() called on uninitialized GlobalContext!"); + "usePBufferSurface() called on uninitialized GlobalContext!"); if (mPBufferSurface == EGL_NO_SURFACE) { - EGLint attribs[] = { EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE }; + EGLint attribs[] = {EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE}; mPBufferSurface = eglCreatePbufferSurface(mEglDisplay, mEglConfig, attribs); } } @@ -255,8 +268,8 @@ void EglManager::createPBufferSurface() { EGLSurface EglManager::createSurface(EGLNativeWindowType window, bool wideColorGamut) { initialize(); - wideColorGamut = wideColorGamut && EglExtensions.glColorSpace && EglExtensions.scRGB - && EglExtensions.pixelFormatFloat && EglExtensions.noConfigContext; + wideColorGamut = wideColorGamut && EglExtensions.glColorSpace && EglExtensions.scRGB && + EglExtensions.pixelFormatFloat && EglExtensions.noConfigContext; // The color space we want to use depends on whether linear blending is turned // on and whether the app has requested wide color gamut rendering. When wide @@ -280,10 +293,7 @@ EGLSurface EglManager::createSurface(EGLNativeWindowType window, bool wideColorG // We insert to placeholders to set EGL_GL_COLORSPACE_KHR and its value. // According to section 3.4.1 of the EGL specification, the attributes // list is considered empty if the first entry is EGL_NONE - EGLint attribs[] = { - EGL_NONE, EGL_NONE, - EGL_NONE - }; + EGLint attribs[] = {EGL_NONE, EGL_NONE, EGL_NONE}; if (EglExtensions.glColorSpace) { attribs[0] = EGL_GL_COLORSPACE_KHR; @@ -302,16 +312,17 @@ EGLSurface EglManager::createSurface(EGLNativeWindowType window, bool wideColorG #endif } - EGLSurface surface = eglCreateWindowSurface(mEglDisplay, - wideColorGamut ? mEglConfigWideGamut : mEglConfig, window, attribs); + EGLSurface surface = eglCreateWindowSurface( + mEglDisplay, wideColorGamut ? mEglConfigWideGamut : mEglConfig, window, attribs); LOG_ALWAYS_FATAL_IF(surface == EGL_NO_SURFACE, - "Failed to create EGLSurface for window %p, eglErr = %s", - (void*) window, eglErrorString()); + "Failed to create EGLSurface for window %p, eglErr = %s", (void*)window, + eglErrorString()); if (mSwapBehavior != SwapBehavior::Preserved) { - LOG_ALWAYS_FATAL_IF(eglSurfaceAttrib(mEglDisplay, surface, EGL_SWAP_BEHAVIOR, EGL_BUFFER_DESTROYED) == EGL_FALSE, + LOG_ALWAYS_FATAL_IF(eglSurfaceAttrib(mEglDisplay, surface, EGL_SWAP_BEHAVIOR, + EGL_BUFFER_DESTROYED) == EGL_FALSE, "Failed to set swap behavior to destroyed for window %p, eglErr = %s", - (void*) window, eglErrorString()); + (void*)window, eglErrorString()); } return surface; @@ -353,11 +364,11 @@ bool EglManager::makeCurrent(EGLSurface surface, EGLint* errOut) { if (!eglMakeCurrent(mEglDisplay, surface, surface, mEglContext)) { if (errOut) { *errOut = eglGetError(); - ALOGW("Failed to make current on surface %p, error=%s", - (void*)surface, egl_error_str(*errOut)); + ALOGW("Failed to make current on surface %p, error=%s", (void*)surface, + egl_error_str(*errOut)); } else { - LOG_ALWAYS_FATAL("Failed to make current on surface %p, error=%s", - (void*)surface, eglErrorString()); + LOG_ALWAYS_FATAL("Failed to make current on surface %p, error=%s", (void*)surface, + eglErrorString()); } } mCurrentSurface = surface; @@ -369,21 +380,20 @@ bool EglManager::makeCurrent(EGLSurface surface, EGLint* errOut) { EGLint EglManager::queryBufferAge(EGLSurface surface) { switch (mSwapBehavior) { - case SwapBehavior::Discard: - return 0; - case SwapBehavior::Preserved: - return 1; - case SwapBehavior::BufferAge: - EGLint bufferAge; - eglQuerySurface(mEglDisplay, surface, EGL_BUFFER_AGE_EXT, &bufferAge); - return bufferAge; + case SwapBehavior::Discard: + return 0; + case SwapBehavior::Preserved: + return 1; + case SwapBehavior::BufferAge: + EGLint bufferAge; + eglQuerySurface(mEglDisplay, surface, EGL_BUFFER_AGE_EXT, &bufferAge); + return bufferAge; } return 0; } Frame EglManager::beginFrame(EGLSurface surface) { - LOG_ALWAYS_FATAL_IF(surface == EGL_NO_SURFACE, - "Tried to beginFrame on EGL_NO_SURFACE!"); + LOG_ALWAYS_FATAL_IF(surface == EGL_NO_SURFACE, "Tried to beginFrame on EGL_NO_SURFACE!"); makeCurrent(surface); Frame frame; frame.mSurface = surface; @@ -401,7 +411,7 @@ void EglManager::damageFrame(const Frame& frame, const SkRect& dirty) { frame.map(dirty, rects); if (!eglSetDamageRegionKHR(mEglDisplay, frame.mSurface, rects, 1)) { LOG_ALWAYS_FATAL("Failed to set damage region on surface %p, error=%s", - (void*)frame.mSurface, eglErrorString()); + (void*)frame.mSurface, eglErrorString()); } } #endif @@ -412,7 +422,6 @@ bool EglManager::damageRequiresSwap() { } bool EglManager::swapBuffers(const Frame& frame, const SkRect& screenDirty) { - if (CC_UNLIKELY(Properties::waitForGpuCompletion)) { ATRACE_NAME("Finishing GPU work"); fence(); @@ -420,8 +429,7 @@ bool EglManager::swapBuffers(const Frame& frame, const SkRect& screenDirty) { EGLint rects[4]; frame.map(screenDirty, rects); - eglSwapBuffersWithDamageKHR(mEglDisplay, frame.mSurface, rects, - screenDirty.isEmpty() ? 0 : 1); + eglSwapBuffersWithDamageKHR(mEglDisplay, frame.mSurface, rects, screenDirty.isEmpty() ? 0 : 1); EGLint err = eglGetError(); if (CC_LIKELY(err == EGL_SUCCESS)) { @@ -431,20 +439,18 @@ bool EglManager::swapBuffers(const Frame& frame, const SkRect& screenDirty) { // For some reason our surface was destroyed out from under us // This really shouldn't happen, but if it does we can recover easily // by just not trying to use the surface anymore - ALOGW("swapBuffers encountered EGL error %d on %p, halting rendering...", - err, frame.mSurface); + ALOGW("swapBuffers encountered EGL error %d on %p, halting rendering...", err, + frame.mSurface); return false; } - LOG_ALWAYS_FATAL("Encountered EGL error %d %s during rendering", - err, egl_error_str(err)); + LOG_ALWAYS_FATAL("Encountered EGL error %d %s during rendering", err, egl_error_str(err)); // Impossible to hit this, but the compiler doesn't know that return false; } void EglManager::fence() { EGLSyncKHR fence = eglCreateSyncKHR(mEglDisplay, EGL_SYNC_FENCE_KHR, NULL); - eglClientWaitSyncKHR(mEglDisplay, fence, - EGL_SYNC_FLUSH_COMMANDS_BIT_KHR, EGL_FOREVER_KHR); + eglClientWaitSyncKHR(mEglDisplay, fence, EGL_SYNC_FLUSH_COMMANDS_BIT_KHR, EGL_FOREVER_KHR); eglDestroySyncKHR(mEglDisplay, fence); } @@ -452,17 +458,17 @@ bool EglManager::setPreserveBuffer(EGLSurface surface, bool preserve) { if (mSwapBehavior != SwapBehavior::Preserved) return false; bool preserved = eglSurfaceAttrib(mEglDisplay, surface, EGL_SWAP_BEHAVIOR, - preserve ? EGL_BUFFER_PRESERVED : EGL_BUFFER_DESTROYED); + preserve ? EGL_BUFFER_PRESERVED : EGL_BUFFER_DESTROYED); if (!preserved) { - ALOGW("Failed to set EGL_SWAP_BEHAVIOR on surface %p, error=%s", - (void*) surface, eglErrorString()); + ALOGW("Failed to set EGL_SWAP_BEHAVIOR on surface %p, error=%s", (void*)surface, + eglErrorString()); // Maybe it's already set? EGLint swapBehavior; if (eglQuerySurface(mEglDisplay, surface, EGL_SWAP_BEHAVIOR, &swapBehavior)) { preserved = (swapBehavior == EGL_BUFFER_PRESERVED); } else { - ALOGW("Failed to query EGL_SWAP_BEHAVIOR on surface %p, error=%p", - (void*) surface, eglErrorString()); + ALOGW("Failed to query EGL_SWAP_BEHAVIOR on surface %p, error=%p", (void*)surface, + eglErrorString()); } } diff --git a/libs/hwui/renderthread/EglManager.h b/libs/hwui/renderthread/EglManager.h index 2982c23552c9..ef9effbf9953 100644 --- a/libs/hwui/renderthread/EglManager.h +++ b/libs/hwui/renderthread/EglManager.h @@ -16,9 +16,9 @@ #ifndef EGLMANAGER_H #define EGLMANAGER_H -#include <cutils/compiler.h> #include <EGL/egl.h> #include <SkRect.h> +#include <cutils/compiler.h> #include <ui/GraphicBuffer.h> #include <utils/StrongPointer.h> diff --git a/libs/hwui/renderthread/Frame.h b/libs/hwui/renderthread/Frame.h index 99996fe40626..d266faa4f7c9 100644 --- a/libs/hwui/renderthread/Frame.h +++ b/libs/hwui/renderthread/Frame.h @@ -19,7 +19,7 @@ #include <stdint.h> struct SkRect; -typedef void *EGLSurface; +typedef void* EGLSurface; namespace android { namespace uirenderer { @@ -28,9 +28,7 @@ namespace renderthread { class Frame { public: Frame(int32_t width, int32_t height, int32_t bufferAge) - : mWidth(width) - , mHeight(height) - , mBufferAge(bufferAge) { } + : mWidth(width), mHeight(height), mBufferAge(bufferAge) {} int32_t width() const { return mWidth; } int32_t height() const { return mHeight; } @@ -57,4 +55,3 @@ private: } /* namespace renderthread */ } /* namespace uirenderer */ } /* namespace android */ - diff --git a/libs/hwui/renderthread/IRenderPipeline.h b/libs/hwui/renderthread/IRenderPipeline.h index f9b6e384d211..246ab269b838 100644 --- a/libs/hwui/renderthread/IRenderPipeline.h +++ b/libs/hwui/renderthread/IRenderPipeline.h @@ -17,6 +17,7 @@ #pragma once #include "FrameInfoVisualizer.h" +#include "SwapBehavior.h" #include <SkRect.h> #include <utils/RefBase.h> @@ -33,16 +34,7 @@ class DeferredLayerUpdater; namespace renderthread { -enum class SwapBehavior { - kSwap_default, - kSwap_discardBuffer, -}; - -enum class MakeCurrentResult { - AlreadyCurrent, - Failed, - Succeeded -}; +enum class MakeCurrentResult { AlreadyCurrent, Failed, Succeeded }; enum class ColorMode { Srgb, @@ -57,14 +49,13 @@ public: virtual MakeCurrentResult makeCurrent() = 0; virtual Frame getFrame() = 0; virtual bool draw(const Frame& frame, const SkRect& screenDirty, const SkRect& dirty, - const FrameBuilder::LightGeometry& lightGeometry, - LayerUpdateQueue* layerUpdateQueue, - const Rect& contentDrawBounds, bool opaque, bool wideColorGamut, - const BakedOpRenderer::LightInfo& lightInfo, - const std::vector< sp<RenderNode> >& renderNodes, - FrameInfoVisualizer* profiler) = 0; + const FrameBuilder::LightGeometry& lightGeometry, + LayerUpdateQueue* layerUpdateQueue, const Rect& contentDrawBounds, + bool opaque, bool wideColorGamut, const BakedOpRenderer::LightInfo& lightInfo, + const std::vector<sp<RenderNode>>& renderNodes, + FrameInfoVisualizer* profiler) = 0; virtual bool swapBuffers(const Frame& frame, bool drew, const SkRect& screenDirty, - FrameInfo* currentFrameInfo, bool* requireSwap) = 0; + FrameInfo* currentFrameInfo, bool* requireSwap) = 0; virtual bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) = 0; virtual DeferredLayerUpdater* createTextureLayer() = 0; virtual bool setSurface(Surface* window, SwapBehavior swapBehavior, ColorMode colorMode) = 0; @@ -73,14 +64,15 @@ public: virtual bool isContextReady() = 0; virtual void onDestroyHardwareResources() = 0; virtual void renderLayers(const FrameBuilder::LightGeometry& lightGeometry, - LayerUpdateQueue* layerUpdateQueue, bool opaque, bool wideColorGamut, - const BakedOpRenderer::LightInfo& lightInfo) = 0; + LayerUpdateQueue* layerUpdateQueue, bool opaque, bool wideColorGamut, + const BakedOpRenderer::LightInfo& lightInfo) = 0; virtual TaskManager* getTaskManager() = 0; - virtual bool createOrUpdateLayer(RenderNode* node, - const DamageAccumulator& damageAccumulator, bool wideColorGamut) = 0; + virtual bool createOrUpdateLayer(RenderNode* node, const DamageAccumulator& damageAccumulator, + bool wideColorGamut) = 0; virtual bool pinImages(std::vector<SkImage*>& mutableImages) = 0; virtual bool pinImages(LsaVector<sk_sp<Bitmap>>& images) = 0; virtual void unpinImages() = 0; + virtual void onPrepareTree() = 0; virtual ~IRenderPipeline() {} }; diff --git a/libs/hwui/renderthread/OpenGLPipeline.cpp b/libs/hwui/renderthread/OpenGLPipeline.cpp index 7283eb123d6a..f3103fd0cbb4 100644 --- a/libs/hwui/renderthread/OpenGLPipeline.cpp +++ b/libs/hwui/renderthread/OpenGLPipeline.cpp @@ -20,9 +20,9 @@ #include "EglManager.h" #include "Frame.h" #include "GlLayer.h" +#include "OpenGLReadback.h" #include "ProfileRenderer.h" #include "renderstate/RenderState.h" -#include "OpenGLReadback.h" #include <cutils/properties.h> #include <strings.h> @@ -32,9 +32,7 @@ namespace uirenderer { namespace renderthread { OpenGLPipeline::OpenGLPipeline(RenderThread& thread) - : mEglManager(thread.eglManager()) - , mRenderThread(thread) { -} + : mEglManager(thread.eglManager()), mRenderThread(thread) {} MakeCurrentResult OpenGLPipeline::makeCurrent() { // TODO: Figure out why this workaround is needed, see b/13913604 @@ -51,23 +49,21 @@ MakeCurrentResult OpenGLPipeline::makeCurrent() { Frame OpenGLPipeline::getFrame() { LOG_ALWAYS_FATAL_IF(mEglSurface == EGL_NO_SURFACE, - "drawRenderNode called on a context with no surface!"); + "drawRenderNode called on a context with no surface!"); return mEglManager.beginFrame(mEglSurface); } bool OpenGLPipeline::draw(const Frame& frame, const SkRect& screenDirty, const SkRect& dirty, - const FrameBuilder::LightGeometry& lightGeometry, - LayerUpdateQueue* layerUpdateQueue, - const Rect& contentDrawBounds, bool opaque, bool wideColorGamut, - const BakedOpRenderer::LightInfo& lightInfo, - const std::vector< sp<RenderNode> >& renderNodes, - FrameInfoVisualizer* profiler) { - + const FrameBuilder::LightGeometry& lightGeometry, + LayerUpdateQueue* layerUpdateQueue, const Rect& contentDrawBounds, + bool opaque, bool wideColorGamut, + const BakedOpRenderer::LightInfo& lightInfo, + const std::vector<sp<RenderNode>>& renderNodes, + FrameInfoVisualizer* profiler) { mEglManager.damageFrame(frame, dirty); bool drew = false; - auto& caches = Caches::getInstance(); FrameBuilder frameBuilder(dirty, frame.width(), frame.height(), lightGeometry, caches); @@ -76,8 +72,8 @@ bool OpenGLPipeline::draw(const Frame& frame, const SkRect& screenDirty, const S frameBuilder.deferRenderNodeScene(renderNodes, contentDrawBounds); - BakedOpRenderer renderer(caches, mRenderThread.renderState(), - opaque, wideColorGamut, lightInfo); + BakedOpRenderer renderer(caches, mRenderThread.renderState(), opaque, wideColorGamut, + lightInfo); frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer); ProfileRenderer profileRenderer(renderer); profiler->draw(profileRenderer); @@ -100,8 +96,7 @@ bool OpenGLPipeline::draw(const Frame& frame, const SkRect& screenDirty, const S } bool OpenGLPipeline::swapBuffers(const Frame& frame, bool drew, const SkRect& screenDirty, - FrameInfo* currentFrameInfo, bool* requireSwap) { - + FrameInfo* currentFrameInfo, bool* requireSwap) { GL_CHECKPOINT(LOW); // Even if we decided to cancel the frame, from the perspective of jank @@ -123,13 +118,13 @@ bool OpenGLPipeline::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap layer->updateTexImage(); layer->apply(); return OpenGLReadbackImpl::copyLayerInto(mRenderThread, - static_cast<GlLayer&>(*layer->backingLayer()), bitmap); + static_cast<GlLayer&>(*layer->backingLayer()), bitmap); } static Layer* createLayer(RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight, - SkColorFilter* colorFilter, int alpha, SkBlendMode mode, bool blend) { - GlLayer* layer = new GlLayer(renderState, layerWidth, layerHeight, colorFilter, alpha, - mode, blend); + SkColorFilter* colorFilter, int alpha, SkBlendMode mode, bool blend) { + GlLayer* layer = + new GlLayer(renderState, layerWidth, layerHeight, colorFilter, alpha, mode, blend); Caches::getInstance().textureState().activateTexture(0); layer->generateTexture(); return layer; @@ -147,7 +142,6 @@ void OpenGLPipeline::onStop() { } bool OpenGLPipeline::setSurface(Surface* surface, SwapBehavior swapBehavior, ColorMode colorMode) { - if (mEglSurface != EGL_NO_SURFACE) { mEglManager.destroySurface(mEglSurface); mEglSurface = EGL_NO_SURFACE; @@ -184,14 +178,16 @@ void OpenGLPipeline::onDestroyHardwareResources() { } void OpenGLPipeline::renderLayers(const FrameBuilder::LightGeometry& lightGeometry, - LayerUpdateQueue* layerUpdateQueue, bool opaque, bool wideColorGamut, - const BakedOpRenderer::LightInfo& lightInfo) { - static const std::vector< sp<RenderNode> > emptyNodeList; + LayerUpdateQueue* layerUpdateQueue, bool opaque, + bool wideColorGamut, + const BakedOpRenderer::LightInfo& lightInfo) { + static const std::vector<sp<RenderNode>> emptyNodeList; auto& caches = Caches::getInstance(); FrameBuilder frameBuilder(*layerUpdateQueue, lightGeometry, caches); layerUpdateQueue->clear(); // TODO: Handle wide color gamut contexts - BakedOpRenderer renderer(caches, mRenderThread.renderState(), opaque, wideColorGamut, lightInfo); + BakedOpRenderer renderer(caches, mRenderThread.renderState(), opaque, wideColorGamut, + lightInfo); LOG_ALWAYS_FATAL_IF(renderer.didDraw(), "shouldn't draw in buildlayer case"); frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer); } @@ -205,13 +201,14 @@ static bool layerMatchesWH(OffscreenBuffer* layer, int width, int height) { } bool OpenGLPipeline::createOrUpdateLayer(RenderNode* node, - const DamageAccumulator& damageAccumulator, bool wideColorGamut) { + const DamageAccumulator& damageAccumulator, + bool wideColorGamut) { RenderState& renderState = mRenderThread.renderState(); OffscreenBufferPool& layerPool = renderState.layerPool(); bool transformUpdateNeeded = false; if (node->getLayer() == nullptr) { - node->setLayer(layerPool.get(renderState, - node->getWidth(), node->getHeight(), wideColorGamut)); + node->setLayer( + layerPool.get(renderState, node->getWidth(), node->getHeight(), wideColorGamut)); transformUpdateNeeded = true; } else if (!layerMatchesWH(node->getLayer(), node->getWidth(), node->getHeight())) { // TODO: remove now irrelevant, currently enqueued damage (respecting damage ordering) @@ -273,8 +270,7 @@ void OpenGLPipeline::invokeFunctor(const RenderThread& thread, Functor* functor) class AutoEglFence { public: - AutoEglFence(EGLDisplay display) - : mDisplay(display) { + AutoEglFence(EGLDisplay display) : mDisplay(display) { fence = eglCreateSyncKHR(mDisplay, EGL_SYNC_FENCE_KHR, NULL); } @@ -285,17 +281,17 @@ public: } EGLSyncKHR fence = EGL_NO_SYNC_KHR; + private: EGLDisplay mDisplay = EGL_NO_DISPLAY; }; class AutoEglImage { public: - AutoEglImage(EGLDisplay display, EGLClientBuffer clientBuffer) - : mDisplay(display) { - EGLint imageAttrs[] = { EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE }; - image = eglCreateImageKHR(display, EGL_NO_CONTEXT, - EGL_NATIVE_BUFFER_ANDROID, clientBuffer, imageAttrs); + AutoEglImage(EGLDisplay display, EGLClientBuffer clientBuffer) : mDisplay(display) { + EGLint imageAttrs[] = {EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE}; + image = eglCreateImageKHR(display, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, clientBuffer, + imageAttrs); } ~AutoEglImage() { @@ -305,21 +301,19 @@ public: } EGLImageKHR image = EGL_NO_IMAGE_KHR; + private: EGLDisplay mDisplay = EGL_NO_DISPLAY; }; class AutoGlTexture { public: - AutoGlTexture(uirenderer::Caches& caches) - : mCaches(caches) { + AutoGlTexture(uirenderer::Caches& caches) : mCaches(caches) { glGenTextures(1, &mTexture); caches.textureState().bindTexture(mTexture); } - ~AutoGlTexture() { - mCaches.textureState().deleteTexture(mTexture); - } + ~AutoGlTexture() { mCaches.textureState().deleteTexture(mTexture); } private: uirenderer::Caches& mCaches; @@ -327,18 +321,17 @@ private: }; static bool uploadBitmapToGraphicBuffer(uirenderer::Caches& caches, SkBitmap& bitmap, - GraphicBuffer& buffer, GLint format, GLint type) { + GraphicBuffer& buffer, GLint format, GLint type) { EGLDisplay display = eglGetCurrentDisplay(); - LOG_ALWAYS_FATAL_IF(display == EGL_NO_DISPLAY, - "Failed to get EGL_DEFAULT_DISPLAY! err=%s", - uirenderer::renderthread::EglManager::eglErrorString()); + LOG_ALWAYS_FATAL_IF(display == EGL_NO_DISPLAY, "Failed to get EGL_DEFAULT_DISPLAY! err=%s", + uirenderer::renderthread::EglManager::eglErrorString()); // We use an EGLImage to access the content of the GraphicBuffer // The EGL image is later bound to a 2D texture - EGLClientBuffer clientBuffer = (EGLClientBuffer) buffer.getNativeBuffer(); + EGLClientBuffer clientBuffer = (EGLClientBuffer)buffer.getNativeBuffer(); AutoEglImage autoImage(display, clientBuffer); if (autoImage.image == EGL_NO_IMAGE_KHR) { ALOGW("Could not create EGL image, err =%s", - uirenderer::renderthread::EglManager::eglErrorString()); + uirenderer::renderthread::EglManager::eglErrorString()); return false; } AutoGlTexture glTexture(caches); @@ -346,8 +339,8 @@ static bool uploadBitmapToGraphicBuffer(uirenderer::Caches& caches, SkBitmap& bi GL_CHECKPOINT(MODERATE); - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, bitmap.width(), bitmap.height(), - format, type, bitmap.getPixels()); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, bitmap.width(), bitmap.height(), format, type, + bitmap.getPixels()); GL_CHECKPOINT(MODERATE); @@ -362,7 +355,7 @@ static bool uploadBitmapToGraphicBuffer(uirenderer::Caches& caches, SkBitmap& bi // The flag EGL_SYNC_FLUSH_COMMANDS_BIT_KHR will trigger a // pipeline flush (similar to what a glFlush() would do.) EGLint waitStatus = eglClientWaitSyncKHR(display, autoFence.fence, - EGL_SYNC_FLUSH_COMMANDS_BIT_KHR, FENCE_TIMEOUT); + EGL_SYNC_FLUSH_COMMANDS_BIT_KHR, FENCE_TIMEOUT); if (waitStatus != EGL_CONDITION_SATISFIED_KHR) { LOG_ALWAYS_FATAL("Failed to wait for the fence %#x", eglGetError()); return false; @@ -373,24 +366,24 @@ static bool uploadBitmapToGraphicBuffer(uirenderer::Caches& caches, SkBitmap& bi // TODO: handle SRGB sanely static PixelFormat internalFormatToPixelFormat(GLint internalFormat) { switch (internalFormat) { - case GL_LUMINANCE: - return PIXEL_FORMAT_RGBA_8888; - case GL_SRGB8_ALPHA8: - return PIXEL_FORMAT_RGBA_8888; - case GL_RGBA: - return PIXEL_FORMAT_RGBA_8888; - case GL_RGB: - return PIXEL_FORMAT_RGB_565; - case GL_RGBA16F: - return PIXEL_FORMAT_RGBA_FP16; - default: - LOG_ALWAYS_FATAL("Unsupported bitmap colorType: %d", internalFormat); - return PIXEL_FORMAT_UNKNOWN; + case GL_LUMINANCE: + return PIXEL_FORMAT_RGBA_8888; + case GL_SRGB8_ALPHA8: + return PIXEL_FORMAT_RGBA_8888; + case GL_RGBA: + return PIXEL_FORMAT_RGBA_8888; + case GL_RGB: + return PIXEL_FORMAT_RGB_565; + case GL_RGBA16F: + return PIXEL_FORMAT_RGBA_FP16; + default: + LOG_ALWAYS_FATAL("Unsupported bitmap colorType: %d", internalFormat); + return PIXEL_FORMAT_UNKNOWN; } } sk_sp<Bitmap> OpenGLPipeline::allocateHardwareBitmap(RenderThread& renderThread, - SkBitmap& skBitmap) { + SkBitmap& skBitmap) { renderThread.eglManager().initialize(); uirenderer::Caches& caches = uirenderer::Caches::getInstance(); @@ -404,13 +397,14 @@ sk_sp<Bitmap> OpenGLPipeline::allocateHardwareBitmap(RenderThread& renderThread, bool hasLinearBlending = caches.extensions().hasLinearBlending(); GLint format, type, internalFormat; uirenderer::Texture::colorTypeToGlFormatAndType(caches, skBitmap.colorType(), - needSRGB && hasLinearBlending, &internalFormat, &format, &type); + needSRGB && hasLinearBlending, &internalFormat, + &format, &type); PixelFormat pixelFormat = internalFormatToPixelFormat(internalFormat); - sp<GraphicBuffer> buffer = new GraphicBuffer(info.width(), info.height(), pixelFormat, - GraphicBuffer::USAGE_HW_TEXTURE | - GraphicBuffer::USAGE_SW_WRITE_NEVER | - GraphicBuffer::USAGE_SW_READ_NEVER, + sp<GraphicBuffer> buffer = new GraphicBuffer( + info.width(), info.height(), pixelFormat, + GraphicBuffer::USAGE_HW_TEXTURE | GraphicBuffer::USAGE_SW_WRITE_NEVER | + GraphicBuffer::USAGE_SW_READ_NEVER, std::string("Bitmap::allocateHardwareBitmap pid [") + std::to_string(getpid()) + "]"); status_t error = buffer->initCheck(); @@ -420,8 +414,8 @@ sk_sp<Bitmap> OpenGLPipeline::allocateHardwareBitmap(RenderThread& renderThread, } SkBitmap bitmap; - if (CC_UNLIKELY(uirenderer::Texture::hasUnsupportedColorType(skBitmap.info(), - hasLinearBlending))) { + if (CC_UNLIKELY( + uirenderer::Texture::hasUnsupportedColorType(skBitmap.info(), hasLinearBlending))) { sk_sp<SkColorSpace> sRGB = SkColorSpace::MakeSRGB(); bitmap = uirenderer::Texture::uploadToN32(skBitmap, hasLinearBlending, std::move(sRGB)); } else { diff --git a/libs/hwui/renderthread/OpenGLPipeline.h b/libs/hwui/renderthread/OpenGLPipeline.h index 4ca19fb6245c..118007c6a46c 100644 --- a/libs/hwui/renderthread/OpenGLPipeline.h +++ b/libs/hwui/renderthread/OpenGLPipeline.h @@ -16,9 +16,9 @@ #pragma once -#include "CanvasContext.h" #include "BakedOpDispatcher.h" #include "BakedOpRenderer.h" +#include "CanvasContext.h" #include "FrameBuilder.h" #include "IRenderPipeline.h" @@ -34,14 +34,13 @@ public: MakeCurrentResult makeCurrent() override; Frame getFrame() override; bool draw(const Frame& frame, const SkRect& screenDirty, const SkRect& dirty, - const FrameBuilder::LightGeometry& lightGeometry, - LayerUpdateQueue* layerUpdateQueue, - const Rect& contentDrawBounds, bool opaque, bool wideColorGamut, - const BakedOpRenderer::LightInfo& lightInfo, - const std::vector< sp<RenderNode> >& renderNodes, - FrameInfoVisualizer* profiler) override; + const FrameBuilder::LightGeometry& lightGeometry, LayerUpdateQueue* layerUpdateQueue, + const Rect& contentDrawBounds, bool opaque, bool wideColorGamut, + const BakedOpRenderer::LightInfo& lightInfo, + const std::vector<sp<RenderNode>>& renderNodes, + FrameInfoVisualizer* profiler) override; bool swapBuffers(const Frame& frame, bool drew, const SkRect& screenDirty, - FrameInfo* currentFrameInfo, bool* requireSwap) override; + FrameInfo* currentFrameInfo, bool* requireSwap) override; bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) override; DeferredLayerUpdater* createTextureLayer() override; bool setSurface(Surface* window, SwapBehavior swapBehavior, ColorMode colorMode) override; @@ -50,19 +49,19 @@ public: bool isContextReady() override; void onDestroyHardwareResources() override; void renderLayers(const FrameBuilder::LightGeometry& lightGeometry, - LayerUpdateQueue* layerUpdateQueue, bool opaque, bool wideColorGamut, - const BakedOpRenderer::LightInfo& lightInfo) override; + LayerUpdateQueue* layerUpdateQueue, bool opaque, bool wideColorGamut, + const BakedOpRenderer::LightInfo& lightInfo) override; TaskManager* getTaskManager() override; - bool createOrUpdateLayer(RenderNode* node, - const DamageAccumulator& damageAccumulator, bool wideColorGamut) override; + bool createOrUpdateLayer(RenderNode* node, const DamageAccumulator& damageAccumulator, + bool wideColorGamut) override; bool pinImages(std::vector<SkImage*>& mutableImages) override { return false; } bool pinImages(LsaVector<sk_sp<Bitmap>>& images) override; void unpinImages() override; + void onPrepareTree() override {} static void destroyLayer(RenderNode* node); static void prepareToDraw(const RenderThread& thread, Bitmap* bitmap); static void invokeFunctor(const RenderThread& thread, Functor* functor); - static sk_sp<Bitmap> allocateHardwareBitmap(RenderThread& thread, - SkBitmap& skBitmap); + static sk_sp<Bitmap> allocateHardwareBitmap(RenderThread& thread, SkBitmap& skBitmap); private: EglManager& mEglManager; diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp index 9048bd14b35c..79e46ed9e65f 100644 --- a/libs/hwui/renderthread/RenderProxy.cpp +++ b/libs/hwui/renderthread/RenderProxy.cpp @@ -22,11 +22,11 @@ #include "Readback.h" #include "Rect.h" #include "pipeline/skia/VectorDrawableAtlas.h" +#include "renderstate/RenderState.h" #include "renderthread/CanvasContext.h" #include "renderthread/EglManager.h" #include "renderthread/RenderTask.h" #include "renderthread/RenderThread.h" -#include "renderstate/RenderState.h" #include "utils/Macros.h" #include "utils/TimeUtils.h" @@ -36,46 +36,12 @@ namespace android { namespace uirenderer { namespace renderthread { -#define ARGS(method) method ## Args - -#define CREATE_BRIDGE0(name) CREATE_BRIDGE(name,,,,,,,,) -#define CREATE_BRIDGE1(name, a1) CREATE_BRIDGE(name, a1,,,,,,,) -#define CREATE_BRIDGE2(name, a1, a2) CREATE_BRIDGE(name, a1,a2,,,,,,) -#define CREATE_BRIDGE3(name, a1, a2, a3) CREATE_BRIDGE(name, a1,a2,a3,,,,,) -#define CREATE_BRIDGE4(name, a1, a2, a3, a4) CREATE_BRIDGE(name, a1,a2,a3,a4,,,,) -#define CREATE_BRIDGE5(name, a1, a2, a3, a4, a5) CREATE_BRIDGE(name, a1,a2,a3,a4,a5,,,) -#define CREATE_BRIDGE6(name, a1, a2, a3, a4, a5, a6) CREATE_BRIDGE(name, a1,a2,a3,a4,a5,a6,,) -#define CREATE_BRIDGE7(name, a1, a2, a3, a4, a5, a6, a7) CREATE_BRIDGE(name, a1,a2,a3,a4,a5,a6,a7,) -#define CREATE_BRIDGE(name, a1, a2, a3, a4, a5, a6, a7, a8) \ - typedef struct { \ - a1; a2; a3; a4; a5; a6; a7; a8; \ - } ARGS(name); \ - static_assert(std::is_trivially_destructible<ARGS(name)>::value, \ - "Error, ARGS must be trivially destructible!"); \ - static void* Bridge_ ## name(ARGS(name)* args) - -#define SETUP_TASK(method) \ - LOG_ALWAYS_FATAL_IF( METHOD_INVOKE_PAYLOAD_SIZE < sizeof(ARGS(method)), \ - "METHOD_INVOKE_PAYLOAD_SIZE %zu is smaller than sizeof(" #method "Args) %zu", \ - METHOD_INVOKE_PAYLOAD_SIZE, sizeof(ARGS(method))); \ - MethodInvokeRenderTask* task = new MethodInvokeRenderTask((RunnableMethod) Bridge_ ## method); \ - ARGS(method) *args = (ARGS(method) *) task->payload() - -CREATE_BRIDGE4(createContext, RenderThread* thread, bool translucent, - RenderNode* rootRenderNode, IContextFactory* contextFactory) { - return CanvasContext::create(*args->thread, args->translucent, - args->rootRenderNode, args->contextFactory); -} - -RenderProxy::RenderProxy(bool translucent, RenderNode* rootRenderNode, IContextFactory* contextFactory) - : mRenderThread(RenderThread::getInstance()) - , mContext(nullptr) { - SETUP_TASK(createContext); - args->translucent = translucent; - args->rootRenderNode = rootRenderNode; - args->thread = &mRenderThread; - args->contextFactory = contextFactory; - mContext = (CanvasContext*) postAndWait(task); +RenderProxy::RenderProxy(bool translucent, RenderNode* rootRenderNode, + IContextFactory* contextFactory) + : mRenderThread(RenderThread::getInstance()), mContext(nullptr) { + mContext = mRenderThread.queue().runSync([&]() -> CanvasContext* { + return CanvasContext::create(mRenderThread, translucent, rootRenderNode, contextFactory); + }); mDrawFrameTask.setContext(&mRenderThread, mContext, rootRenderNode); } @@ -83,162 +49,72 @@ RenderProxy::~RenderProxy() { destroyContext(); } -CREATE_BRIDGE1(destroyContext, CanvasContext* context) { - delete args->context; - return nullptr; -} - void RenderProxy::destroyContext() { if (mContext) { - SETUP_TASK(destroyContext); - args->context = mContext; - mContext = nullptr; mDrawFrameTask.setContext(nullptr, nullptr, nullptr); // This is also a fence as we need to be certain that there are no // outstanding mDrawFrame tasks posted before it is destroyed - postAndWait(task); + mRenderThread.queue().runSync([this]() { delete mContext; }); + mContext = nullptr; } } -CREATE_BRIDGE2(setSwapBehavior, CanvasContext* context, SwapBehavior swapBehavior) { - args->context->setSwapBehavior(args->swapBehavior); - return nullptr; -} - void RenderProxy::setSwapBehavior(SwapBehavior swapBehavior) { - SETUP_TASK(setSwapBehavior); - args->context = mContext; - args->swapBehavior = swapBehavior; - post(task); -} - -CREATE_BRIDGE1(loadSystemProperties, CanvasContext* context) { - bool needsRedraw = false; - if (Caches::hasInstance()) { - needsRedraw = Properties::load(); - } - if (args->context->profiler().consumeProperties()) { - needsRedraw = true; - } - return (void*) needsRedraw; + mRenderThread.queue().post([this, swapBehavior]() { mContext->setSwapBehavior(swapBehavior); }); } bool RenderProxy::loadSystemProperties() { - SETUP_TASK(loadSystemProperties); - args->context = mContext; - return (bool) postAndWait(task); -} - -CREATE_BRIDGE2(setName, CanvasContext* context, const char* name) { - args->context->setName(std::string(args->name)); - return nullptr; + return mRenderThread.queue().runSync([this]() -> bool { + bool needsRedraw = false; + if (Caches::hasInstance()) { + needsRedraw = Properties::load(); + } + if (mContext->profiler().consumeProperties()) { + needsRedraw = true; + } + return needsRedraw; + }); } void RenderProxy::setName(const char* name) { - SETUP_TASK(setName); - args->context = mContext; - args->name = name; - postAndWait(task); // block since name/value pointers owned by caller -} - -CREATE_BRIDGE2(initialize, CanvasContext* context, Surface* surface) { - args->context->initialize(args->surface); - return nullptr; + // block since name/value pointers owned by caller + // TODO: Support move arguments + mRenderThread.queue().runSync([this, name]() { mContext->setName(std::string(name)); }); } void RenderProxy::initialize(const sp<Surface>& surface) { - SETUP_TASK(initialize); - args->context = mContext; - args->surface = surface.get(); - post(task); -} - -CREATE_BRIDGE2(updateSurface, CanvasContext* context, Surface* surface) { - args->context->updateSurface(args->surface); - return nullptr; + mRenderThread.queue().post( + [ this, surf = surface ]() mutable { mContext->setSurface(std::move(surf)); }); } void RenderProxy::updateSurface(const sp<Surface>& surface) { - SETUP_TASK(updateSurface); - args->context = mContext; - args->surface = surface.get(); - post(task); -} - -CREATE_BRIDGE2(pauseSurface, CanvasContext* context, Surface* surface) { - return (void*) args->context->pauseSurface(args->surface); + mRenderThread.queue().post( + [ this, surf = surface ]() mutable { mContext->setSurface(std::move(surf)); }); } bool RenderProxy::pauseSurface(const sp<Surface>& surface) { - SETUP_TASK(pauseSurface); - args->context = mContext; - args->surface = surface.get(); - return (bool) postAndWait(task); -} - -CREATE_BRIDGE2(setStopped, CanvasContext* context, bool stopped) { - args->context->setStopped(args->stopped); - return nullptr; + return mRenderThread.queue().runSync([this]() -> bool { return mContext->pauseSurface(); }); } void RenderProxy::setStopped(bool stopped) { - SETUP_TASK(setStopped); - args->context = mContext; - args->stopped = stopped; - postAndWait(task); -} - -CREATE_BRIDGE4(setup, CanvasContext* context, - float lightRadius, uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha) { - args->context->setup(args->lightRadius, - args->ambientShadowAlpha, args->spotShadowAlpha); - return nullptr; + mRenderThread.queue().runSync([this, stopped]() { mContext->setStopped(stopped); }); } -void RenderProxy::setup(float lightRadius, - uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha) { - SETUP_TASK(setup); - args->context = mContext; - args->lightRadius = lightRadius; - args->ambientShadowAlpha = ambientShadowAlpha; - args->spotShadowAlpha = spotShadowAlpha; - post(task); -} - -CREATE_BRIDGE2(setLightCenter, CanvasContext* context, Vector3 lightCenter) { - args->context->setLightCenter(args->lightCenter); - return nullptr; +void RenderProxy::setup(float lightRadius, uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha) { + mRenderThread.queue().post( + [=]() { mContext->setup(lightRadius, ambientShadowAlpha, spotShadowAlpha); }); } void RenderProxy::setLightCenter(const Vector3& lightCenter) { - SETUP_TASK(setLightCenter); - args->context = mContext; - args->lightCenter = lightCenter; - post(task); -} - -CREATE_BRIDGE2(setOpaque, CanvasContext* context, bool opaque) { - args->context->setOpaque(args->opaque); - return nullptr; + mRenderThread.queue().post([=]() { mContext->setLightCenter(lightCenter); }); } void RenderProxy::setOpaque(bool opaque) { - SETUP_TASK(setOpaque); - args->context = mContext; - args->opaque = opaque; - post(task); -} - -CREATE_BRIDGE2(setWideGamut, CanvasContext* context, bool wideGamut) { - args->context->setWideGamut(args->wideGamut); - return nullptr; + mRenderThread.queue().post([=]() { mContext->setOpaque(opaque); }); } void RenderProxy::setWideGamut(bool wideGamut) { - SETUP_TASK(setWideGamut); - args->context = mContext; - args->wideGamut = wideGamut; - post(task); + mRenderThread.queue().post([=]() { mContext->setWideGamut(wideGamut); }); } int64_t* RenderProxy::frameInfo() { @@ -249,77 +125,40 @@ int RenderProxy::syncAndDrawFrame() { return mDrawFrameTask.drawFrame(); } -CREATE_BRIDGE1(destroy, CanvasContext* context) { - args->context->destroy(); - return nullptr; -} - void RenderProxy::destroy() { - SETUP_TASK(destroy); - args->context = mContext; // destroyCanvasAndSurface() needs a fence as when it returns the // underlying BufferQueue is going to be released from under // the render thread. - postAndWait(task); -} - -CREATE_BRIDGE2(invokeFunctor, RenderThread* thread, Functor* functor) { - CanvasContext::invokeFunctor(*args->thread, args->functor); - return nullptr; + mRenderThread.queue().runSync([=]() { mContext->destroy(); }); } void RenderProxy::invokeFunctor(Functor* functor, bool waitForCompletion) { ATRACE_CALL(); RenderThread& thread = RenderThread::getInstance(); - SETUP_TASK(invokeFunctor); - args->thread = &thread; - args->functor = functor; + auto invoke = [&thread, functor]() { CanvasContext::invokeFunctor(thread, functor); }; if (waitForCompletion) { // waitForCompletion = true is expected to be fairly rare and only // happen in destruction. Thus it should be fine to temporarily // create a Mutex - staticPostAndWait(task); + thread.queue().runSync(std::move(invoke)); } else { - thread.queue(task); + thread.queue().post(std::move(invoke)); } } -CREATE_BRIDGE1(createTextureLayer, CanvasContext* context) { - return args->context->createTextureLayer(); -} - DeferredLayerUpdater* RenderProxy::createTextureLayer() { - SETUP_TASK(createTextureLayer); - args->context = mContext; - void* retval = postAndWait(task); - DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(retval); - return layer; -} - -CREATE_BRIDGE2(buildLayer, CanvasContext* context, RenderNode* node) { - args->context->buildLayer(args->node); - return nullptr; + return mRenderThread.queue().runSync([this]() -> auto { + return mContext->createTextureLayer(); + }); } void RenderProxy::buildLayer(RenderNode* node) { - SETUP_TASK(buildLayer); - args->context = mContext; - args->node = node; - postAndWait(task); -} - -CREATE_BRIDGE3(copyLayerInto, CanvasContext* context, DeferredLayerUpdater* layer, - SkBitmap* bitmap) { - bool success = args->context->copyLayerInto(args->layer, args->bitmap); - return (void*) success; + mRenderThread.queue().runSync([&]() { mContext->buildLayer(node); }); } bool RenderProxy::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap& bitmap) { - SETUP_TASK(copyLayerInto); - args->context = mContext; - args->layer = layer; - args->bitmap = &bitmap; - return (bool) postAndWait(task); + return mRenderThread.queue().runSync( + [&]() -> bool { return mContext->copyLayerInto(layer, &bitmap); }); } void RenderProxy::pushLayerUpdate(DeferredLayerUpdater* layer) { @@ -330,302 +169,129 @@ void RenderProxy::cancelLayerUpdate(DeferredLayerUpdater* layer) { mDrawFrameTask.removeLayerUpdate(layer); } -CREATE_BRIDGE1(detachSurfaceTexture, DeferredLayerUpdater* layer) { - args->layer->detachSurfaceTexture(); - return nullptr; -} - void RenderProxy::detachSurfaceTexture(DeferredLayerUpdater* layer) { - SETUP_TASK(detachSurfaceTexture); - args->layer = layer; - postAndWait(task); -} - -CREATE_BRIDGE1(destroyHardwareResources, CanvasContext* context) { - args->context->destroyHardwareResources(); - return nullptr; + return mRenderThread.queue().runSync([&]() { layer->detachSurfaceTexture(); }); } void RenderProxy::destroyHardwareResources() { - SETUP_TASK(destroyHardwareResources); - args->context = mContext; - postAndWait(task); -} - -CREATE_BRIDGE2(trimMemory, RenderThread* thread, int level) { - CanvasContext::trimMemory(*args->thread, args->level); - return nullptr; + return mRenderThread.queue().runSync([&]() { mContext->destroyHardwareResources(); }); } void RenderProxy::trimMemory(int level) { // Avoid creating a RenderThread to do a trimMemory. if (RenderThread::hasInstance()) { RenderThread& thread = RenderThread::getInstance(); - SETUP_TASK(trimMemory); - args->thread = &thread; - args->level = level; - thread.queue(task); + thread.queue().post([&thread, level]() { CanvasContext::trimMemory(thread, level); }); } } -CREATE_BRIDGE2(overrideProperty, const char* name, const char* value) { - Properties::overrideProperty(args->name, args->value); - return nullptr; -} - void RenderProxy::overrideProperty(const char* name, const char* value) { - SETUP_TASK(overrideProperty); - args->name = name; - args->value = value; - staticPostAndWait(task); // expensive, but block here since name/value pointers owned by caller + // expensive, but block here since name/value pointers owned by caller + RenderThread::getInstance().queue().runSync( + [&]() { Properties::overrideProperty(name, value); }); } -CREATE_BRIDGE0(fence) { - // Intentionally empty - return nullptr; -} - -template <typename T> -void UNUSED(T t) {} - void RenderProxy::fence() { - SETUP_TASK(fence); - UNUSED(args); - postAndWait(task); + mRenderThread.queue().runSync([]() {}); } void RenderProxy::staticFence() { - SETUP_TASK(fence); - UNUSED(args); - staticPostAndWait(task); -} - -CREATE_BRIDGE1(stopDrawing, CanvasContext* context) { - args->context->stopDrawing(); - return nullptr; + RenderThread::getInstance().queue().runSync([]() {}); } void RenderProxy::stopDrawing() { - SETUP_TASK(stopDrawing); - args->context = mContext; - postAndWait(task); -} - -CREATE_BRIDGE1(notifyFramePending, CanvasContext* context) { - args->context->notifyFramePending(); - return nullptr; + mRenderThread.queue().runSync([this]() { mContext->stopDrawing(); }); } void RenderProxy::notifyFramePending() { - SETUP_TASK(notifyFramePending); - args->context = mContext; - mRenderThread.queueAtFront(task); -} - -CREATE_BRIDGE4(dumpProfileInfo, CanvasContext* context, RenderThread* thread, - int fd, int dumpFlags) { - args->context->profiler().dumpData(args->fd); - if (args->dumpFlags & DumpFlags::FrameStats) { - args->context->dumpFrames(args->fd); - } - if (args->dumpFlags & DumpFlags::JankStats) { - args->thread->globalProfileData()->dump(args->fd); - } - if (args->dumpFlags & DumpFlags::Reset) { - args->context->resetFrameStats(); - } - return nullptr; + mRenderThread.queue().post([this]() { mContext->notifyFramePending(); }); } void RenderProxy::dumpProfileInfo(int fd, int dumpFlags) { - SETUP_TASK(dumpProfileInfo); - args->context = mContext; - args->thread = &mRenderThread; - args->fd = fd; - args->dumpFlags = dumpFlags; - postAndWait(task); -} - -CREATE_BRIDGE1(resetProfileInfo, CanvasContext* context) { - args->context->resetFrameStats(); - return nullptr; + mRenderThread.queue().runSync([&]() { + mContext->profiler().dumpData(fd); + if (dumpFlags & DumpFlags::FrameStats) { + mContext->dumpFrames(fd); + } + if (dumpFlags & DumpFlags::JankStats) { + mRenderThread.globalProfileData()->dump(fd); + } + if (dumpFlags & DumpFlags::Reset) { + mContext->resetFrameStats(); + } + }); } void RenderProxy::resetProfileInfo() { - SETUP_TASK(resetProfileInfo); - args->context = mContext; - postAndWait(task); -} - -CREATE_BRIDGE2(frameTimePercentile, RenderThread* thread, int percentile) { - return reinterpret_cast<void*>(static_cast<uintptr_t>( - args->thread->globalProfileData()->findPercentile(args->percentile))); -} - -uint32_t RenderProxy::frameTimePercentile(int p) { - SETUP_TASK(frameTimePercentile); - args->thread = &mRenderThread; - args->percentile = p; - return static_cast<uint32_t>(reinterpret_cast<uintptr_t>( - postAndWait(task))); + mRenderThread.queue().runSync([=]() { mContext->resetFrameStats(); }); } -CREATE_BRIDGE2(dumpGraphicsMemory, int fd, RenderThread* thread) { - args->thread->dumpGraphicsMemory(args->fd); - return nullptr; +uint32_t RenderProxy::frameTimePercentile(int percentile) { + return mRenderThread.queue().runSync([&]() -> auto { + return mRenderThread.globalProfileData()->findPercentile(percentile); + }); } void RenderProxy::dumpGraphicsMemory(int fd) { - if (!RenderThread::hasInstance()) return; - SETUP_TASK(dumpGraphicsMemory); - args->fd = fd; - args->thread = &RenderThread::getInstance(); - staticPostAndWait(task); -} - -CREATE_BRIDGE2(setProcessStatsBuffer, RenderThread* thread, int fd) { - args->thread->globalProfileData().switchStorageToAshmem(args->fd); - close(args->fd); - return nullptr; + auto& thread = RenderThread::getInstance(); + thread.queue().runSync([&]() { thread.dumpGraphicsMemory(fd); }); } void RenderProxy::setProcessStatsBuffer(int fd) { - SETUP_TASK(setProcessStatsBuffer); auto& rt = RenderThread::getInstance(); - args->thread = &rt; - args->fd = dup(fd); - rt.queue(task); -} - -CREATE_BRIDGE1(rotateProcessStatsBuffer, RenderThread* thread) { - args->thread->globalProfileData().rotateStorage(); - return nullptr; + rt.queue().post([&rt, fd = dup(fd) ]() { + rt.globalProfileData().switchStorageToAshmem(fd); + close(fd); + }); } void RenderProxy::rotateProcessStatsBuffer() { - SETUP_TASK(rotateProcessStatsBuffer); auto& rt = RenderThread::getInstance(); - args->thread = &rt; - rt.queue(task); + rt.queue().post([&rt]() { rt.globalProfileData().rotateStorage(); }); } int RenderProxy::getRenderThreadTid() { return mRenderThread.getTid(); } -CREATE_BRIDGE3(addRenderNode, CanvasContext* context, RenderNode* node, bool placeFront) { - args->context->addRenderNode(args->node, args->placeFront); - return nullptr; -} - void RenderProxy::addRenderNode(RenderNode* node, bool placeFront) { - SETUP_TASK(addRenderNode); - args->context = mContext; - args->node = node; - args->placeFront = placeFront; - post(task); -} - -CREATE_BRIDGE2(removeRenderNode, CanvasContext* context, RenderNode* node) { - args->context->removeRenderNode(args->node); - return nullptr; + mRenderThread.queue().post([=]() { mContext->addRenderNode(node, placeFront); }); } void RenderProxy::removeRenderNode(RenderNode* node) { - SETUP_TASK(removeRenderNode); - args->context = mContext; - args->node = node; - post(task); -} - -CREATE_BRIDGE2(drawRenderNode, CanvasContext* context, RenderNode* node) { - args->context->prepareAndDraw(args->node); - return nullptr; + mRenderThread.queue().post([=]() { mContext->removeRenderNode(node); }); } void RenderProxy::drawRenderNode(RenderNode* node) { - SETUP_TASK(drawRenderNode); - args->context = mContext; - args->node = node; - // Be pseudo-thread-safe and don't use any member variables - staticPostAndWait(task); + mRenderThread.queue().runSync([=]() { mContext->prepareAndDraw(node); }); } void RenderProxy::setContentDrawBounds(int left, int top, int right, int bottom) { mDrawFrameTask.setContentDrawBounds(left, top, right, bottom); } -CREATE_BRIDGE1(serializeDisplayListTree, CanvasContext* context) { - args->context->serializeDisplayListTree(); - return nullptr; -} - void RenderProxy::serializeDisplayListTree() { - SETUP_TASK(serializeDisplayListTree); - args->context = mContext; - post(task); -} - -CREATE_BRIDGE2(addFrameMetricsObserver, CanvasContext* context, - FrameMetricsObserver* frameStatsObserver) { - args->context->addFrameMetricsObserver(args->frameStatsObserver); - if (args->frameStatsObserver != nullptr) { - args->frameStatsObserver->decStrong(args->context); - } - return nullptr; -} - -void RenderProxy::addFrameMetricsObserver(FrameMetricsObserver* observer) { - SETUP_TASK(addFrameMetricsObserver); - args->context = mContext; - args->frameStatsObserver = observer; - if (observer != nullptr) { - observer->incStrong(mContext); - } - post(task); -} - -CREATE_BRIDGE2(removeFrameMetricsObserver, CanvasContext* context, - FrameMetricsObserver* frameStatsObserver) { - args->context->removeFrameMetricsObserver(args->frameStatsObserver); - if (args->frameStatsObserver != nullptr) { - args->frameStatsObserver->decStrong(args->context); - } - return nullptr; -} - -void RenderProxy::removeFrameMetricsObserver(FrameMetricsObserver* observer) { - SETUP_TASK(removeFrameMetricsObserver); - args->context = mContext; - args->frameStatsObserver = observer; - if (observer != nullptr) { - observer->incStrong(mContext); - } - post(task); + mRenderThread.queue().post([=]() { mContext->serializeDisplayListTree(); }); } -CREATE_BRIDGE4(copySurfaceInto, RenderThread* thread, - Surface* surface, Rect srcRect, SkBitmap* bitmap) { - return (void*)args->thread->readback().copySurfaceInto(*args->surface, - args->srcRect, args->bitmap); +void RenderProxy::addFrameMetricsObserver(FrameMetricsObserver* observerPtr) { + mRenderThread.queue().post([ this, observer = sp{observerPtr} ]() { + mContext->addFrameMetricsObserver(observer.get()); + }); } -int RenderProxy::copySurfaceInto(sp<Surface>& surface, int left, int top, - int right, int bottom, SkBitmap* bitmap) { - SETUP_TASK(copySurfaceInto); - args->bitmap = bitmap; - args->surface = surface.get(); - args->thread = &RenderThread::getInstance(); - args->srcRect.set(left, top, right, bottom); - return static_cast<int>( - reinterpret_cast<intptr_t>( staticPostAndWait(task) )); +void RenderProxy::removeFrameMetricsObserver(FrameMetricsObserver* observerPtr) { + mRenderThread.queue().post([ this, observer = sp{observerPtr} ]() { + mContext->removeFrameMetricsObserver(observer.get()); + }); } -CREATE_BRIDGE2(prepareToDraw, RenderThread* thread, Bitmap* bitmap) { - CanvasContext::prepareToDraw(*args->thread, args->bitmap); - args->bitmap->unref(); - args->bitmap = nullptr; - return nullptr; +int RenderProxy::copySurfaceInto(sp<Surface>& surface, int left, int top, int right, int bottom, + SkBitmap* bitmap) { + auto& thread = RenderThread::getInstance(); + return static_cast<int>(thread.queue().runSync([&]() -> auto { + return thread.readback().copySurfaceInto(*surface, Rect(left, top, right, bottom), bitmap); + })); } void RenderProxy::prepareToDraw(Bitmap& bitmap) { @@ -635,10 +301,11 @@ void RenderProxy::prepareToDraw(Bitmap& bitmap) { // window or not. if (!RenderThread::hasInstance()) return; RenderThread* renderThread = &RenderThread::getInstance(); - SETUP_TASK(prepareToDraw); - args->thread = renderThread; bitmap.ref(); - args->bitmap = &bitmap; + auto task = [renderThread, &bitmap]() { + CanvasContext::prepareToDraw(*renderThread, &bitmap); + bitmap.unref(); + }; nsecs_t lastVsync = renderThread->timeLord().latestVsync(); nsecs_t estimatedNextVsync = lastVsync + renderThread->timeLord().frameIntervalNanos(); nsecs_t timeToNextVsync = estimatedNextVsync - systemTime(CLOCK_MONOTONIC); @@ -648,97 +315,52 @@ void RenderProxy::prepareToDraw(Bitmap& bitmap) { // TODO: Make this concept a first-class supported thing? RT could use // knowledge of pending draws to better schedule this task if (timeToNextVsync > -6_ms && timeToNextVsync < 1_ms) { - renderThread->queueAt(task, estimatedNextVsync + 8_ms); + renderThread->queue().postAt(estimatedNextVsync + 8_ms, task); } else { - renderThread->queue(task); + renderThread->queue().post(task); } } -CREATE_BRIDGE2(allocateHardwareBitmap, RenderThread* thread, SkBitmap* bitmap) { - sk_sp<Bitmap> hardwareBitmap = args->thread->allocateHardwareBitmap(*args->bitmap); - return hardwareBitmap.release(); -} - sk_sp<Bitmap> RenderProxy::allocateHardwareBitmap(SkBitmap& bitmap) { - SETUP_TASK(allocateHardwareBitmap); - args->bitmap = &bitmap; - args->thread = &RenderThread::getInstance(); - sk_sp<Bitmap> hardwareBitmap(reinterpret_cast<Bitmap*>(staticPostAndWait(task))); - return hardwareBitmap; -} - -CREATE_BRIDGE3(copyGraphicBufferInto, RenderThread* thread, GraphicBuffer* buffer, SkBitmap* bitmap) { - return (void*) args->thread->readback().copyGraphicBufferInto(args->buffer, args->bitmap); + auto& thread = RenderThread::getInstance(); + return thread.queue().runSync([&]() -> auto { return thread.allocateHardwareBitmap(bitmap); }); } int RenderProxy::copyGraphicBufferInto(GraphicBuffer* buffer, SkBitmap* bitmap) { RenderThread& thread = RenderThread::getInstance(); if (Properties::isSkiaEnabled() && gettid() == thread.getTid()) { - //TODO: fix everything that hits this. We should never be triggering a readback ourselves. - return (int) thread.readback().copyGraphicBufferInto(buffer, bitmap); + // TODO: fix everything that hits this. We should never be triggering a readback ourselves. + return (int)thread.readback().copyGraphicBufferInto(buffer, bitmap); } else { - SETUP_TASK(copyGraphicBufferInto); - args->thread = &thread; - args->bitmap = bitmap; - args->buffer = buffer; - return static_cast<int>(reinterpret_cast<intptr_t>(staticPostAndWait(task))); + return thread.queue().runSync([&]() -> int { + return (int)thread.readback().copyGraphicBufferInto(buffer, bitmap); + }); } } -CREATE_BRIDGE2(onBitmapDestroyed, RenderThread* thread, uint32_t pixelRefId) { - args->thread->renderState().onBitmapDestroyed(args->pixelRefId); - return nullptr; -} - void RenderProxy::onBitmapDestroyed(uint32_t pixelRefId) { if (!RenderThread::hasInstance()) return; - SETUP_TASK(onBitmapDestroyed); RenderThread& thread = RenderThread::getInstance(); - args->thread = &thread; - args->pixelRefId = pixelRefId; - thread.queue(task); + thread.queue().post( + [&thread, pixelRefId]() { thread.renderState().onBitmapDestroyed(pixelRefId); }); } void RenderProxy::disableVsync() { Properties::disableVsync = true; } -void RenderProxy::post(RenderTask* task) { - mRenderThread.queue(task); -} - -CREATE_BRIDGE1(repackVectorDrawableAtlas, RenderThread* thread) { - args->thread->cacheManager().acquireVectorDrawableAtlas()->repackIfNeeded( - args->thread->getGrContext()); - return nullptr; -} - void RenderProxy::repackVectorDrawableAtlas() { RenderThread& thread = RenderThread::getInstance(); - SETUP_TASK(repackVectorDrawableAtlas); - args->thread = &thread; - thread.queue(task); -} - -void* RenderProxy::postAndWait(MethodInvokeRenderTask* task) { - void* retval; - task->setReturnPtr(&retval); - SignalingRenderTask syncTask(task, &mSyncMutex, &mSyncCondition); - AutoMutex _lock(mSyncMutex); - mRenderThread.queue(&syncTask); - while (!syncTask.hasRun()) { - mSyncCondition.wait(mSyncMutex); - } - return retval; + thread.queue().post([&thread]() { + thread.cacheManager().acquireVectorDrawableAtlas()->repackIfNeeded(thread.getGrContext()); + }); } -void* RenderProxy::staticPostAndWait(MethodInvokeRenderTask* task) { +void RenderProxy::releaseVDAtlasEntries() { RenderThread& thread = RenderThread::getInstance(); - LOG_ALWAYS_FATAL_IF(gettid() == thread.getTid()); - void* retval; - task->setReturnPtr(&retval); - thread.queueAndWait(task); - return retval; + thread.queue().post([&thread]() { + thread.cacheManager().acquireVectorDrawableAtlas()->delayedReleaseEntries(); + }); } } /* namespace renderthread */ diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h index 06eaebd066ee..bc57d9255ad5 100644 --- a/libs/hwui/renderthread/RenderProxy.h +++ b/libs/hwui/renderthread/RenderProxy.h @@ -17,22 +17,16 @@ #ifndef RENDERPROXY_H_ #define RENDERPROXY_H_ -#include "RenderTask.h" - -#include <cutils/compiler.h> -#include <EGL/egl.h> #include <SkBitmap.h> -#include <utils/Condition.h> +#include <cutils/compiler.h> +#include <gui/Surface.h> #include <utils/Functor.h> -#include <utils/Mutex.h> -#include <utils/Timers.h> -#include <utils/StrongPointer.h> -#include "../Caches.h" #include "../FrameMetricsObserver.h" #include "../IContextFactory.h" -#include "CanvasContext.h" #include "DrawFrameTask.h" +#include "SwapBehavior.h" +#include "hwui/Bitmap.h" namespace android { class GraphicBuffer; @@ -41,22 +35,20 @@ namespace uirenderer { class DeferredLayerUpdater; class RenderNode; -class DisplayList; -class Layer; class Rect; namespace renderthread { -class ErrorChannel; +class CanvasContext; class RenderThread; class RenderProxyBridge; namespace DumpFlags { - enum { - FrameStats = 1 << 0, - Reset = 1 << 1, - JankStats = 1 << 2, - }; +enum { + FrameStats = 1 << 0, + Reset = 1 << 1, + JankStats = 1 << 2, +}; }; /* @@ -81,8 +73,7 @@ public: ANDROID_API void updateSurface(const sp<Surface>& surface); ANDROID_API bool pauseSurface(const sp<Surface>& surface); ANDROID_API void setStopped(bool stopped); - ANDROID_API void setup(float lightRadius, - uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha); + ANDROID_API void setup(float lightRadius, uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha); ANDROID_API void setLightCenter(const Vector3& lightCenter); ANDROID_API void setOpaque(bool opaque); ANDROID_API void setWideGamut(bool wideGamut); @@ -129,8 +120,8 @@ public: ANDROID_API void removeFrameMetricsObserver(FrameMetricsObserver* observer); ANDROID_API long getDroppedFrameReportCount(); - ANDROID_API static int copySurfaceInto(sp<Surface>& surface, - int left, int top, int right, int bottom, SkBitmap* bitmap); + ANDROID_API static int copySurfaceInto(sp<Surface>& surface, int left, int top, int right, + int bottom, SkBitmap* bitmap); ANDROID_API static void prepareToDraw(Bitmap& bitmap); static sk_sp<Bitmap> allocateHardwareBitmap(SkBitmap& bitmap); @@ -143,22 +134,16 @@ public: static void repackVectorDrawableAtlas(); + static void releaseVDAtlasEntries(); + private: RenderThread& mRenderThread; CanvasContext* mContext; DrawFrameTask mDrawFrameTask; - Mutex mSyncMutex; - Condition mSyncCondition; - void destroyContext(); - void post(RenderTask* task); - void* postAndWait(MethodInvokeRenderTask* task); - - static void* staticPostAndWait(MethodInvokeRenderTask* task); - // Friend class to help with bridging friend class RenderProxyBridge; }; diff --git a/libs/hwui/renderthread/RenderTask.h b/libs/hwui/renderthread/RenderTask.h index a7acf91cbdb1..c56a3578ad58 100644 --- a/libs/hwui/renderthread/RenderTask.h +++ b/libs/hwui/renderthread/RenderTask.h @@ -53,7 +53,7 @@ public: ANDROID_API virtual void run() = 0; RenderTask* mNext; - nsecs_t mRunAt; // nano-seconds on the SYSTEM_TIME_MONOTONIC clock + nsecs_t mRunAt; // nano-seconds on the SYSTEM_TIME_MONOTONIC clock }; class SignalingRenderTask : public RenderTask { @@ -75,8 +75,7 @@ typedef void* (*RunnableMethod)(void* data); class MethodInvokeRenderTask : public RenderTask { public: - explicit MethodInvokeRenderTask(RunnableMethod method) - : mMethod(method), mReturnPtr(nullptr) {} + explicit MethodInvokeRenderTask(RunnableMethod method) : mMethod(method), mReturnPtr(nullptr) {} void* payload() { return mData; } void setReturnPtr(void** retptr) { mReturnPtr = retptr; } @@ -89,6 +88,7 @@ public: // Commit suicide delete this; } + private: RunnableMethod mMethod; char mData[METHOD_INVOKE_PAYLOAD_SIZE]; diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp index 72a428f1c70c..8e0546b529af 100644 --- a/libs/hwui/renderthread/RenderThread.cpp +++ b/libs/hwui/renderthread/RenderThread.cpp @@ -16,17 +16,18 @@ #include "RenderThread.h" -#include "hwui/Bitmap.h" -#include "renderstate/RenderState.h" -#include "renderthread/OpenGLPipeline.h" -#include "pipeline/skia/SkiaOpenGLReadback.h" -#include "pipeline/skia/SkiaOpenGLPipeline.h" -#include "pipeline/skia/SkiaVulkanPipeline.h" #include "CanvasContext.h" #include "EglManager.h" #include "OpenGLReadback.h" #include "RenderProxy.h" #include "VulkanManager.h" +#include "hwui/Bitmap.h" +#include "pipeline/skia/SkiaOpenGLPipeline.h" +#include "pipeline/skia/SkiaOpenGLReadback.h" +#include "pipeline/skia/SkiaVulkanReadback.h" +#include "pipeline/skia/SkiaVulkanPipeline.h" +#include "renderstate/RenderState.h" +#include "renderthread/OpenGLPipeline.h" #include "utils/FatVector.h" #include <gui/DisplayEventReceiver.h> @@ -49,107 +50,19 @@ static const size_t EVENT_BUFFER_SIZE = 100; // Slight delay to give the UI time to push us a new frame before we replay static const nsecs_t DISPATCH_FRAME_CALLBACKS_DELAY = milliseconds_to_nanoseconds(4); -TaskQueue::TaskQueue() : mHead(nullptr), mTail(nullptr) {} - -RenderTask* TaskQueue::next() { - RenderTask* ret = mHead; - if (ret) { - mHead = ret->mNext; - if (!mHead) { - mTail = nullptr; - } - ret->mNext = nullptr; - } - return ret; -} - -RenderTask* TaskQueue::peek() { - return mHead; -} - -void TaskQueue::queue(RenderTask* task) { - // Since the RenderTask itself forms the linked list it is not allowed - // to have the same task queued twice - LOG_ALWAYS_FATAL_IF(task->mNext || mTail == task, "Task is already in the queue!"); - if (mTail) { - // Fast path if we can just append - if (mTail->mRunAt <= task->mRunAt) { - mTail->mNext = task; - mTail = task; - } else { - // Need to find the proper insertion point - RenderTask* previous = nullptr; - RenderTask* next = mHead; - while (next && next->mRunAt <= task->mRunAt) { - previous = next; - next = next->mNext; - } - if (!previous) { - task->mNext = mHead; - mHead = task; - } else { - previous->mNext = task; - if (next) { - task->mNext = next; - } else { - mTail = task; - } - } - } - } else { - mTail = mHead = task; - } -} - -void TaskQueue::queueAtFront(RenderTask* task) { - LOG_ALWAYS_FATAL_IF(task->mNext || mHead == task, "Task is already in the queue!"); - if (mTail) { - task->mNext = mHead; - mHead = task; - } else { - mTail = mHead = task; - } -} - -void TaskQueue::remove(RenderTask* task) { - // TaskQueue is strict here to enforce that users are keeping track of - // their RenderTasks due to how their memory is managed - LOG_ALWAYS_FATAL_IF(!task->mNext && mTail != task, - "Cannot remove a task that isn't in the queue!"); - - // If task is the head we can just call next() to pop it off - // Otherwise we need to scan through to find the task before it - if (peek() == task) { - next(); - } else { - RenderTask* previous = mHead; - while (previous->mNext != task) { - previous = previous->mNext; - } - previous->mNext = task->mNext; - if (mTail == task) { - mTail = previous; - } - } -} - -class DispatchFrameCallbacks : public RenderTask { -private: - RenderThread* mRenderThread; -public: - explicit DispatchFrameCallbacks(RenderThread* rt) : mRenderThread(rt) {} - - virtual void run() override { - mRenderThread->dispatchFrameCallbacks(); - } -}; - static bool gHasRenderThreadInstance = false; +static void (*gOnStartHook)() = nullptr; + bool RenderThread::hasInstance() { return gHasRenderThreadInstance; } +void RenderThread::setOnStartHook(void (*onStartHook)()) { + LOG_ALWAYS_FATAL_IF(hasInstance(), "can't set an onStartHook after we've started..."); + gOnStartHook = onStartHook; +} + RenderThread& RenderThread::getInstance() { // This is a pointer because otherwise __cxa_finalize // will try to delete it like a Good Citizen but that causes us to crash @@ -159,19 +72,16 @@ RenderThread& RenderThread::getInstance() { return *sInstance; } -RenderThread::RenderThread() : Thread(true) - , mNextWakeup(LLONG_MAX) +RenderThread::RenderThread() + : ThreadBase() , mDisplayEventReceiver(nullptr) , mVsyncRequested(false) , mFrameCallbackTaskPending(false) - , mFrameCallbackTask(nullptr) , mRenderState(nullptr) , mEglManager(nullptr) , mVkManager(nullptr) { Properties::load(); - mFrameCallbackTask = new DispatchFrameCallbacks(this); - mLooper = new Looper(false); - run("RenderThread"); + start("RenderThread"); } RenderThread::~RenderThread() { @@ -182,17 +92,18 @@ void RenderThread::initializeDisplayEventReceiver() { LOG_ALWAYS_FATAL_IF(mDisplayEventReceiver, "Initializing a second DisplayEventReceiver?"); mDisplayEventReceiver = new DisplayEventReceiver(); status_t status = mDisplayEventReceiver->initCheck(); - LOG_ALWAYS_FATAL_IF(status != NO_ERROR, "Initialization of DisplayEventReceiver " - "failed with status: %d", status); + LOG_ALWAYS_FATAL_IF(status != NO_ERROR, + "Initialization of DisplayEventReceiver " + "failed with status: %d", + status); // Register the FD - mLooper->addFd(mDisplayEventReceiver->getFd(), 0, - Looper::EVENT_INPUT, RenderThread::displayEventReceiverCallback, this); + mLooper->addFd(mDisplayEventReceiver->getFd(), 0, Looper::EVENT_INPUT, + RenderThread::displayEventReceiverCallback, this); } void RenderThread::initThreadLocals() { - sp<IBinder> dtoken(SurfaceComposerClient::getBuiltInDisplay( - ISurfaceComposer::eDisplayIdMain)); + sp<IBinder> dtoken(SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain)); status_t status = SurfaceComposerClient::getDisplayInfo(dtoken, &mDisplayInfo); LOG_ALWAYS_FATAL_IF(status, "Failed to get display info\n"); nsecs_t frameIntervalNanos = static_cast<nsecs_t>(1000000000 / mDisplayInfo.fps); @@ -232,18 +143,15 @@ void RenderThread::dumpGraphicsMemory(int fd) { break; } default: - LOG_ALWAYS_FATAL("canvas context type %d not supported", (int32_t) renderType); + LOG_ALWAYS_FATAL("canvas context type %d not supported", (int32_t)renderType); break; } - FILE *file = fdopen(fd, "a"); - fprintf(file, "\n%s\n", cachesOutput.string()); - fprintf(file, "\nPipeline=%s\n", pipeline.string()); - fflush(file); + dprintf(fd, "\n%s\n", cachesOutput.string()); + dprintf(fd, "\nPipeline=%s\n", pipeline.string()); } Readback& RenderThread::readback() { - if (!mReadback) { auto renderType = Properties::getRenderPipelineType(); switch (renderType) { @@ -251,14 +159,13 @@ Readback& RenderThread::readback() { mReadback = new OpenGLReadbackImpl(*this); break; case RenderPipelineType::SkiaGL: - case RenderPipelineType::SkiaVulkan: - // It works to use the OpenGL pipeline for Vulkan but this is not - // ideal as it causes us to create an OpenGL context in addition - // to the Vulkan one. mReadback = new skiapipeline::SkiaOpenGLReadback(*this); break; + case RenderPipelineType::SkiaVulkan: + mReadback = new skiapipeline::SkiaVulkanReadback(*this); + break; default: - LOG_ALWAYS_FATAL("canvas context type %d not supported", (int32_t) renderType); + LOG_ALWAYS_FATAL("canvas context type %d not supported", (int32_t)renderType); break; } } @@ -266,30 +173,32 @@ Readback& RenderThread::readback() { return *mReadback; } -void RenderThread::setGrContext(GrContext* context) { +void RenderThread::setGrContext(sk_sp<GrContext> context) { mCacheManager->reset(context); - if (mGrContext.get()) { + if (mGrContext) { mGrContext->releaseResourcesAndAbandonContext(); } - mGrContext.reset(context); + mGrContext = std::move(context); } int RenderThread::displayEventReceiverCallback(int fd, int events, void* data) { if (events & (Looper::EVENT_ERROR | Looper::EVENT_HANGUP)) { ALOGE("Display event receiver pipe was closed or an error occurred. " - "events=0x%x", events); - return 0; // remove the callback + "events=0x%x", + events); + return 0; // remove the callback } if (!(events & Looper::EVENT_INPUT)) { ALOGW("Received spurious callback for unhandled poll event. " - "events=0x%x", events); - return 1; // keep the callback + "events=0x%x", + events); + return 1; // keep the callback } reinterpret_cast<RenderThread*>(data)->drainDisplayEventQueue(); - return 1; // keep the callback + return 1; // keep the callback } static nsecs_t latestVsyncEvent(DisplayEventReceiver* receiver) { @@ -300,9 +209,9 @@ static nsecs_t latestVsyncEvent(DisplayEventReceiver* receiver) { for (ssize_t i = 0; i < n; i++) { const DisplayEventReceiver::Event& ev = buf[i]; switch (ev.header.type) { - case DisplayEventReceiver::DISPLAY_EVENT_VSYNC: - latest = ev.header.timestamp; - break; + case DisplayEventReceiver::DISPLAY_EVENT_VSYNC: + latest = ev.header.timestamp; + break; } } } @@ -321,7 +230,7 @@ void RenderThread::drainDisplayEventQueue() { ATRACE_NAME("queue mFrameCallbackTask"); mFrameCallbackTaskPending = true; nsecs_t runAt = (vsyncEvent + DISPATCH_FRAME_CALLBACKS_DELAY); - queueAt(mFrameCallbackTask, runAt); + queue().postAt(runAt, [this]() { dispatchFrameCallbacks(); }); } } } @@ -337,7 +246,8 @@ void RenderThread::dispatchFrameCallbacks() { // Assume one of them will probably animate again so preemptively // request the next vsync in case it occurs mid-frame requestVsync(); - for (std::set<IFrameCallback*>::iterator it = callbacks.begin(); it != callbacks.end(); it++) { + for (std::set<IFrameCallback*>::iterator it = callbacks.begin(); it != callbacks.end(); + it++) { (*it)->doFrame(); } } @@ -347,49 +257,25 @@ void RenderThread::requestVsync() { if (!mVsyncRequested) { mVsyncRequested = true; status_t status = mDisplayEventReceiver->requestNextVsync(); - LOG_ALWAYS_FATAL_IF(status != NO_ERROR, - "requestNextVsync failed with status: %d", status); + LOG_ALWAYS_FATAL_IF(status != NO_ERROR, "requestNextVsync failed with status: %d", status); } } bool RenderThread::threadLoop() { setpriority(PRIO_PROCESS, 0, PRIORITY_DISPLAY); + if (gOnStartHook) { + gOnStartHook(); + } initThreadLocals(); - int timeoutMillis = -1; - for (;;) { - int result = mLooper->pollOnce(timeoutMillis); - LOG_ALWAYS_FATAL_IF(result == Looper::POLL_ERROR, - "RenderThread Looper POLL_ERROR!"); - - nsecs_t nextWakeup; - { - FatVector<RenderTask*, 10> workQueue; - // Process our queue, if we have anything. By first acquiring - // all the pending events then processing them we avoid vsync - // starvation if more tasks are queued while we are processing tasks. - while (RenderTask* task = nextTask(&nextWakeup)) { - workQueue.push_back(task); - } - for (auto task : workQueue) { - task->run(); - // task may have deleted itself, do not reference it again - } - } - if (nextWakeup == LLONG_MAX) { - timeoutMillis = -1; - } else { - nsecs_t timeoutNanos = nextWakeup - systemTime(SYSTEM_TIME_MONOTONIC); - timeoutMillis = nanoseconds_to_milliseconds(timeoutNanos); - if (timeoutMillis < 0) { - timeoutMillis = 0; - } - } + while (true) { + waitForWork(); + processQueue(); if (mPendingRegistrationFrameCallbacks.size() && !mFrameCallbackTaskPending) { drainDisplayEventQueue(); - mFrameCallbacks.insert( - mPendingRegistrationFrameCallbacks.begin(), mPendingRegistrationFrameCallbacks.end()); + mFrameCallbacks.insert(mPendingRegistrationFrameCallbacks.begin(), + mPendingRegistrationFrameCallbacks.end()); mPendingRegistrationFrameCallbacks.clear(); requestVsync(); } @@ -406,46 +292,6 @@ bool RenderThread::threadLoop() { return false; } -void RenderThread::queue(RenderTask* task) { - AutoMutex _lock(mLock); - mQueue.queue(task); - if (mNextWakeup && task->mRunAt < mNextWakeup) { - mNextWakeup = 0; - mLooper->wake(); - } -} - -void RenderThread::queueAndWait(RenderTask* task) { - // These need to be local to the thread to avoid the Condition - // signaling the wrong thread. The easiest way to achieve that is to just - // make this on the stack, although that has a slight cost to it - Mutex mutex; - Condition condition; - SignalingRenderTask syncTask(task, &mutex, &condition); - - AutoMutex _lock(mutex); - queue(&syncTask); - while (!syncTask.hasRun()) { - condition.wait(mutex); - } -} - -void RenderThread::queueAtFront(RenderTask* task) { - AutoMutex _lock(mLock); - mQueue.queueAtFront(task); - mLooper->wake(); -} - -void RenderThread::queueAt(RenderTask* task, nsecs_t runAtNs) { - task->mRunAt = runAtNs; - queue(task); -} - -void RenderThread::remove(RenderTask* task) { - AutoMutex _lock(mLock); - mQueue.remove(task); -} - void RenderThread::postFrameCallback(IFrameCallback* callback) { mPendingRegistrationFrameCallbacks.insert(callback); } @@ -463,26 +309,6 @@ void RenderThread::pushBackFrameCallback(IFrameCallback* callback) { } } -RenderTask* RenderThread::nextTask(nsecs_t* nextWakeup) { - AutoMutex _lock(mLock); - RenderTask* next = mQueue.peek(); - if (!next) { - mNextWakeup = LLONG_MAX; - } else { - mNextWakeup = next->mRunAt; - // Most tasks won't be delayed, so avoid unnecessary systemTime() calls - if (next->mRunAt <= 0 || next->mRunAt <= systemTime(SYSTEM_TIME_MONOTONIC)) { - next = mQueue.next(); - } else { - next = nullptr; - } - } - if (nextWakeup) { - *nextWakeup = mNextWakeup; - } - return next; -} - sk_sp<Bitmap> RenderThread::allocateHardwareBitmap(SkBitmap& skBitmap) { auto renderType = Properties::getRenderPipelineType(); switch (renderType) { @@ -493,12 +319,16 @@ sk_sp<Bitmap> RenderThread::allocateHardwareBitmap(SkBitmap& skBitmap) { case RenderPipelineType::SkiaVulkan: return skiapipeline::SkiaVulkanPipeline::allocateHardwareBitmap(*this, skBitmap); default: - LOG_ALWAYS_FATAL("canvas context type %d not supported", (int32_t) renderType); + LOG_ALWAYS_FATAL("canvas context type %d not supported", (int32_t)renderType); break; } return nullptr; } +bool RenderThread::isCurrent() { + return gettid() == getInstance().getTid(); +} + } /* namespace renderthread */ } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h index bef47b3e27c5..3aa548773b3b 100644 --- a/libs/hwui/renderthread/RenderThread.h +++ b/libs/hwui/renderthread/RenderThread.h @@ -22,15 +22,18 @@ #include "../JankTracker.h" #include "CacheManager.h" #include "TimeLord.h" +#include "thread/ThreadBase.h" #include <GrContext.h> -#include <cutils/compiler.h> #include <SkBitmap.h> +#include <cutils/compiler.h> #include <ui/DisplayInfo.h> #include <utils/Looper.h> #include <utils/Thread.h> +#include <thread/ThreadBase.h> #include <memory> +#include <mutex> #include <set> namespace android { @@ -47,26 +50,10 @@ class TestUtils; namespace renderthread { class CanvasContext; -class DispatchFrameCallbacks; class EglManager; class RenderProxy; class VulkanManager; -class TaskQueue { -public: - TaskQueue(); - - RenderTask* next(); - void queue(RenderTask* task); - void queueAtFront(RenderTask* task); - RenderTask* peek(); - void remove(RenderTask* task); - -private: - RenderTask* mHead; - RenderTask* mTail; -}; - // Mimics android.view.Choreographer.FrameCallback class IFrameCallback { public: @@ -76,16 +63,14 @@ protected: ~IFrameCallback() {} }; -class ANDROID_API RenderThread : public Thread { +class RenderThread : private ThreadBase { PREVENT_COPY_AND_ASSIGN(RenderThread); + public: - // RenderThread takes complete ownership of tasks that are queued - // and will delete them after they are run - ANDROID_API void queue(RenderTask* task); - ANDROID_API void queueAndWait(RenderTask* task); - ANDROID_API void queueAtFront(RenderTask* task); - void queueAt(RenderTask* task, nsecs_t runAtNs); - void remove(RenderTask* task); + // Sets a callback that fires before any RenderThread setup has occured. + ANDROID_API static void setOnStartHook(void (*onStartHook)()); + + WorkQueue& queue() { return ThreadBase::queue(); } // Mimics android.view.Choreographer void postFrameCallback(IFrameCallback* callback); @@ -103,7 +88,7 @@ public: const DisplayInfo& mainDisplayInfo() { return mDisplayInfo; } GrContext* getGrContext() const { return mGrContext.get(); } - void setGrContext(GrContext* cxt); + void setGrContext(sk_sp<GrContext> cxt); CacheManager& cacheManager() { return *mCacheManager; } VulkanManager& vulkanManager() { return *mVkManager; } @@ -111,6 +96,14 @@ public: sk_sp<Bitmap> allocateHardwareBitmap(SkBitmap& skBitmap); void dumpGraphicsMemory(int fd); + /** + * isCurrent provides a way to query, if the caller is running on + * the render thread. + * + * @return true only if isCurrent is invoked from the render thread. + */ + static bool isCurrent(); + protected: virtual bool threadLoop() override; @@ -132,17 +125,6 @@ private: void dispatchFrameCallbacks(); void requestVsync(); - // Returns the next task to be run. If this returns NULL nextWakeup is set - // to the time to requery for the nextTask to run. mNextWakeup is also - // set to this time - RenderTask* nextTask(nsecs_t* nextWakeup); - - sp<Looper> mLooper; - Mutex mLock; - - nsecs_t mNextWakeup; - TaskQueue mQueue; - DisplayInfo mDisplayInfo; DisplayEventReceiver* mDisplayEventReceiver; @@ -154,7 +136,6 @@ private: // the previous one std::set<IFrameCallback*> mPendingRegistrationFrameCallbacks; bool mFrameCallbackTaskPending; - DispatchFrameCallbacks* mFrameCallbackTask; TimeLord mTimeLord; RenderState* mRenderState; diff --git a/libs/hwui/renderthread/TimeLord.cpp b/libs/hwui/renderthread/TimeLord.cpp index 6c2575f699bb..b82c5d159756 100644 --- a/libs/hwui/renderthread/TimeLord.cpp +++ b/libs/hwui/renderthread/TimeLord.cpp @@ -19,10 +19,7 @@ namespace android { namespace uirenderer { namespace renderthread { -TimeLord::TimeLord() - : mFrameIntervalNanos(milliseconds_to_nanoseconds(16)) - , mFrameTimeNanos(0) { -} +TimeLord::TimeLord() : mFrameIntervalNanos(milliseconds_to_nanoseconds(16)), mFrameTimeNanos(0) {} bool TimeLord::vsyncReceived(nsecs_t vsync) { if (vsync > mFrameTimeNanos) { diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp index 2195143658d2..9d246ffc9a4a 100644 --- a/libs/hwui/renderthread/VulkanManager.cpp +++ b/libs/hwui/renderthread/VulkanManager.cpp @@ -31,11 +31,10 @@ namespace android { namespace uirenderer { namespace renderthread { -#define GET_PROC(F) m ## F = (PFN_vk ## F) vkGetInstanceProcAddr(instance, "vk" #F) -#define GET_DEV_PROC(F) m ## F = (PFN_vk ## F) vkGetDeviceProcAddr(device, "vk" #F) +#define GET_PROC(F) m##F = (PFN_vk##F)vkGetInstanceProcAddr(instance, "vk" #F) +#define GET_DEV_PROC(F) m##F = (PFN_vk##F)vkGetDeviceProcAddr(device, "vk" #F) -VulkanManager::VulkanManager(RenderThread& thread) : mRenderThread(thread) { -} +VulkanManager::VulkanManager(RenderThread& thread) : mRenderThread(thread) {} void VulkanManager::destroy() { if (!hasVkContext()) return; @@ -51,12 +50,15 @@ void VulkanManager::destroy() { } void VulkanManager::initialize() { - if (hasVkContext()) { return; } + if (hasVkContext()) { + return; + } auto canPresent = [](VkInstance, VkPhysicalDevice, uint32_t) { return true; }; mBackendContext.reset(GrVkBackendContext::Create(vkGetInstanceProcAddr, vkGetDeviceProcAddr, - &mPresentQueueIndex, canPresent)); + &mPresentQueueIndex, canPresent)); + LOG_ALWAYS_FATAL_IF(!mBackendContext.get()); // Get all the addresses of needed vulkan functions VkInstance instance = mBackendContext->fInstance; @@ -99,15 +101,19 @@ void VulkanManager::initialize() { // this needs to be on the render queue commandPoolInfo.queueFamilyIndex = mBackendContext->fGraphicsQueueIndex; commandPoolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; - SkDEBUGCODE(VkResult res =) mCreateCommandPool(mBackendContext->fDevice, - &commandPoolInfo, nullptr, &mCommandPool); + SkDEBUGCODE(VkResult res =) mCreateCommandPool(mBackendContext->fDevice, &commandPoolInfo, + nullptr, &mCommandPool); SkASSERT(VK_SUCCESS == res); } mGetDeviceQueue(mBackendContext->fDevice, mPresentQueueIndex, 0, &mPresentQueue); - mRenderThread.setGrContext(GrContext::Create(kVulkan_GrBackend, - (GrBackendContext) mBackendContext.get())); + GrContextOptions options; + options.fDisableDistanceFieldPaths = true; + mRenderThread.cacheManager().configureContext(&options); + sk_sp<GrContext> grContext(GrContext::MakeVulkan(mBackendContext, options)); + LOG_ALWAYS_FATAL_IF(!grContext.get()); + mRenderThread.setGrContext(grContext); DeviceInfo::initialize(mRenderThread.getGrContext()->caps()->maxRenderTargetSize()); if (Properties::enablePartialUpdates && Properties::useBufferAge) { @@ -127,13 +133,13 @@ VulkanSurface::BackbufferInfo* VulkanManager::getAvailableBackbuffer(VulkanSurfa surface->mCurrentBackbufferIndex = 0; } - VulkanSurface::BackbufferInfo* backbuffer = surface->mBackbuffers + - surface->mCurrentBackbufferIndex; + VulkanSurface::BackbufferInfo* backbuffer = + surface->mBackbuffers + surface->mCurrentBackbufferIndex; // Before we reuse a backbuffer, make sure its fences have all signaled so that we can safely // reuse its commands buffers. - VkResult res = mWaitForFences(mBackendContext->fDevice, 2, backbuffer->mUsageFences, - true, UINT64_MAX); + VkResult res = + mWaitForFences(mBackendContext->fDevice, 2, backbuffer->mUsageFences, true, UINT64_MAX); if (res != VK_SUCCESS) { return nullptr; } @@ -141,7 +147,6 @@ VulkanSurface::BackbufferInfo* VulkanManager::getAvailableBackbuffer(VulkanSurfa return backbuffer; } - SkSurface* VulkanManager::getBackbufferSurface(VulkanSurface* surface) { VulkanSurface::BackbufferInfo* backbuffer = getAvailableBackbuffer(surface); SkASSERT(backbuffer); @@ -154,7 +159,8 @@ SkSurface* VulkanManager::getBackbufferSurface(VulkanSurface* surface) { // The acquire will signal the attached mAcquireSemaphore. We use this to know the image has // finished presenting and that it is safe to begin sending new commands to the returned image. res = mAcquireNextImageKHR(mBackendContext->fDevice, surface->mSwapchain, UINT64_MAX, - backbuffer->mAcquireSemaphore, VK_NULL_HANDLE, &backbuffer->mImageIndex); + backbuffer->mAcquireSemaphore, VK_NULL_HANDLE, + &backbuffer->mImageIndex); if (VK_ERROR_SURFACE_LOST_KHR == res) { // need to figure out how to create a new vkSurface without the platformData* @@ -172,7 +178,8 @@ SkSurface* VulkanManager::getBackbufferSurface(VulkanSurface* surface) { // acquire the image res = mAcquireNextImageKHR(mBackendContext->fDevice, surface->mSwapchain, UINT64_MAX, - backbuffer->mAcquireSemaphore, VK_NULL_HANDLE, &backbuffer->mImageIndex); + backbuffer->mAcquireSemaphore, VK_NULL_HANDLE, + &backbuffer->mImageIndex); if (VK_SUCCESS != res) { return nullptr; @@ -182,25 +189,25 @@ SkSurface* VulkanManager::getBackbufferSurface(VulkanSurface* surface) { // set up layout transfer from initial to color attachment VkImageLayout layout = surface->mImageInfos[backbuffer->mImageIndex].mImageLayout; SkASSERT(VK_IMAGE_LAYOUT_UNDEFINED == layout || VK_IMAGE_LAYOUT_PRESENT_SRC_KHR == layout); - VkPipelineStageFlags srcStageMask = (VK_IMAGE_LAYOUT_UNDEFINED == layout) ? - VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT : - VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + VkPipelineStageFlags srcStageMask = (VK_IMAGE_LAYOUT_UNDEFINED == layout) + ? VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT + : VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; VkPipelineStageFlags dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - VkAccessFlags srcAccessMask = (VK_IMAGE_LAYOUT_UNDEFINED == layout) ? - 0 : VK_ACCESS_MEMORY_READ_BIT; + VkAccessFlags srcAccessMask = + (VK_IMAGE_LAYOUT_UNDEFINED == layout) ? 0 : VK_ACCESS_MEMORY_READ_BIT; VkAccessFlags dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; VkImageMemoryBarrier imageMemoryBarrier = { - VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, // sType - NULL, // pNext - srcAccessMask, // outputMask - dstAccessMask, // inputMask - layout, // oldLayout - VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, // newLayout - mPresentQueueIndex, // srcQueueFamilyIndex - mBackendContext->fGraphicsQueueIndex, // dstQueueFamilyIndex - surface->mImages[backbuffer->mImageIndex], // image - { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 } // subresourceRange + VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, // sType + NULL, // pNext + srcAccessMask, // outputMask + dstAccessMask, // inputMask + layout, // oldLayout + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, // newLayout + mPresentQueueIndex, // srcQueueFamilyIndex + mBackendContext->fGraphicsQueueIndex, // dstQueueFamilyIndex + surface->mImages[backbuffer->mImageIndex], // image + {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1} // subresourceRange }; mResetCommandBuffer(backbuffer->mTransitionCmdBuffers[0], 0); @@ -210,8 +217,8 @@ SkSurface* VulkanManager::getBackbufferSurface(VulkanSurface* surface) { info.flags = 0; mBeginCommandBuffer(backbuffer->mTransitionCmdBuffers[0], &info); - mCmdPipelineBarrier(backbuffer->mTransitionCmdBuffers[0], srcStageMask, dstStageMask, 0, - 0, nullptr, 0, nullptr, 1, &imageMemoryBarrier); + mCmdPipelineBarrier(backbuffer->mTransitionCmdBuffers[0], srcStageMask, dstStageMask, 0, 0, + nullptr, 0, nullptr, 1, &imageMemoryBarrier); mEndCommandBuffer(backbuffer->mTransitionCmdBuffers[0]); @@ -235,7 +242,7 @@ SkSurface* VulkanManager::getBackbufferSurface(VulkanSurface* surface) { GrVkImageInfo* imageInfo; sk_sp<SkSurface> skSurface = surface->mImageInfos[backbuffer->mImageIndex].mSurface; skSurface->getRenderTargetHandle((GrBackendObject*)&imageInfo, - SkSurface::kFlushRead_BackendHandleAccess); + SkSurface::kFlushRead_BackendHandleAccess); imageInfo->updateImageLayout(VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); surface->mBackbuffer = std::move(skSurface); @@ -246,14 +253,14 @@ void VulkanManager::destroyBuffers(VulkanSurface* surface) { if (surface->mBackbuffers) { for (uint32_t i = 0; i < surface->mImageCount + 1; ++i) { mWaitForFences(mBackendContext->fDevice, 2, surface->mBackbuffers[i].mUsageFences, true, - UINT64_MAX); + UINT64_MAX); surface->mBackbuffers[i].mImageIndex = -1; mDestroySemaphore(mBackendContext->fDevice, surface->mBackbuffers[i].mAcquireSemaphore, - nullptr); + nullptr); mDestroySemaphore(mBackendContext->fDevice, surface->mBackbuffers[i].mRenderSemaphore, - nullptr); + nullptr); mFreeCommandBuffers(mBackendContext->fDevice, mCommandPool, 2, - surface->mBackbuffers[i].mTransitionCmdBuffers); + surface->mBackbuffers[i].mTransitionCmdBuffers); mDestroyFence(mBackendContext->fDevice, surface->mBackbuffers[i].mUsageFences[0], 0); mDestroyFence(mBackendContext->fDevice, surface->mBackbuffers[i].mUsageFences[1], 0); } @@ -290,11 +297,11 @@ void VulkanManager::destroySurface(VulkanSurface* surface) { void VulkanManager::createBuffers(VulkanSurface* surface, VkFormat format, VkExtent2D extent) { mGetSwapchainImagesKHR(mBackendContext->fDevice, surface->mSwapchain, &surface->mImageCount, - nullptr); + nullptr); SkASSERT(surface->mImageCount); surface->mImages = new VkImage[surface->mImageCount]; - mGetSwapchainImagesKHR(mBackendContext->fDevice, surface->mSwapchain, - &surface->mImageCount, surface->mImages); + mGetSwapchainImagesKHR(mBackendContext->fDevice, surface->mSwapchain, &surface->mImageCount, + surface->mImages); SkSurfaceProps props(0, kUnknown_SkPixelGeometry); @@ -303,7 +310,7 @@ void VulkanManager::createBuffers(VulkanSurface* surface, VkFormat format, VkExt for (uint32_t i = 0; i < surface->mImageCount; ++i) { GrVkImageInfo info; info.fImage = surface->mImages[i]; - info.fAlloc = { VK_NULL_HANDLE, 0, 0, 0 }; + info.fAlloc = {VK_NULL_HANDLE, 0, 0, 0}; info.fImageLayout = VK_IMAGE_LAYOUT_UNDEFINED; info.fImageTiling = VK_IMAGE_TILING_OPTIMAL; info.fFormat = format; @@ -312,8 +319,8 @@ void VulkanManager::createBuffers(VulkanSurface* surface, VkFormat format, VkExt GrBackendRenderTarget backendRT(extent.width, extent.height, 0, 0, info); VulkanSurface::ImageInfo& imageInfo = surface->mImageInfos[i]; - imageInfo.mSurface = SkSurface::MakeFromBackendRenderTarget(mRenderThread.getGrContext(), - backendRT, kTopLeft_GrSurfaceOrigin, nullptr, &props); + imageInfo.mSurface = SkSurface::MakeFromBackendRenderTarget( + mRenderThread.getGrContext(), backendRT, kTopLeft_GrSurfaceOrigin, nullptr, &props); } SkASSERT(mCommandPool != VK_NULL_HANDLE); @@ -343,16 +350,16 @@ void VulkanManager::createBuffers(VulkanSurface* surface, VkFormat format, VkExt for (uint32_t i = 0; i < surface->mImageCount + 1; ++i) { SkDEBUGCODE(VkResult res); surface->mBackbuffers[i].mImageIndex = -1; - SkDEBUGCODE(res = ) mCreateSemaphore(mBackendContext->fDevice, &semaphoreInfo, nullptr, - &surface->mBackbuffers[i].mAcquireSemaphore); - SkDEBUGCODE(res = ) mCreateSemaphore(mBackendContext->fDevice, &semaphoreInfo, nullptr, - &surface->mBackbuffers[i].mRenderSemaphore); - SkDEBUGCODE(res = ) mAllocateCommandBuffers(mBackendContext->fDevice, &commandBuffersInfo, - surface->mBackbuffers[i].mTransitionCmdBuffers); - SkDEBUGCODE(res = ) mCreateFence(mBackendContext->fDevice, &fenceInfo, nullptr, - &surface->mBackbuffers[i].mUsageFences[0]); - SkDEBUGCODE(res = ) mCreateFence(mBackendContext->fDevice, &fenceInfo, nullptr, - &surface->mBackbuffers[i].mUsageFences[1]); + SkDEBUGCODE(res =) mCreateSemaphore(mBackendContext->fDevice, &semaphoreInfo, nullptr, + &surface->mBackbuffers[i].mAcquireSemaphore); + SkDEBUGCODE(res =) mCreateSemaphore(mBackendContext->fDevice, &semaphoreInfo, nullptr, + &surface->mBackbuffers[i].mRenderSemaphore); + SkDEBUGCODE(res =) mAllocateCommandBuffers(mBackendContext->fDevice, &commandBuffersInfo, + surface->mBackbuffers[i].mTransitionCmdBuffers); + SkDEBUGCODE(res =) mCreateFence(mBackendContext->fDevice, &fenceInfo, nullptr, + &surface->mBackbuffers[i].mUsageFences[0]); + SkDEBUGCODE(res =) mCreateFence(mBackendContext->fDevice, &fenceInfo, nullptr, + &surface->mBackbuffers[i].mUsageFences[1]); SkASSERT(VK_SUCCESS == res); } surface->mCurrentBackbufferIndex = surface->mImageCount; @@ -362,35 +369,36 @@ bool VulkanManager::createSwapchain(VulkanSurface* surface) { // check for capabilities VkSurfaceCapabilitiesKHR caps; VkResult res = mGetPhysicalDeviceSurfaceCapabilitiesKHR(mBackendContext->fPhysicalDevice, - surface->mVkSurface, &caps); + surface->mVkSurface, &caps); if (VK_SUCCESS != res) { return false; } uint32_t surfaceFormatCount; res = mGetPhysicalDeviceSurfaceFormatsKHR(mBackendContext->fPhysicalDevice, surface->mVkSurface, - &surfaceFormatCount, nullptr); + &surfaceFormatCount, nullptr); if (VK_SUCCESS != res) { return false; } FatVector<VkSurfaceFormatKHR, 4> surfaceFormats(surfaceFormatCount); res = mGetPhysicalDeviceSurfaceFormatsKHR(mBackendContext->fPhysicalDevice, surface->mVkSurface, - &surfaceFormatCount, surfaceFormats.data()); + &surfaceFormatCount, surfaceFormats.data()); if (VK_SUCCESS != res) { return false; } uint32_t presentModeCount; res = mGetPhysicalDeviceSurfacePresentModesKHR(mBackendContext->fPhysicalDevice, - surface->mVkSurface, &presentModeCount, nullptr); + surface->mVkSurface, &presentModeCount, nullptr); if (VK_SUCCESS != res) { return false; } FatVector<VkPresentModeKHR, VK_PRESENT_MODE_RANGE_SIZE_KHR> presentModes(presentModeCount); res = mGetPhysicalDeviceSurfacePresentModesKHR(mBackendContext->fPhysicalDevice, - surface->mVkSurface, &presentModeCount, presentModes.data()); + surface->mVkSurface, &presentModeCount, + presentModes.data()); if (VK_SUCCESS != res) { return false; } @@ -420,12 +428,12 @@ bool VulkanManager::createSwapchain(VulkanSurface* surface) { VK_IMAGE_USAGE_TRANSFER_DST_BIT; SkASSERT((caps.supportedUsageFlags & usageFlags) == usageFlags); SkASSERT(caps.supportedTransforms & caps.currentTransform); - SkASSERT(caps.supportedCompositeAlpha & (VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR | - VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR)); + SkASSERT(caps.supportedCompositeAlpha & + (VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR | VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR)); VkCompositeAlphaFlagBitsKHR composite_alpha = - (caps.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR) ? - VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR : - VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; + (caps.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR) + ? VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR + : VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; // Pick our surface format. For now, just make sure it matches our sRGB request: VkFormat surfaceFormat = VK_FORMAT_UNDEFINED; @@ -470,7 +478,7 @@ bool VulkanManager::createSwapchain(VulkanSurface* surface) { swapchainCreateInfo.imageArrayLayers = 1; swapchainCreateInfo.imageUsage = usageFlags; - uint32_t queueFamilies[] = { mBackendContext->fGraphicsQueueIndex, mPresentQueueIndex }; + uint32_t queueFamilies[] = {mBackendContext->fGraphicsQueueIndex, mPresentQueueIndex}; if (mBackendContext->fGraphicsQueueIndex != mPresentQueueIndex) { swapchainCreateInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT; swapchainCreateInfo.queueFamilyIndexCount = 2; @@ -488,7 +496,7 @@ bool VulkanManager::createSwapchain(VulkanSurface* surface) { swapchainCreateInfo.oldSwapchain = surface->mSwapchain; res = mCreateSwapchainKHR(mBackendContext->fDevice, &swapchainCreateInfo, nullptr, - &surface->mSwapchain); + &surface->mSwapchain); if (VK_SUCCESS != res) { return false; } @@ -507,7 +515,6 @@ bool VulkanManager::createSwapchain(VulkanSurface* surface) { return true; } - VulkanSurface* VulkanManager::createSurface(ANativeWindow* window) { initialize(); @@ -524,21 +531,20 @@ VulkanSurface* VulkanManager::createSurface(ANativeWindow* window) { surfaceCreateInfo.flags = 0; surfaceCreateInfo.window = window; - VkResult res = mCreateAndroidSurfaceKHR(mBackendContext->fInstance, &surfaceCreateInfo, - nullptr, &surface->mVkSurface); + VkResult res = mCreateAndroidSurfaceKHR(mBackendContext->fInstance, &surfaceCreateInfo, nullptr, + &surface->mVkSurface); if (VK_SUCCESS != res) { delete surface; return nullptr; } -SkDEBUGCODE( - VkBool32 supported; - res = mGetPhysicalDeviceSurfaceSupportKHR(mBackendContext->fPhysicalDevice, - mPresentQueueIndex, surface->mVkSurface, &supported); - // All physical devices and queue families on Android must be capable of presentation with any - // native window. - SkASSERT(VK_SUCCESS == res && supported); -); + SkDEBUGCODE(VkBool32 supported; res = mGetPhysicalDeviceSurfaceSupportKHR( + mBackendContext->fPhysicalDevice, mPresentQueueIndex, + surface->mVkSurface, &supported); + // All physical devices and queue families on Android must be capable of + // presentation with any + // native window. + SkASSERT(VK_SUCCESS == res && supported);); if (!createSwapchain(surface)) { destroySurface(surface); @@ -573,11 +579,9 @@ static VkAccessFlags layoutToSrcAccessMask(const VkImageLayout layout) { VkAccessFlags flags = 0; if (VK_IMAGE_LAYOUT_GENERAL == layout) { flags = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | - VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | - VK_ACCESS_TRANSFER_WRITE_BIT | - VK_ACCESS_TRANSFER_READ_BIT | - VK_ACCESS_SHADER_READ_BIT | - VK_ACCESS_HOST_WRITE_BIT | VK_ACCESS_HOST_READ_BIT; + VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT | + VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_HOST_WRITE_BIT | + VK_ACCESS_HOST_READ_BIT; } else if (VK_IMAGE_LAYOUT_PREINITIALIZED == layout) { flags = VK_ACCESS_HOST_WRITE_BIT; } else if (VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL == layout) { @@ -600,12 +604,13 @@ void VulkanManager::swapBuffers(VulkanSurface* surface) { mDeviceWaitIdle(mBackendContext->fDevice); } - VulkanSurface::BackbufferInfo* backbuffer = surface->mBackbuffers + - surface->mCurrentBackbufferIndex; + SkASSERT(surface->mBackbuffers); + VulkanSurface::BackbufferInfo* backbuffer = + surface->mBackbuffers + surface->mCurrentBackbufferIndex; GrVkImageInfo* imageInfo; SkSurface* skSurface = surface->mImageInfos[backbuffer->mImageIndex].mSurface.get(); skSurface->getRenderTargetHandle((GrBackendObject*)&imageInfo, - SkSurface::kFlushRead_BackendHandleAccess); + SkSurface::kFlushRead_BackendHandleAccess); // Check to make sure we never change the actually wrapped image SkASSERT(imageInfo->fImage == surface->mImages[backbuffer->mImageIndex]); @@ -618,16 +623,16 @@ void VulkanManager::swapBuffers(VulkanSurface* surface) { VkAccessFlags dstAccessMask = VK_ACCESS_MEMORY_READ_BIT; VkImageMemoryBarrier imageMemoryBarrier = { - VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, // sType - NULL, // pNext - srcAccessMask, // outputMask - dstAccessMask, // inputMask - layout, // oldLayout - VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, // newLayout - mBackendContext->fGraphicsQueueIndex, // srcQueueFamilyIndex - mPresentQueueIndex, // dstQueueFamilyIndex - surface->mImages[backbuffer->mImageIndex], // image - { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 } // subresourceRange + VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, // sType + NULL, // pNext + srcAccessMask, // outputMask + dstAccessMask, // inputMask + layout, // oldLayout + VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, // newLayout + mBackendContext->fGraphicsQueueIndex, // srcQueueFamilyIndex + mPresentQueueIndex, // dstQueueFamilyIndex + surface->mImages[backbuffer->mImageIndex], // image + {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1} // subresourceRange }; mResetCommandBuffer(backbuffer->mTransitionCmdBuffers[1], 0); @@ -636,8 +641,8 @@ void VulkanManager::swapBuffers(VulkanSurface* surface) { info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; info.flags = 0; mBeginCommandBuffer(backbuffer->mTransitionCmdBuffers[1], &info); - mCmdPipelineBarrier(backbuffer->mTransitionCmdBuffers[1], srcStageMask, dstStageMask, 0, - 0, nullptr, 0, nullptr, 1, &imageMemoryBarrier); + mCmdPipelineBarrier(backbuffer->mTransitionCmdBuffers[1], srcStageMask, dstStageMask, 0, 0, + nullptr, 0, nullptr, 1, &imageMemoryBarrier); mEndCommandBuffer(backbuffer->mTransitionCmdBuffers[1]); surface->mImageInfos[backbuffer->mImageIndex].mImageLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; @@ -661,16 +666,15 @@ void VulkanManager::swapBuffers(VulkanSurface* surface) { // Submit present operation to present queue. We use a semaphore here to make sure all rendering // to the image is complete and that the layout has been change to present on the graphics // queue. - const VkPresentInfoKHR presentInfo = - { - VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, // sType - NULL, // pNext - 1, // waitSemaphoreCount - &backbuffer->mRenderSemaphore, // pWaitSemaphores - 1, // swapchainCount - &surface->mSwapchain, // pSwapchains - &backbuffer->mImageIndex, // pImageIndices - NULL // pResults + const VkPresentInfoKHR presentInfo = { + VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, // sType + NULL, // pNext + 1, // waitSemaphoreCount + &backbuffer->mRenderSemaphore, // pWaitSemaphores + 1, // swapchainCount + &surface->mSwapchain, // pSwapchains + &backbuffer->mImageIndex, // pImageIndices + NULL // pResults }; mQueuePresentKHR(mPresentQueue, &presentInfo); @@ -682,10 +686,11 @@ void VulkanManager::swapBuffers(VulkanSurface* surface) { } int VulkanManager::getAge(VulkanSurface* surface) { - VulkanSurface::BackbufferInfo* backbuffer = surface->mBackbuffers + - surface->mCurrentBackbufferIndex; - if (mSwapBehavior == SwapBehavior::Discard - || surface->mImageInfos[backbuffer->mImageIndex].mInvalid) { + SkASSERT(surface->mBackbuffers); + VulkanSurface::BackbufferInfo* backbuffer = + surface->mBackbuffers + surface->mCurrentBackbufferIndex; + if (mSwapBehavior == SwapBehavior::Discard || + surface->mImageInfos[backbuffer->mImageIndex].mInvalid) { return 0; } uint16_t lastUsed = surface->mImageInfos[backbuffer->mImageIndex].mLastUsed; diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h index d225b3fc4ec0..c319c9ec209f 100644 --- a/libs/hwui/renderthread/VulkanManager.h +++ b/libs/hwui/renderthread/VulkanManager.h @@ -37,13 +37,14 @@ public: private: friend class VulkanManager; struct BackbufferInfo { - uint32_t mImageIndex; // image this is associated with - VkSemaphore mAcquireSemaphore; // we signal on this for acquisition of image - VkSemaphore mRenderSemaphore; // we wait on this for rendering to be done - VkCommandBuffer mTransitionCmdBuffers[2]; // to transition layout between present and render + uint32_t mImageIndex; // image this is associated with + VkSemaphore mAcquireSemaphore; // we signal on this for acquisition of image + VkSemaphore mRenderSemaphore; // we wait on this for rendering to be done + VkCommandBuffer + mTransitionCmdBuffers[2]; // to transition layout between present and render // We use these fences to make sure the above Command buffers have finished their work // before attempting to reuse them or destroy them. - VkFence mUsageFences[2]; + VkFence mUsageFences[2]; }; struct ImageInfo { @@ -58,11 +59,11 @@ private: VkSurfaceKHR mVkSurface = VK_NULL_HANDLE; VkSwapchainKHR mSwapchain = VK_NULL_HANDLE; - BackbufferInfo* mBackbuffers; + BackbufferInfo* mBackbuffers = nullptr; uint32_t mCurrentBackbufferIndex; uint32_t mImageCount; - VkImage* mImages; + VkImage* mImages = nullptr; ImageInfo* mImageInfos; uint16_t mCurrentTime = 0; }; @@ -118,11 +119,16 @@ private: VulkanSurface::BackbufferInfo* getAvailableBackbuffer(VulkanSurface* surface); // simple wrapper class that exists only to initialize a pointer to NULL - template <typename FNPTR_TYPE> class VkPtr { + template <typename FNPTR_TYPE> + class VkPtr { public: VkPtr() : fPtr(NULL) {} - VkPtr operator=(FNPTR_TYPE ptr) { fPtr = ptr; return *this; } + VkPtr operator=(FNPTR_TYPE ptr) { + fPtr = ptr; + return *this; + } operator FNPTR_TYPE() const { return fPtr; } + private: FNPTR_TYPE fPtr; }; @@ -183,4 +189,3 @@ private: } /* namespace android */ #endif /* VULKANMANAGER_H */ - diff --git a/libs/hwui/service/GraphicsStatsService.cpp b/libs/hwui/service/GraphicsStatsService.cpp index f7a90b0a65ec..e0303a8a62d5 100644 --- a/libs/hwui/service/GraphicsStatsService.cpp +++ b/libs/hwui/service/GraphicsStatsService.cpp @@ -25,10 +25,10 @@ #include <errno.h> #include <fcntl.h> #include <inttypes.h> +#include <sys/mman.h> #include <sys/stat.h> #include <sys/types.h> #include <unistd.h> -#include <sys/mman.h> namespace android { namespace uirenderer { @@ -42,8 +42,8 @@ static_assert(sizeof(sCurrentFileVersion) == sHeaderSize, "Header size is wrong" constexpr int sHistogramSize = ProfileData::HistogramSize(); static bool mergeProfileDataIntoProto(service::GraphicsStatsProto* proto, - const std::string& package, int versionCode, int64_t startTime, int64_t endTime, - const ProfileData* data); + const std::string& package, int64_t versionCode, + int64_t startTime, int64_t endTime, const ProfileData* data); static void dumpAsTextToFd(service::GraphicsStatsProto* proto, int outFd); class FileDescriptor { @@ -57,6 +57,7 @@ public: } bool valid() { return mFd != -1; } operator int() { return mFd; } + private: int mFd; }; @@ -68,21 +69,13 @@ public: int GetErrno() { return mCopyAdapter.mErrno; } - virtual bool Next(void** data, int* size) override { - return mImpl.Next(data, size); - } + virtual bool Next(void** data, int* size) override { return mImpl.Next(data, size); } - virtual void BackUp(int count) override { - mImpl.BackUp(count); - } + virtual void BackUp(int count) override { mImpl.BackUp(count); } - virtual int64 ByteCount() const override { - return mImpl.ByteCount(); - } + virtual int64 ByteCount() const override { return mImpl.ByteCount(); } - bool Flush() { - return mImpl.Flush(); - } + bool Flush() { return mImpl.Flush(); } private: struct FDAdapter : public io::CopyingOutputStream { @@ -95,7 +88,7 @@ private: virtual bool Write(const void* buffer, int size) override { int ret; while (size) { - ret = TEMP_FAILURE_RETRY( write(mFd, buffer, size) ); + ret = TEMP_FAILURE_RETRY(write(mFd, buffer, size)); if (ret <= 0) { mErrno = errno; return false; @@ -110,8 +103,8 @@ private: io::CopyingOutputStreamAdaptor mImpl; }; -bool GraphicsStatsService::parseFromFile(const std::string& path, service::GraphicsStatsProto* output) { - +bool GraphicsStatsService::parseFromFile(const std::string& path, + service::GraphicsStatsProto* output) { FileDescriptor fd{open(path.c_str(), O_RDONLY)}; if (!fd.valid()) { int err = errno; @@ -129,7 +122,7 @@ bool GraphicsStatsService::parseFromFile(const std::string& path, service::Graph // we get an unexpected error if (err != ENOENT) { ALOGW("Failed to fstat '%s', errno=%d (%s) (st_size %d)", path.c_str(), err, - strerror(err), (int) sb.st_size); + strerror(err), (int)sb.st_size); } return false; } @@ -154,14 +147,15 @@ bool GraphicsStatsService::parseFromFile(const std::string& path, service::Graph io::ArrayInputStream input{data, dataSize}; bool success = output->ParseFromZeroCopyStream(&input); if (!success) { - ALOGW("Parse failed on '%s' error='%s'", - path.c_str(), output->InitializationErrorString().c_str()); + ALOGW("Parse failed on '%s' error='%s'", path.c_str(), + output->InitializationErrorString().c_str()); } return success; } bool mergeProfileDataIntoProto(service::GraphicsStatsProto* proto, const std::string& package, - int versionCode, int64_t startTime, int64_t endTime, const ProfileData* data) { + int64_t versionCode, int64_t startTime, int64_t endTime, + const ProfileData* data) { if (proto->stats_start() == 0 || proto->stats_start() > startTime) { proto->set_stats_start(startTime); } @@ -173,24 +167,23 @@ bool mergeProfileDataIntoProto(service::GraphicsStatsProto* proto, const std::st auto summary = proto->mutable_summary(); summary->set_total_frames(summary->total_frames() + data->totalFrameCount()); summary->set_janky_frames(summary->janky_frames() + data->jankFrameCount()); - summary->set_missed_vsync_count( - summary->missed_vsync_count() + data->jankTypeCount(kMissedVsync)); - summary->set_high_input_latency_count( - summary->high_input_latency_count() + data->jankTypeCount(kHighInputLatency)); - summary->set_slow_ui_thread_count( - summary->slow_ui_thread_count() + data->jankTypeCount(kSlowUI)); - summary->set_slow_bitmap_upload_count( - summary->slow_bitmap_upload_count() + data->jankTypeCount(kSlowSync)); - summary->set_slow_draw_count( - summary->slow_draw_count() + data->jankTypeCount(kSlowRT)); + summary->set_missed_vsync_count(summary->missed_vsync_count() + + data->jankTypeCount(kMissedVsync)); + summary->set_high_input_latency_count(summary->high_input_latency_count() + + data->jankTypeCount(kHighInputLatency)); + summary->set_slow_ui_thread_count(summary->slow_ui_thread_count() + + data->jankTypeCount(kSlowUI)); + summary->set_slow_bitmap_upload_count(summary->slow_bitmap_upload_count() + + data->jankTypeCount(kSlowSync)); + summary->set_slow_draw_count(summary->slow_draw_count() + data->jankTypeCount(kSlowRT)); bool creatingHistogram = false; if (proto->histogram_size() == 0) { proto->mutable_histogram()->Reserve(sHistogramSize); creatingHistogram = true; } else if (proto->histogram_size() != sHistogramSize) { - ALOGE("Histogram size mismatch, proto is %d expected %d", - proto->histogram_size(), sHistogramSize); + ALOGE("Histogram size mismatch, proto is %d expected %d", proto->histogram_size(), + sHistogramSize); return false; } int index = 0; @@ -205,7 +198,8 @@ bool mergeProfileDataIntoProto(service::GraphicsStatsProto* proto, const std::st } else { bucket = proto->mutable_histogram(index); if (bucket->render_millis() != static_cast<int32_t>(entry.renderTimeMs)) { - ALOGW("Frame time mistmatch %d vs. %u", bucket->render_millis(), entry.renderTimeMs); + ALOGW("Frame time mistmatch %d vs. %u", bucket->render_millis(), + entry.renderTimeMs); hitMergeError = true; return; } @@ -232,17 +226,17 @@ void dumpAsTextToFd(service::GraphicsStatsProto* proto, int fd) { // This isn't a full validation, just enough that we can deref at will if (proto->package_name().empty() || !proto->has_summary()) { ALOGW("Skipping dump, invalid package_name() '%s' or summary %d", - proto->package_name().c_str(), proto->has_summary()); + proto->package_name().c_str(), proto->has_summary()); return; } dprintf(fd, "\nPackage: %s", proto->package_name().c_str()); - dprintf(fd, "\nVersion: %d", proto->version_code()); + dprintf(fd, "\nVersion: %lld", proto->version_code()); dprintf(fd, "\nStats since: %lldns", proto->stats_start()); dprintf(fd, "\nStats end: %lldns", proto->stats_end()); auto summary = proto->summary(); dprintf(fd, "\nTotal frames rendered: %d", summary.total_frames()); dprintf(fd, "\nJanky frames: %d (%.2f%%)", summary.janky_frames(), - (float) summary.janky_frames() / (float) summary.total_frames() * 100.0f); + (float)summary.janky_frames() / (float)summary.total_frames() * 100.0f); dprintf(fd, "\n50th percentile: %dms", findPercentile(proto, 50)); dprintf(fd, "\n90th percentile: %dms", findPercentile(proto, 90)); dprintf(fd, "\n95th percentile: %dms", findPercentile(proto, 95)); @@ -260,7 +254,8 @@ void dumpAsTextToFd(service::GraphicsStatsProto* proto, int fd) { } void GraphicsStatsService::saveBuffer(const std::string& path, const std::string& package, - int versionCode, int64_t startTime, int64_t endTime, const ProfileData* data) { + int64_t versionCode, int64_t startTime, int64_t endTime, + const ProfileData* data) { service::GraphicsStatsProto statsProto; if (!parseFromFile(path, &statsProto)) { statsProto.Clear(); @@ -275,8 +270,8 @@ void GraphicsStatsService::saveBuffer(const std::string& path, const std::string return; } if (statsProto.package_name().empty() || !statsProto.has_summary()) { - ALOGE("missing package_name() '%s' summary %d", - statsProto.package_name().c_str(), statsProto.has_summary()); + ALOGE("missing package_name() '%s' summary %d", statsProto.package_name().c_str(), + statsProto.has_summary()); return; } int outFd = open(path.c_str(), O_CREAT | O_RDWR | O_TRUNC, 0660); @@ -288,8 +283,8 @@ void GraphicsStatsService::saveBuffer(const std::string& path, const std::string int wrote = write(outFd, &sCurrentFileVersion, sHeaderSize); if (wrote != sHeaderSize) { int err = errno; - ALOGW("Failed to write header to '%s', returned=%d errno=%d (%s)", - path.c_str(), wrote, err, strerror(err)); + ALOGW("Failed to write header to '%s', returned=%d errno=%d (%s)", path.c_str(), wrote, err, + strerror(err)); close(outFd); return; } @@ -297,8 +292,8 @@ void GraphicsStatsService::saveBuffer(const std::string& path, const std::string FileOutputStreamLite output(outFd); bool success = statsProto.SerializeToZeroCopyStream(&output) && output.Flush(); if (output.GetErrno() != 0) { - ALOGW("Error writing to fd=%d, path='%s' err=%d (%s)", - outFd, path.c_str(), output.GetErrno(), strerror(output.GetErrno())); + ALOGW("Error writing to fd=%d, path='%s' err=%d (%s)", outFd, path.c_str(), + output.GetErrno(), strerror(output.GetErrno())); success = false; } else if (!success) { ALOGW("Serialize failed on '%s' unknown error", path.c_str()); @@ -313,6 +308,7 @@ public: int fd() { return mFd; } DumpType type() { return mType; } service::GraphicsStatsServiceDumpProto& proto() { return mProto; } + private: int mFd; DumpType mType; @@ -323,19 +319,20 @@ GraphicsStatsService::Dump* GraphicsStatsService::createDump(int outFd, DumpType return new Dump(outFd, type); } -void GraphicsStatsService::addToDump(Dump* dump, const std::string& path, const std::string& package, - int versionCode, int64_t startTime, int64_t endTime, const ProfileData* data) { +void GraphicsStatsService::addToDump(Dump* dump, const std::string& path, + const std::string& package, int64_t versionCode, + int64_t startTime, int64_t endTime, const ProfileData* data) { service::GraphicsStatsProto statsProto; if (!path.empty() && !parseFromFile(path, &statsProto)) { statsProto.Clear(); } - if (data && !mergeProfileDataIntoProto( - &statsProto, package, versionCode, startTime, endTime, data)) { + if (data && + !mergeProfileDataIntoProto(&statsProto, package, versionCode, startTime, endTime, data)) { return; } if (!statsProto.IsInitialized()) { ALOGW("Failed to load profile data from path '%s' and data %p", - path.empty() ? "<empty>" : path.c_str(), data); + path.empty() ? "<empty>" : path.c_str(), data); return; } diff --git a/libs/hwui/service/GraphicsStatsService.h b/libs/hwui/service/GraphicsStatsService.h index d0fd60ee1aba..bce0f3ddbb82 100644 --- a/libs/hwui/service/GraphicsStatsService.h +++ b/libs/hwui/service/GraphicsStatsService.h @@ -44,11 +44,13 @@ public: }; ANDROID_API static void saveBuffer(const std::string& path, const std::string& package, - int versionCode, int64_t startTime, int64_t endTime, const ProfileData* data); + int64_t versionCode, int64_t startTime, int64_t endTime, + const ProfileData* data); ANDROID_API static Dump* createDump(int outFd, DumpType type); - ANDROID_API static void addToDump(Dump* dump, const std::string& path, const std::string& package, - int versionCode, int64_t startTime, int64_t endTime, const ProfileData* data); + ANDROID_API static void addToDump(Dump* dump, const std::string& path, + const std::string& package, int64_t versionCode, + int64_t startTime, int64_t endTime, const ProfileData* data); ANDROID_API static void addToDump(Dump* dump, const std::string& path); ANDROID_API static void finishDump(Dump* dump); diff --git a/libs/hwui/tests/common/BitmapAllocationTestUtils.h b/libs/hwui/tests/common/BitmapAllocationTestUtils.h index 2988979cc266..312b60bbc067 100644 --- a/libs/hwui/tests/common/BitmapAllocationTestUtils.h +++ b/libs/hwui/tests/common/BitmapAllocationTestUtils.h @@ -27,17 +27,17 @@ namespace test { class BitmapAllocationTestUtils { public: - static sk_sp<Bitmap> allocateHeapBitmap(int width, int height, - SkColorType colorType, std::function<void(SkBitmap& bitmap)> setup) { - sk_sp<Bitmap> bitmap = TestUtils::createBitmap(width, height, colorType); - SkBitmap skBitmap; - bitmap->getSkBitmap(&skBitmap); - setup(skBitmap); - return bitmap; + static sk_sp<Bitmap> allocateHeapBitmap(int width, int height, SkColorType colorType, + std::function<void(SkBitmap& bitmap)> setup) { + sk_sp<Bitmap> bitmap = TestUtils::createBitmap(width, height, colorType); + SkBitmap skBitmap; + bitmap->getSkBitmap(&skBitmap); + setup(skBitmap); + return bitmap; } - static sk_sp<Bitmap> allocateHardwareBitmap(int width, int height, - SkColorType colorType, std::function<void(SkBitmap& bitmap)> setup) { + static sk_sp<Bitmap> allocateHardwareBitmap(int width, int height, SkColorType colorType, + std::function<void(SkBitmap& bitmap)> setup) { SkBitmap skBitmap; SkImageInfo info = SkImageInfo::Make(width, height, colorType, kPremul_SkAlphaType); skBitmap.setInfo(info); @@ -46,8 +46,8 @@ public: return Bitmap::allocateHardwareBitmap(skBitmap); } - typedef sk_sp<Bitmap> (*BitmapAllocator) (int, int, SkColorType, - std::function<void(SkBitmap& bitmap)> setup); + typedef sk_sp<Bitmap> (*BitmapAllocator)(int, int, SkColorType, + std::function<void(SkBitmap& bitmap)> setup); template <class T, BitmapAllocator allocator> static test::TestScene* createBitmapAllocationScene(const TestScene::Options&) { @@ -55,22 +55,16 @@ public: } template <class BaseScene> - static bool registerBitmapAllocationScene(std::string name, std::string description) { - TestScene::registerScene({ - name + "GlTex", - description + " (GlTex version).", - createBitmapAllocationScene<BaseScene, &allocateHeapBitmap> - }); + static bool registerBitmapAllocationScene(std::string name, std::string description) { + TestScene::registerScene({name + "GlTex", description + " (GlTex version).", + createBitmapAllocationScene<BaseScene, &allocateHeapBitmap>}); - TestScene::registerScene({ - name + "EglImage", - description + " (EglImage version).", - createBitmapAllocationScene<BaseScene, &allocateHardwareBitmap> - }); + TestScene::registerScene({name + "EglImage", description + " (EglImage version).", + createBitmapAllocationScene<BaseScene, &allocateHardwareBitmap>}); return true; } }; -} // namespace test -} // namespace uirenderer -} // namespace android +} // namespace test +} // namespace uirenderer +} // namespace android diff --git a/libs/hwui/tests/common/LeakChecker.cpp b/libs/hwui/tests/common/LeakChecker.cpp index fe38ec958527..5b361548eeda 100644 --- a/libs/hwui/tests/common/LeakChecker.cpp +++ b/libs/hwui/tests/common/LeakChecker.cpp @@ -19,10 +19,10 @@ #include "Caches.h" #include "TestUtils.h" -#include <cstdio> -#include <iostream> #include <memunreachable/memunreachable.h> #include <unistd.h> +#include <cstdio> +#include <iostream> #include <unordered_set> using namespace std; @@ -45,12 +45,12 @@ static void logUnreachable(initializer_list<UnreachableMemoryInfo> infolist) { merged.allocation_bytes = max(merged.allocation_bytes, info.allocation_bytes); merged.num_allocations = max(merged.num_allocations, info.num_allocations); for (auto& leak : info.leaks) { - if (addrs.find(leak.begin) == addrs.end()) { - merged.leaks.push_back(leak); - merged.num_leaks++; - merged.leak_bytes += leak.size; - addrs.insert(leak.begin); - } + if (addrs.find(leak.begin) == addrs.end()) { + merged.leaks.push_back(leak); + merged.num_leaks++; + merged.leak_bytes += leak.size; + addrs.insert(leak.begin); + } } } @@ -59,7 +59,7 @@ static void logUnreachable(initializer_list<UnreachableMemoryInfo> infolist) { cout << endl << "Leaked memory!" << endl; if (!merged.leaks[0].backtrace.num_frames) { cout << "Re-run with 'export LIBC_DEBUG_MALLOC_OPTIONS=backtrace' to get backtraces" - << endl; + << endl; } cout << merged.ToString(false); } diff --git a/libs/hwui/tests/common/LeakChecker.h b/libs/hwui/tests/common/LeakChecker.h index cdf47d6bda80..e43df177cdba 100644 --- a/libs/hwui/tests/common/LeakChecker.h +++ b/libs/hwui/tests/common/LeakChecker.h @@ -22,7 +22,7 @@ namespace test { class LeakChecker { public: static void checkForLeaks(); -}; // class TestUtils +}; // class TestUtils } /* namespace test */ } /* namespace uirenderer */ diff --git a/libs/hwui/tests/common/TestContext.cpp b/libs/hwui/tests/common/TestContext.cpp index c1ca1e7ac28a..92b6cbdfc613 100644 --- a/libs/hwui/tests/common/TestContext.cpp +++ b/libs/hwui/tests/common/TestContext.cpp @@ -24,24 +24,23 @@ namespace test { static const int IDENT_DISPLAYEVENT = 1; -static android::DisplayInfo DUMMY_DISPLAY { - 1080, //w - 1920, //h - 320.0, // xdpi - 320.0, // ydpi - 60.0, // fps - 2.0, // density - 0, // orientation - false, // secure? - 0, // appVsyncOffset - 0, // presentationDeadline +static android::DisplayInfo DUMMY_DISPLAY{ + 1080, // w + 1920, // h + 320.0, // xdpi + 320.0, // ydpi + 60.0, // fps + 2.0, // density + 0, // orientation + false, // secure? + 0, // appVsyncOffset + 0, // presentationDeadline }; DisplayInfo getBuiltInDisplay() { #if !HWUI_NULL_GPU DisplayInfo display; - sp<IBinder> dtoken(SurfaceComposerClient::getBuiltInDisplay( - ISurfaceComposer::eDisplayIdMain)); + sp<IBinder> dtoken(SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain)); status_t status = SurfaceComposerClient::getDisplayInfo(dtoken, &display); LOG_ALWAYS_FATAL_IF(status, "Failed to get display info\n"); return display; @@ -56,8 +55,8 @@ android::DisplayInfo gDisplay = DUMMY_DISPLAY; TestContext::TestContext() { mLooper = new Looper(true); mSurfaceComposerClient = new SurfaceComposerClient(); - mLooper->addFd(mDisplayEventReceiver.getFd(), IDENT_DISPLAYEVENT, - Looper::EVENT_INPUT, nullptr, nullptr); + mLooper->addFd(mDisplayEventReceiver.getFd(), IDENT_DISPLAYEVENT, Looper::EVENT_INPUT, nullptr, + nullptr); } TestContext::~TestContext() {} @@ -78,13 +77,11 @@ void TestContext::createSurface() { } void TestContext::createWindowSurface() { - mSurfaceControl = mSurfaceComposerClient->createSurface(String8("HwuiTest"), - gDisplay.w, gDisplay.h, PIXEL_FORMAT_RGBX_8888); + mSurfaceControl = mSurfaceComposerClient->createSurface(String8("HwuiTest"), gDisplay.w, + gDisplay.h, PIXEL_FORMAT_RGBX_8888); - SurfaceComposerClient::openGlobalTransaction(); - mSurfaceControl->setLayer(0x7FFFFFF); - mSurfaceControl->show(); - SurfaceComposerClient::closeGlobalTransaction(); + SurfaceComposerClient::Transaction t; + t.setLayer(mSurfaceControl, 0x7FFFFFF).show(mSurfaceControl).apply(); mSurface = mSurfaceControl->getSurface(); } @@ -124,10 +121,11 @@ void TestContext::waitForVsync() { // Drain it DisplayEventReceiver::Event buf[100]; - while (mDisplayEventReceiver.getEvents(buf, 100) > 0) { } + while (mDisplayEventReceiver.getEvents(buf, 100) > 0) { + } #endif } -} // namespace test -} // namespace uirenderer -} // namespace android +} // namespace test +} // namespace uirenderer +} // namespace android diff --git a/libs/hwui/tests/common/TestContext.h b/libs/hwui/tests/common/TestContext.h index 312988b968de..0996f4dc706e 100644 --- a/libs/hwui/tests/common/TestContext.h +++ b/libs/hwui/tests/common/TestContext.h @@ -17,24 +17,24 @@ #ifndef TESTCONTEXT_H #define TESTCONTEXT_H +#include <gui/BufferItemConsumer.h> #include <gui/DisplayEventReceiver.h> #include <gui/ISurfaceComposer.h> -#include <gui/BufferItemConsumer.h> +#include <gui/Surface.h> #include <gui/SurfaceComposerClient.h> #include <gui/SurfaceControl.h> -#include <gui/Surface.h> #include <ui/DisplayInfo.h> #include <utils/Looper.h> -#include <thread> #include <atomic> +#include <thread> namespace android { namespace uirenderer { namespace test { extern DisplayInfo gDisplay; -#define dp(x) ((x) * android::uirenderer::test::gDisplay.density) +#define dp(x) ((x)*android::uirenderer::test::gDisplay.density) DisplayInfo getBuiltInDisplay(); @@ -45,8 +45,7 @@ public: // Must be called before surface(); void setRenderOffscreen(bool renderOffscreen) { - LOG_ALWAYS_FATAL_IF(mSurface.get(), - "Must be called before surface is created"); + LOG_ALWAYS_FATAL_IF(mSurface.get(), "Must be called before surface is created"); mRenderOffscreen = renderOffscreen; } @@ -68,8 +67,8 @@ private: bool mRenderOffscreen; }; -} // namespace test -} // namespace uirenderer -} // namespace android +} // namespace test +} // namespace uirenderer +} // namespace android #endif diff --git a/libs/hwui/tests/common/TestListViewSceneBase.cpp b/libs/hwui/tests/common/TestListViewSceneBase.cpp index 38c4848ebc41..a7f4d4d3991f 100644 --- a/libs/hwui/tests/common/TestListViewSceneBase.cpp +++ b/libs/hwui/tests/common/TestListViewSceneBase.cpp @@ -35,17 +35,17 @@ void TestListViewSceneBase::createContent(int width, int height, Canvas& canvas) for (int y = 0; y < height + (heightWithSpacing - 1); y += heightWithSpacing) { int id = mListItems.size(); auto setup = std::bind(&TestListViewSceneBase::createListItem, this, std::placeholders::_1, - std::placeholders::_2, id, mItemWidth, mItemHeight); - auto node = TestUtils::createNode(mItemLeft, y, mItemLeft + mItemWidth, - y + mItemHeight, setup); + std::placeholders::_2, id, mItemWidth, mItemHeight); + auto node = + TestUtils::createNode(mItemLeft, y, mItemLeft + mItemWidth, y + mItemHeight, setup); mListItems.push_back(node); } mListView = TestUtils::createNode(0, 0, width, height, - [this](RenderProperties& props, Canvas& canvas) { - for (size_t ci = 0; ci < mListItems.size(); ci++) { - canvas.drawRenderNode(mListItems[ci].get()); - } - }); + [this](RenderProperties& props, Canvas& canvas) { + for (size_t ci = 0; ci < mListItems.size(); ci++) { + canvas.drawRenderNode(mListItems[ci].get()); + } + }); canvas.drawColor(Color::Grey_500, SkBlendMode::kSrcOver); canvas.drawRenderNode(mListView.get()); @@ -56,8 +56,8 @@ void TestListViewSceneBase::doFrame(int frameNr) { int itemIndexOffset = scrollPx / (mItemSpacing + mItemHeight); int pxOffset = -(scrollPx % (mItemSpacing + mItemHeight)); - std::unique_ptr<Canvas> canvas(Canvas::create_recording_canvas(mListView->stagingProperties().getWidth(), - mListView->stagingProperties().getHeight())); + std::unique_ptr<Canvas> canvas(Canvas::create_recording_canvas( + mListView->stagingProperties().getWidth(), mListView->stagingProperties().getHeight())); for (size_t ci = 0; ci < mListItems.size(); ci++) { // update item position auto listItem = mListItems[(ci + itemIndexOffset) % mListItems.size()]; @@ -72,6 +72,6 @@ void TestListViewSceneBase::doFrame(int frameNr) { mListView->setStagingDisplayList(canvas->finishRecording()); } -} // namespace test -} // namespace uirenderer -} // namespace android +} // namespace test +} // namespace uirenderer +} // namespace android diff --git a/libs/hwui/tests/common/TestListViewSceneBase.h b/libs/hwui/tests/common/TestListViewSceneBase.h index ed6867ab3750..ab0d3c74226b 100644 --- a/libs/hwui/tests/common/TestListViewSceneBase.h +++ b/libs/hwui/tests/common/TestListViewSceneBase.h @@ -15,9 +15,9 @@ */ #pragma once -#include "TestScene.h" #include <RenderNode.h> #include <RenderProperties.h> +#include "TestScene.h" namespace android { namespace uirenderer { @@ -25,20 +25,21 @@ namespace test { class TestListViewSceneBase : public TestScene { public: - virtual void createListItem(RenderProperties& props, Canvas& canvas, int id, - int itemWidth, int itemHeight) = 0; + virtual void createListItem(RenderProperties& props, Canvas& canvas, int id, int itemWidth, + int itemHeight) = 0; + private: int mItemHeight; int mItemSpacing; int mItemWidth; int mItemLeft; sp<RenderNode> mListView; - std::vector< sp<RenderNode> > mListItems; + std::vector<sp<RenderNode> > mListItems; void createContent(int width, int height, Canvas& canvas) override; void doFrame(int frameNr) override; }; -} // namespace test -} // namespace uirenderer -} // namespace android +} // namespace test +} // namespace uirenderer +} // namespace android diff --git a/libs/hwui/tests/common/TestScene.h b/libs/hwui/tests/common/TestScene.h index f6f7c62a4f63..91022cfe734b 100644 --- a/libs/hwui/tests/common/TestScene.h +++ b/libs/hwui/tests/common/TestScene.h @@ -16,6 +16,9 @@ #pragma once +#include <gui/Surface.h> +#include <utils/StrongPointer.h> + #include <string> #include <unordered_map> @@ -52,9 +55,8 @@ public: class Registrar { public: - explicit Registrar(const TestScene::Info& info) { - TestScene::registerScene(info); - } + explicit Registrar(const TestScene::Info& info) { TestScene::registerScene(info); } + private: Registrar() = delete; Registrar(const Registrar&) = delete; @@ -67,8 +69,10 @@ public: static std::unordered_map<std::string, Info>& testMap(); static void registerScene(const Info& info); + + sp<Surface> renderTarget; }; -} // namespace test -} // namespace uirenderer -} // namespace android +} // namespace test +} // namespace uirenderer +} // namespace android diff --git a/libs/hwui/tests/common/TestUtils.cpp b/libs/hwui/tests/common/TestUtils.cpp index 64ec58d0adab..51cf772e8691 100644 --- a/libs/hwui/tests/common/TestUtils.cpp +++ b/libs/hwui/tests/common/TestUtils.cpp @@ -16,16 +16,17 @@ #include "TestUtils.h" -#include "hwui/Paint.h" #include "DeferredLayerUpdater.h" +#include "hwui/Paint.h" -#include <renderthread/EglManager.h> -#include <renderthread/OpenGLPipeline.h> +#include <SkClipStack.h> +#include <minikin/Layout.h> #include <pipeline/skia/SkiaOpenGLPipeline.h> #include <pipeline/skia/SkiaVulkanPipeline.h> +#include <renderthread/EglManager.h> +#include <renderthread/OpenGLPipeline.h> #include <renderthread/VulkanManager.h> #include <utils/Unicode.h> -#include <SkClipStack.h> #include <SkGlyphCache.h> @@ -43,10 +44,10 @@ SkColor TestUtils::interpolateColor(float fraction, SkColor start, SkColor end) int endG = (end >> 8) & 0xff; int endB = end & 0xff; - return (int)((startA + (int)(fraction * (endA - startA))) << 24) - | (int)((startR + (int)(fraction * (endR - startR))) << 16) - | (int)((startG + (int)(fraction * (endG - startG))) << 8) - | (int)((startB + (int)(fraction * (endB - startB)))); + return (int)((startA + (int)(fraction * (endA - startA))) << 24) | + (int)((startR + (int)(fraction * (endR - startR))) << 16) | + (int)((startG + (int)(fraction * (endG - startG))) << 8) | + (int)((startB + (int)(fraction * (endB - startB)))); } sp<DeferredLayerUpdater> TestUtils::createTextureLayerUpdater( @@ -76,15 +77,16 @@ sp<DeferredLayerUpdater> TestUtils::createTextureLayerUpdater( // updateLayer so it's ready to draw layerUpdater->updateLayer(true, Matrix4::identity().data); if (layerUpdater->backingLayer()->getApi() == Layer::Api::OpenGL) { - static_cast<GlLayer*>(layerUpdater->backingLayer())->setRenderTarget( - GL_TEXTURE_EXTERNAL_OES); + static_cast<GlLayer*>(layerUpdater->backingLayer()) + ->setRenderTarget(GL_TEXTURE_EXTERNAL_OES); } return layerUpdater; } void TestUtils::layoutTextUnscaled(const SkPaint& paint, const char* text, - std::vector<glyph_t>* outGlyphs, std::vector<float>* outPositions, - float* outTotalAdvance, Rect* outBounds) { + std::vector<glyph_t>* outGlyphs, + std::vector<float>* outPositions, float* outTotalAdvance, + Rect* outBounds) { Rect bounds; float totalAdvance = 0; SkSurfaceProps surfaceProps(0, kUnknown_SkPixelGeometry); @@ -117,17 +119,22 @@ void TestUtils::layoutTextUnscaled(const SkPaint& paint, const char* text, *outTotalAdvance = totalAdvance; } - -void TestUtils::drawUtf8ToCanvas(Canvas* canvas, const char* text, - const SkPaint& paint, float x, float y) { +void TestUtils::drawUtf8ToCanvas(Canvas* canvas, const char* text, const SkPaint& paint, float x, + float y) { auto utf16 = asciiToUtf16(text); - canvas->drawText(utf16.get(), 0, strlen(text), strlen(text), x, y, 0, paint, nullptr); + SkPaint glyphPaint(paint); + glyphPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); + canvas->drawText(utf16.get(), 0, strlen(text), strlen(text), x, y, minikin::Bidi::LTR, + glyphPaint, nullptr, nullptr /* measured text */, 0 /* measured text offset */); } -void TestUtils::drawUtf8ToCanvas(Canvas* canvas, const char* text, - const SkPaint& paint, const SkPath& path) { +void TestUtils::drawUtf8ToCanvas(Canvas* canvas, const char* text, const SkPaint& paint, + const SkPath& path) { auto utf16 = asciiToUtf16(text); - canvas->drawTextOnPath(utf16.get(), strlen(text), 0, path, 0, 0, paint, nullptr); + SkPaint glyphPaint(paint); + glyphPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); + canvas->drawTextOnPath(utf16.get(), strlen(text), minikin::Bidi::LTR, path, 0, 0, glyphPaint, + nullptr); } void TestUtils::TestTask::run() { diff --git a/libs/hwui/tests/common/TestUtils.h b/libs/hwui/tests/common/TestUtils.h index f29363131030..1bfa046eff63 100644 --- a/libs/hwui/tests/common/TestUtils.h +++ b/libs/hwui/tests/common/TestUtils.h @@ -22,11 +22,11 @@ #include <Properties.h> #include <Rect.h> #include <RenderNode.h> +#include <Snapshot.h> #include <hwui/Bitmap.h> #include <pipeline/skia/SkiaRecordingCanvas.h> #include <renderstate/RenderState.h> #include <renderthread/RenderThread.h> -#include <Snapshot.h> #include <RecordedOp.h> #include <RecordingCanvas.h> @@ -36,85 +36,88 @@ namespace android { namespace uirenderer { -#define EXPECT_MATRIX_APPROX_EQ(a, b) \ - EXPECT_TRUE(TestUtils::matricesAreApproxEqual(a, b)) - -#define EXPECT_RECT_APPROX_EQ(a, b) \ - EXPECT_TRUE(MathUtils::areEqual((a).left, (b).left) \ - && MathUtils::areEqual((a).top, (b).top) \ - && MathUtils::areEqual((a).right, (b).right) \ - && MathUtils::areEqual((a).bottom, (b).bottom)); - -#define EXPECT_CLIP_RECT(expRect, clipStatePtr) \ - EXPECT_NE(nullptr, (clipStatePtr)) << "Op is unclipped"; \ - if ((clipStatePtr)->mode == ClipMode::Rectangle) { \ - EXPECT_EQ((expRect), reinterpret_cast<const ClipRect*>(clipStatePtr)->rect); \ - } else { \ - ADD_FAILURE() << "ClipState not a rect"; \ - } +#define EXPECT_MATRIX_APPROX_EQ(a, b) EXPECT_TRUE(TestUtils::matricesAreApproxEqual(a, b)) + +#define EXPECT_RECT_APPROX_EQ(a, b) \ + EXPECT_TRUE(MathUtils::areEqual((a).left, (b).left) && \ + MathUtils::areEqual((a).top, (b).top) && \ + MathUtils::areEqual((a).right, (b).right) && \ + MathUtils::areEqual((a).bottom, (b).bottom)); + +#define EXPECT_CLIP_RECT(expRect, clipStatePtr) \ + EXPECT_NE(nullptr, (clipStatePtr)) << "Op is unclipped"; \ + if ((clipStatePtr)->mode == ClipMode::Rectangle) { \ + EXPECT_EQ((expRect), reinterpret_cast<const ClipRect*>(clipStatePtr)->rect); \ + } else { \ + ADD_FAILURE() << "ClipState not a rect"; \ + } #define INNER_PIPELINE_TEST(test_case_name, test_name, pipeline, functionCall) \ - TEST(test_case_name, test_name##_##pipeline) { \ - RenderPipelineType oldType = Properties::getRenderPipelineType(); \ - Properties::overrideRenderPipelineType(RenderPipelineType::pipeline); \ - functionCall; \ - Properties::overrideRenderPipelineType(oldType); \ + TEST(test_case_name, test_name##_##pipeline) { \ + RenderPipelineType oldType = Properties::getRenderPipelineType(); \ + Properties::overrideRenderPipelineType(RenderPipelineType::pipeline); \ + functionCall; \ + Properties::overrideRenderPipelineType(oldType); \ }; /** * Like gtests' TEST, but only runs with the OpenGL RenderPipelineType */ -#define OPENGL_PIPELINE_TEST(test_case_name, test_name) \ - class test_case_name##_##test_name##_HwuiTest { \ - public: \ - static void doTheThing(); \ - }; \ - INNER_PIPELINE_TEST(test_case_name, test_name, OpenGL, \ - test_case_name##_##test_name##_HwuiTest::doTheThing()) \ +#define OPENGL_PIPELINE_TEST(test_case_name, test_name) \ + class test_case_name##_##test_name##_HwuiTest { \ + public: \ + static void doTheThing(); \ + }; \ + INNER_PIPELINE_TEST(test_case_name, test_name, OpenGL, \ + test_case_name##_##test_name##_HwuiTest::doTheThing()) \ void test_case_name##_##test_name##_HwuiTest::doTheThing() #define INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, pipeline) \ - INNER_PIPELINE_TEST(test_case_name, test_name, pipeline, \ - TestUtils::runOnRenderThread(test_case_name##_##test_name##_RenderThreadTest::doTheThing)) + INNER_PIPELINE_TEST(test_case_name, test_name, pipeline, \ + TestUtils::runOnRenderThread( \ + test_case_name##_##test_name##_RenderThreadTest::doTheThing)) /** * Like gtest's TEST, but runs on the RenderThread, and 'renderThread' is passed, in top level scope * (for e.g. accessing its RenderState) */ -#define RENDERTHREAD_TEST(test_case_name, test_name) \ - class test_case_name##_##test_name##_RenderThreadTest { \ - public: \ - static void doTheThing(renderthread::RenderThread& renderThread); \ - }; \ - INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, OpenGL); \ - INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, SkiaGL); \ +#define RENDERTHREAD_TEST(test_case_name, test_name) \ + class test_case_name##_##test_name##_RenderThreadTest { \ + public: \ + static void doTheThing(renderthread::RenderThread& renderThread); \ + }; \ + INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, OpenGL); \ + INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, SkiaGL); \ /* Temporarily disabling Vulkan until we can figure out a way to stub out the driver */ \ - /* INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, SkiaVulkan); */ \ - void test_case_name##_##test_name##_RenderThreadTest::doTheThing(renderthread::RenderThread& renderThread) + /* INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, SkiaVulkan); */ \ + void test_case_name##_##test_name##_RenderThreadTest::doTheThing( \ + renderthread::RenderThread& renderThread) /** * Like RENDERTHREAD_TEST, but only runs with the OpenGL RenderPipelineType */ -#define RENDERTHREAD_OPENGL_PIPELINE_TEST(test_case_name, test_name) \ - class test_case_name##_##test_name##_RenderThreadTest { \ - public: \ +#define RENDERTHREAD_OPENGL_PIPELINE_TEST(test_case_name, test_name) \ + class test_case_name##_##test_name##_RenderThreadTest { \ + public: \ static void doTheThing(renderthread::RenderThread& renderThread); \ - }; \ - INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, OpenGL); \ - void test_case_name##_##test_name##_RenderThreadTest::doTheThing(renderthread::RenderThread& renderThread) + }; \ + INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, OpenGL); \ + void test_case_name##_##test_name##_RenderThreadTest::doTheThing( \ + renderthread::RenderThread& renderThread) /** * Like RENDERTHREAD_TEST, but only runs with the Skia RenderPipelineTypes */ -#define RENDERTHREAD_SKIA_PIPELINE_TEST(test_case_name, test_name) \ - class test_case_name##_##test_name##_RenderThreadTest { \ - public: \ - static void doTheThing(renderthread::RenderThread& renderThread); \ - }; \ - INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, SkiaGL); \ +#define RENDERTHREAD_SKIA_PIPELINE_TEST(test_case_name, test_name) \ + class test_case_name##_##test_name##_RenderThreadTest { \ + public: \ + static void doTheThing(renderthread::RenderThread& renderThread); \ + }; \ + INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, SkiaGL); \ /* Temporarily disabling Vulkan until we can figure out a way to stub out the driver */ \ - /* INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, SkiaVulkan); */ \ - void test_case_name##_##test_name##_RenderThreadTest::doTheThing(renderthread::RenderThread& renderThread) + /* INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, SkiaVulkan); */ \ + void test_case_name##_##test_name##_RenderThreadTest::doTheThing( \ + renderthread::RenderThread& renderThread) /** * Sets a property value temporarily, generally for the duration of a test, restoring the previous @@ -125,14 +128,11 @@ namespace uirenderer { template <typename T> class ScopedProperty { public: - ScopedProperty(T& property, T newValue) - : mPropertyPtr(&property) - , mOldValue(property) { + ScopedProperty(T& property, T newValue) : mPropertyPtr(&property), mOldValue(property) { property = newValue; } - ~ScopedProperty() { - *mPropertyPtr = mOldValue; - } + ~ScopedProperty() { *mPropertyPtr = mOldValue; } + private: T* mPropertyPtr; T mOldValue; @@ -142,18 +142,15 @@ class TestUtils { public: class SignalingDtor { public: - SignalingDtor() - : mSignal(nullptr) {} - explicit SignalingDtor(int* signal) - : mSignal(signal) {} - void setSignal(int* signal) { - mSignal = signal; - } + SignalingDtor() : mSignal(nullptr) {} + explicit SignalingDtor(int* signal) : mSignal(signal) {} + void setSignal(int* signal) { mSignal = signal; } ~SignalingDtor() { if (mSignal) { (*mSignal)++; } } + private: int* mSignal; }; @@ -181,7 +178,7 @@ public: } static sk_sp<Bitmap> createBitmap(int width, int height, - SkColorType colorType = kN32_SkColorType) { + SkColorType colorType = kN32_SkColorType) { SkImageInfo info = SkImageInfo::Make(width, height, colorType, kPremul_SkAlphaType); return Bitmap::allocateHeapBitmap(info); } @@ -199,15 +196,16 @@ public: renderthread::RenderThread& renderThread, uint32_t width, uint32_t height, const SkMatrix& transform); - template<class CanvasType> - static std::unique_ptr<DisplayList> createDisplayList(int width, int height, - std::function<void(CanvasType& canvas)> canvasCallback) { + template <class CanvasType> + static std::unique_ptr<DisplayList> createDisplayList( + int width, int height, std::function<void(CanvasType& canvas)> canvasCallback) { CanvasType canvas(width, height); canvasCallback(canvas); return std::unique_ptr<DisplayList>(canvas.finishRecording()); } - static sp<RenderNode> createNode(int left, int top, int right, int bottom, + static sp<RenderNode> createNode( + int left, int top, int right, int bottom, std::function<void(RenderProperties& props, Canvas& canvas)> setup) { #if HWUI_NULL_GPU // if RenderNodes are being sync'd/used, device info will be needed, since @@ -219,8 +217,8 @@ public: RenderProperties& props = node->mutateStagingProperties(); props.setLeftTopRightBottom(left, top, right, bottom); if (setup) { - std::unique_ptr<Canvas> canvas(Canvas::create_recording_canvas(props.getWidth(), - props.getHeight())); + std::unique_ptr<Canvas> canvas( + Canvas::create_recording_canvas(props.getWidth(), props.getHeight())); setup(props, *canvas.get()); node->setStagingDisplayList(canvas->finishRecording()); } @@ -228,8 +226,9 @@ public: return node; } - template<class RecordingCanvasType> - static sp<RenderNode> createNode(int left, int top, int right, int bottom, + template <class RecordingCanvasType> + static sp<RenderNode> createNode( + int left, int top, int right, int bottom, std::function<void(RenderProperties& props, RecordingCanvasType& canvas)> setup) { #if HWUI_NULL_GPU // if RenderNodes are being sync'd/used, device info will be needed, since @@ -249,22 +248,23 @@ public: return node; } - static void recordNode(RenderNode& node, - std::function<void(Canvas&)> contentCallback) { - std::unique_ptr<Canvas> canvas(Canvas::create_recording_canvas( - node.stagingProperties().getWidth(), node.stagingProperties().getHeight())); - contentCallback(*canvas.get()); - node.setStagingDisplayList(canvas->finishRecording()); + static void recordNode(RenderNode& node, std::function<void(Canvas&)> contentCallback) { + std::unique_ptr<Canvas> canvas(Canvas::create_recording_canvas( + node.stagingProperties().getWidth(), node.stagingProperties().getHeight())); + contentCallback(*canvas.get()); + node.setStagingDisplayList(canvas->finishRecording()); } - static sp<RenderNode> createSkiaNode(int left, int top, int right, int bottom, - std::function<void(RenderProperties& props, skiapipeline::SkiaRecordingCanvas& canvas)> setup, + static sp<RenderNode> createSkiaNode( + int left, int top, int right, int bottom, + std::function<void(RenderProperties& props, skiapipeline::SkiaRecordingCanvas& canvas)> + setup, const char* name = nullptr, skiapipeline::SkiaDisplayList* displayList = nullptr) { - #if HWUI_NULL_GPU +#if HWUI_NULL_GPU // if RenderNodes are being sync'd/used, device info will be needed, since // DeviceInfo::maxTextureSize() affects layer property DeviceInfo::initialize(); - #endif +#endif sp<RenderNode> node = new RenderNode(); if (name) { node->setName(name); @@ -276,8 +276,8 @@ public: } if (setup) { std::unique_ptr<skiapipeline::SkiaRecordingCanvas> canvas( - new skiapipeline::SkiaRecordingCanvas(nullptr, - props.getWidth(), props.getHeight())); + new skiapipeline::SkiaRecordingCanvas(nullptr, props.getWidth(), + props.getHeight())); setup(props, *canvas.get()); node->setStagingDisplayList(canvas->finishRecording()); } @@ -306,8 +306,7 @@ public: class TestTask : public renderthread::RenderTask { public: - explicit TestTask(RtCallback rtCallback) - : rtCallback(rtCallback) {} + explicit TestTask(RtCallback rtCallback) : rtCallback(rtCallback) {} virtual ~TestTask() {} virtual void run() override; RtCallback rtCallback; @@ -318,37 +317,37 @@ public: */ static void runOnRenderThread(RtCallback rtCallback) { TestTask task(rtCallback); - renderthread::RenderThread::getInstance().queueAndWait(&task); + renderthread::RenderThread::getInstance().queue().runSync([&]() { task.run(); }); } - static bool isRenderThreadRunning() { - return renderthread::RenderThread::hasInstance(); - } + static bool isRenderThreadRunning() { return renderthread::RenderThread::hasInstance(); } static SkColor interpolateColor(float fraction, SkColor start, SkColor end); static void layoutTextUnscaled(const SkPaint& paint, const char* text, - std::vector<glyph_t>* outGlyphs, std::vector<float>* outPositions, - float* outTotalAdvance, Rect* outBounds); + std::vector<glyph_t>* outGlyphs, + std::vector<float>* outPositions, float* outTotalAdvance, + Rect* outBounds); - static void drawUtf8ToCanvas(Canvas* canvas, const char* text, - const SkPaint& paint, float x, float y); + static void drawUtf8ToCanvas(Canvas* canvas, const char* text, const SkPaint& paint, float x, + float y); - static void drawUtf8ToCanvas(Canvas* canvas, const char* text, - const SkPaint& paint, const SkPath& path); + static void drawUtf8ToCanvas(Canvas* canvas, const char* text, const SkPaint& paint, + const SkPath& path); static std::unique_ptr<uint16_t[]> asciiToUtf16(const char* str); class MockFunctor : public Functor { - public: - virtual status_t operator ()(int what, void* data) { - mLastMode = what; - return DrawGlInfo::kStatusDone; - } - int getLastMode() const { return mLastMode; } - private: - int mLastMode = -1; - }; + public: + virtual status_t operator()(int what, void* data) { + mLastMode = what; + return DrawGlInfo::kStatusDone; + } + int getLastMode() const { return mLastMode; } + + private: + int mLastMode = -1; + }; static SkColor getColor(const sk_sp<SkSurface>& surface, int x, int y); @@ -367,7 +366,8 @@ private: if (displayList) { if (displayList->isSkiaDL()) { for (auto&& childDr : static_cast<skiapipeline::SkiaDisplayList*>( - const_cast<DisplayList*>(displayList))->mChildNodes) { + const_cast<DisplayList*>(displayList)) + ->mChildNodes) { syncHierarchyPropertiesAndDisplayListImpl(childDr.getRenderNode()); } } else { @@ -378,7 +378,7 @@ private: } } -}; // class TestUtils +}; // class TestUtils } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/tests/common/scenes/BitmapFillrate.cpp b/libs/hwui/tests/common/scenes/BitmapFillrate.cpp index be58d09b7f4d..1d3d60716d68 100644 --- a/libs/hwui/tests/common/scenes/BitmapFillrate.cpp +++ b/libs/hwui/tests/common/scenes/BitmapFillrate.cpp @@ -32,8 +32,7 @@ static bool _BitmapFillrate( class BitmapFillrate : public TestScene { public: BitmapFillrate(BitmapAllocationTestUtils::BitmapAllocator allocator) - : TestScene() - , mAllocator(allocator) { } + : TestScene(), mAllocator(allocator) {} void createContent(int width, int height, Canvas& canvas) override { canvas.drawColor(Color::White, SkBlendMode::kSrcOver); @@ -41,7 +40,7 @@ public: createNode(canvas, 0xA0CDDC39, width / 3, height / 3, width, height); createNode(canvas, 0x90009688, width / 3, 0, width, height); createNode(canvas, 0xA0FF5722, 0, height / 3, width, height); - createNode(canvas, 0x9000796B, width / 6, height/6, width, height); + createNode(canvas, 0x9000796B, width / 6, height / 6, width, height); createNode(canvas, 0xA0FFC107, width / 6, 0, width, height); } @@ -52,23 +51,23 @@ public: mNodes[ci]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y); } } + private: - void createNode(Canvas& canvas, SkColor color, int left, int top, - int width, int height) { + void createNode(Canvas& canvas, SkColor color, int left, int top, int width, int height) { int itemWidth = 2 * width / 3; int itemHeight = 2 * height / 3; - auto card = TestUtils::createNode(left, top, left + itemWidth , top + itemHeight, + auto card = TestUtils::createNode( + left, top, left + itemWidth, top + itemHeight, [this, itemWidth, itemHeight, color](RenderProperties& props, Canvas& canvas) { - sk_sp<Bitmap> bitmap = mAllocator(itemWidth, itemHeight, kRGBA_8888_SkColorType, - [color](SkBitmap& skBitmap) { - skBitmap.eraseColor(color); - }); - canvas.drawBitmap(*bitmap, 0, 0, nullptr); - }); + sk_sp<Bitmap> bitmap = + mAllocator(itemWidth, itemHeight, kRGBA_8888_SkColorType, + [color](SkBitmap& skBitmap) { skBitmap.eraseColor(color); }); + canvas.drawBitmap(*bitmap, 0, 0, nullptr); + }); canvas.drawRenderNode(card.get()); mNodes.push_back(card); } BitmapAllocationTestUtils::BitmapAllocator mAllocator; - std::vector< sp<RenderNode> > mNodes; + std::vector<sp<RenderNode> > mNodes; };
\ No newline at end of file diff --git a/libs/hwui/tests/common/scenes/BitmapShaders.cpp b/libs/hwui/tests/common/scenes/BitmapShaders.cpp index 4797dec8e89e..15039b5fa976 100644 --- a/libs/hwui/tests/common/scenes/BitmapShaders.cpp +++ b/libs/hwui/tests/common/scenes/BitmapShaders.cpp @@ -14,55 +14,52 @@ * limitations under the License. */ +#include <SkImagePriv.h> #include "TestSceneBase.h" -#include "utils/Color.h" #include "tests/common/BitmapAllocationTestUtils.h" -#include <SkImagePriv.h> +#include "utils/Color.h" class BitmapShaders; -static bool _BitmapShaders( - BitmapAllocationTestUtils::registerBitmapAllocationScene<BitmapShaders>( - "bitmapShader", "Draws bitmap shaders with repeat and mirror modes.")); +static bool _BitmapShaders(BitmapAllocationTestUtils::registerBitmapAllocationScene<BitmapShaders>( + "bitmapShader", "Draws bitmap shaders with repeat and mirror modes.")); class BitmapShaders : public TestScene { public: BitmapShaders(BitmapAllocationTestUtils::BitmapAllocator allocator) - : TestScene() - , mAllocator(allocator) { } + : TestScene(), mAllocator(allocator) {} sp<RenderNode> card; void createContent(int width, int height, Canvas& canvas) override { canvas.drawColor(Color::Grey_200, SkBlendMode::kSrcOver); - sk_sp<Bitmap> hwuiBitmap = mAllocator(200, 200, kRGBA_8888_SkColorType, - [](SkBitmap& skBitmap) { - skBitmap.eraseColor(Color::White); - SkCanvas skCanvas(skBitmap); - SkPaint skPaint; - skPaint.setColor(Color::Red_500); - skCanvas.drawRect(SkRect::MakeWH(100, 100), skPaint); - skPaint.setColor(Color::Blue_500); - skCanvas.drawRect(SkRect::MakeXYWH(100, 100, 100, 100), skPaint); - }); + sk_sp<Bitmap> hwuiBitmap = + mAllocator(200, 200, kRGBA_8888_SkColorType, [](SkBitmap& skBitmap) { + skBitmap.eraseColor(Color::White); + SkCanvas skCanvas(skBitmap); + SkPaint skPaint; + skPaint.setColor(Color::Red_500); + skCanvas.drawRect(SkRect::MakeWH(100, 100), skPaint); + skPaint.setColor(Color::Blue_500); + skCanvas.drawRect(SkRect::MakeXYWH(100, 100, 100, 100), skPaint); + }); SkPaint paint; - sk_sp<SkImage> image = hwuiBitmap->makeImage(); - sk_sp<SkShader> repeatShader = image->makeShader( - SkShader::TileMode::kRepeat_TileMode, - SkShader::TileMode::kRepeat_TileMode, - nullptr); + sk_sp<SkColorFilter> colorFilter; + sk_sp<SkImage> image = hwuiBitmap->makeImage(&colorFilter); + sk_sp<SkShader> repeatShader = + image->makeShader(SkShader::TileMode::kRepeat_TileMode, + SkShader::TileMode::kRepeat_TileMode, nullptr); paint.setShader(std::move(repeatShader)); canvas.drawRoundRect(0, 0, 500, 500, 50.0f, 50.0f, paint); - sk_sp<SkShader> mirrorShader = image->makeShader( - SkShader::TileMode::kMirror_TileMode, - SkShader::TileMode::kMirror_TileMode, - nullptr); + sk_sp<SkShader> mirrorShader = + image->makeShader(SkShader::TileMode::kMirror_TileMode, + SkShader::TileMode::kMirror_TileMode, nullptr); paint.setShader(std::move(mirrorShader)); canvas.drawRoundRect(0, 600, 500, 1100, 50.0f, 50.0f, paint); } - void doFrame(int frameNr) override { } + void doFrame(int frameNr) override {} BitmapAllocationTestUtils::BitmapAllocator mAllocator; }; diff --git a/libs/hwui/tests/common/scenes/ClippingAnimation.cpp b/libs/hwui/tests/common/scenes/ClippingAnimation.cpp index f47e05a1f3e6..2a016ac1b5bc 100644 --- a/libs/hwui/tests/common/scenes/ClippingAnimation.cpp +++ b/libs/hwui/tests/common/scenes/ClippingAnimation.cpp @@ -19,19 +19,17 @@ class ClippingAnimation; static TestScene::Registrar _RectGrid(TestScene::Info{ - "clip", - "Complex clip cases" - "Low CPU/GPU load.", - TestScene::simpleCreateScene<ClippingAnimation> -}); + "clip", + "Complex clip cases" + "Low CPU/GPU load.", + TestScene::simpleCreateScene<ClippingAnimation>}); class ClippingAnimation : public TestScene { public: sp<RenderNode> card; void createContent(int width, int height, Canvas& canvas) override { canvas.drawColor(Color::White, SkBlendMode::kSrcOver); - card = TestUtils::createNode(0, 0, 200, 400, - [](RenderProperties& props, Canvas& canvas) { + card = TestUtils::createNode(0, 0, 200, 400, [](RenderProperties& props, Canvas& canvas) { canvas.save(SaveFlags::MatrixClip); { canvas.clipRect(0, 0, 200, 200, SkClipOp::kIntersect); diff --git a/libs/hwui/tests/common/scenes/GlyphStressAnimation.cpp b/libs/hwui/tests/common/scenes/GlyphStressAnimation.cpp index 5b685bb2f3f7..38999cb1d2ec 100644 --- a/libs/hwui/tests/common/scenes/GlyphStressAnimation.cpp +++ b/libs/hwui/tests/common/scenes/GlyphStressAnimation.cpp @@ -25,39 +25,33 @@ class GlyphStressAnimation; static TestScene::Registrar _GlyphStress(TestScene::Info{ - "glyphstress", - "A stress test for both the glyph cache, and glyph rendering.", - TestScene::simpleCreateScene<GlyphStressAnimation> -}); + "glyphstress", "A stress test for both the glyph cache, and glyph rendering.", + TestScene::simpleCreateScene<GlyphStressAnimation>}); class GlyphStressAnimation : public TestScene { public: sp<RenderNode> container; void createContent(int width, int height, Canvas& canvas) override { container = TestUtils::createNode(0, 0, width, height, nullptr); - doFrame(0); // update container + doFrame(0); // update container canvas.drawColor(Color::White, SkBlendMode::kSrcOver); canvas.drawRenderNode(container.get()); } void doFrame(int frameNr) override { - std::unique_ptr<uint16_t[]> text = TestUtils::asciiToUtf16( - "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"); - ssize_t textLength = 26 * 2; + const char* text = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; - std::unique_ptr<Canvas> canvas(Canvas::create_recording_canvas( - container->stagingProperties().getWidth(), - container->stagingProperties().getHeight())); + std::unique_ptr<Canvas> canvas( + Canvas::create_recording_canvas(container->stagingProperties().getWidth(), + container->stagingProperties().getHeight())); Paint paint; - paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); paint.setAntiAlias(true); paint.setColor(Color::Black); for (int i = 0; i < 5; i++) { paint.setTextSize(10 + (frameNr % 20) + i * 20); - canvas->drawText(text.get(), 0, textLength, textLength, - 0, 100 * (i + 2), minikin::kBidi_Force_LTR, paint, nullptr); + TestUtils::drawUtf8ToCanvas(canvas.get(), text, paint, 0, 100 * (i + 2)); } container->setStagingDisplayList(canvas->finishRecording()); diff --git a/libs/hwui/tests/common/scenes/HwBitmap565.cpp b/libs/hwui/tests/common/scenes/HwBitmap565.cpp index 18fea3d1cb81..cbdb756b8fa7 100644 --- a/libs/hwui/tests/common/scenes/HwBitmap565.cpp +++ b/libs/hwui/tests/common/scenes/HwBitmap565.cpp @@ -15,16 +15,14 @@ */ #include "TestSceneBase.h" -#include "utils/Color.h" #include "tests/common/BitmapAllocationTestUtils.h" +#include "utils/Color.h" class HwBitmap565; static TestScene::Registrar _HwBitmap565(TestScene::Info{ - "hwBitmap565", - "Draws composite shader with hardware bitmap", - TestScene::simpleCreateScene<HwBitmap565> -}); + "hwBitmap565", "Draws composite shader with hardware bitmap", + TestScene::simpleCreateScene<HwBitmap565>}); class HwBitmap565 : public TestScene { public: @@ -32,18 +30,18 @@ public: void createContent(int width, int height, Canvas& canvas) override { canvas.drawColor(Color::Grey_200, SkBlendMode::kSrcOver); - sk_sp<Bitmap> hardwareBitmap = BitmapAllocationTestUtils::allocateHardwareBitmap(200, 200, - kRGB_565_SkColorType, [](SkBitmap& skBitmap) { - skBitmap.eraseColor(Color::White); - SkCanvas skCanvas(skBitmap); - SkPaint skPaint; - skPaint.setColor(Color::Red_500); - skCanvas.drawRect(SkRect::MakeWH(100, 100), skPaint); - skPaint.setColor(Color::Blue_500); - skCanvas.drawRect(SkRect::MakeXYWH(100, 100, 100, 100), skPaint); - }); + sk_sp<Bitmap> hardwareBitmap = BitmapAllocationTestUtils::allocateHardwareBitmap( + 200, 200, kRGB_565_SkColorType, [](SkBitmap& skBitmap) { + skBitmap.eraseColor(Color::White); + SkCanvas skCanvas(skBitmap); + SkPaint skPaint; + skPaint.setColor(Color::Red_500); + skCanvas.drawRect(SkRect::MakeWH(100, 100), skPaint); + skPaint.setColor(Color::Blue_500); + skCanvas.drawRect(SkRect::MakeXYWH(100, 100, 100, 100), skPaint); + }); canvas.drawBitmap(*hardwareBitmap, 10.0f, 10.0f, nullptr); } - void doFrame(int frameNr) override { } + void doFrame(int frameNr) override {} };
\ No newline at end of file diff --git a/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp b/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp index c246ebaddcad..f137562e7c73 100644 --- a/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp +++ b/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp @@ -17,17 +17,15 @@ #include "TestSceneBase.h" #include "utils/Color.h" -#include <ui/PixelFormat.h> #include <SkGradientShader.h> #include <SkImagePriv.h> +#include <ui/PixelFormat.h> class HwBitmapInCompositeShader; static TestScene::Registrar _HwBitmapInCompositeShader(TestScene::Info{ - "hwbitmapcompositeshader", - "Draws composite shader with hardware bitmap", - TestScene::simpleCreateScene<HwBitmapInCompositeShader> -}); + "hwbitmapcompositeshader", "Draws composite shader with hardware bitmap", + TestScene::simpleCreateScene<HwBitmapInCompositeShader>}); class HwBitmapInCompositeShader : public TestScene { public: @@ -35,16 +33,15 @@ public: void createContent(int width, int height, Canvas& canvas) override { canvas.drawColor(Color::Red_500, SkBlendMode::kSrcOver); - uint32_t usage = GraphicBuffer::USAGE_HW_TEXTURE - | GraphicBuffer::USAGE_SW_READ_NEVER - | GRALLOC_USAGE_SW_WRITE_RARELY; + uint32_t usage = GraphicBuffer::USAGE_HW_TEXTURE | GraphicBuffer::USAGE_SW_READ_NEVER | + GRALLOC_USAGE_SW_WRITE_RARELY; sp<GraphicBuffer> buffer = new GraphicBuffer(400, 200, PIXEL_FORMAT_RGBA_8888, usage); unsigned char* pixels = nullptr; buffer->lock(GraphicBuffer::USAGE_SW_WRITE_RARELY, ((void**)&pixels)); - size_t size = bytesPerPixel(buffer->getPixelFormat()) * buffer->getStride() - * buffer->getHeight(); + size_t size = + bytesPerPixel(buffer->getPixelFormat()) * buffer->getStride() * buffer->getHeight(); memset(pixels, 0, size); for (int i = 0; i < 6000; i++) { pixels[4000 + 4 * i + 0] = 255; @@ -61,8 +58,8 @@ public: SkColor colors[2]; colors[0] = Color::Black; colors[1] = Color::White; - sk_sp<SkShader> gradientShader = SkGradientShader::MakeRadial(center, 50, colors, nullptr, - 2, SkShader::TileMode::kRepeat_TileMode); + sk_sp<SkShader> gradientShader = SkGradientShader::MakeRadial( + center, 50, colors, nullptr, 2, SkShader::TileMode::kRepeat_TileMode); sk_sp<SkShader> compositeShader( SkShader::MakeComposeShader(hardwareShader, gradientShader, SkBlendMode::kDstATop)); @@ -72,11 +69,12 @@ public: canvas.drawRoundRect(0, 0, 400, 200, 10.0f, 10.0f, paint); } - void doFrame(int frameNr) override { } + void doFrame(int frameNr) override {} sk_sp<SkShader> createBitmapShader(Bitmap& bitmap) { - sk_sp<SkImage> image = bitmap.makeImage(); + sk_sp<SkColorFilter> colorFilter; + sk_sp<SkImage> image = bitmap.makeImage(&colorFilter); return image->makeShader(SkShader::TileMode::kClamp_TileMode, - SkShader::TileMode::kClamp_TileMode); + SkShader::TileMode::kClamp_TileMode); } }; diff --git a/libs/hwui/tests/common/scenes/HwLayerAnimation.cpp b/libs/hwui/tests/common/scenes/HwLayerAnimation.cpp index 3a230ae6e8b7..cac2fb3d8d5c 100644 --- a/libs/hwui/tests/common/scenes/HwLayerAnimation.cpp +++ b/libs/hwui/tests/common/scenes/HwLayerAnimation.cpp @@ -19,22 +19,20 @@ class HwLayerAnimation; static TestScene::Registrar _HwLayer(TestScene::Info{ - "hwlayer", - "A nested pair of nodes with LAYER_TYPE_HARDWARE set on each. " - "Tests the hardware layer codepath.", - TestScene::simpleCreateScene<HwLayerAnimation> -}); + "hwlayer", + "A nested pair of nodes with LAYER_TYPE_HARDWARE set on each. " + "Tests the hardware layer codepath.", + TestScene::simpleCreateScene<HwLayerAnimation>}); class HwLayerAnimation : public TestScene { public: sp<RenderNode> card; void createContent(int width, int height, Canvas& canvas) override { - card = TestUtils::createNode(0, 0, 200, 200, - [](RenderProperties& props, Canvas& canvas) { + card = TestUtils::createNode(0, 0, 200, 200, [](RenderProperties& props, Canvas& canvas) { props.mutateLayerProperties().setType(LayerType::RenderLayer); canvas.drawColor(0xFF0000FF, SkBlendMode::kSrcOver); }); - canvas.drawColor(0xFFFFFFFF, SkBlendMode::kSrcOver); // background + canvas.drawColor(0xFFFFFFFF, SkBlendMode::kSrcOver); // background canvas.drawRenderNode(card.get()); } void doFrame(int frameNr) override { diff --git a/libs/hwui/tests/common/scenes/HwLayerSizeAnimation.cpp b/libs/hwui/tests/common/scenes/HwLayerSizeAnimation.cpp index d84113238e53..77a59dfe6ba5 100644 --- a/libs/hwui/tests/common/scenes/HwLayerSizeAnimation.cpp +++ b/libs/hwui/tests/common/scenes/HwLayerSizeAnimation.cpp @@ -19,29 +19,27 @@ class HwLayerSizeAnimation; static TestScene::Registrar _HwLayerSize(TestScene::Info{ - "hwlayersize", - "A nested pair of nodes with LayerType::RenderLayer(hardware) set on the child and " - "LayerType::None on the parent. " - "Tests animating the size of a hardware layer.", - TestScene::simpleCreateScene<HwLayerSizeAnimation> -}); + "hwlayersize", + "A nested pair of nodes with LayerType::RenderLayer(hardware) set on the child and " + "LayerType::None on the parent. " + "Tests animating the size of a hardware layer.", + TestScene::simpleCreateScene<HwLayerSizeAnimation>}); class HwLayerSizeAnimation : public TestScene { public: sp<RenderNode> card; void createContent(int width, int height, Canvas& canvas) override { - card = TestUtils::createNode(0, 0, 200, 200, - [](RenderProperties& props, Canvas& canvas) { + card = TestUtils::createNode(0, 0, 200, 200, [](RenderProperties& props, Canvas& canvas) { props.mutateLayerProperties().setType(LayerType::RenderLayer); canvas.drawColor(0xFF0000FF, SkBlendMode::kSrcOver); }); - canvas.drawColor(0xFFFFFFFF, SkBlendMode::kSrcOver); // background + canvas.drawColor(0xFFFFFFFF, SkBlendMode::kSrcOver); // background canvas.drawRenderNode(card.get()); } void doFrame(int frameNr) override { int curFrame = frameNr % 150; - //we animate left and top coordinates, which in turn animates width and - //height (bottom/right coordinates are fixed) + // we animate left and top coordinates, which in turn animates width and + // height (bottom/right coordinates are fixed) card->mutateStagingProperties().setLeftTop(curFrame, curFrame); card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y); } diff --git a/libs/hwui/tests/common/scenes/ListOfFadedTextAnimation.cpp b/libs/hwui/tests/common/scenes/ListOfFadedTextAnimation.cpp index b7357e179bfe..58c99800875b 100644 --- a/libs/hwui/tests/common/scenes/ListOfFadedTextAnimation.cpp +++ b/libs/hwui/tests/common/scenes/ListOfFadedTextAnimation.cpp @@ -22,15 +22,14 @@ class ListOfFadedTextAnimation; static TestScene::Registrar _ListOfFadedTextAnimation(TestScene::Info{ - "fadingedges", - "A mock ListView of scrolling text with faded edge. Doesn't re-bind/re-record views" - "as they are recycled, so won't upload much content (either glyphs, or bitmaps).", - TestScene::simpleCreateScene<ListOfFadedTextAnimation> -}); + "fadingedges", + "A mock ListView of scrolling text with faded edge. Doesn't re-bind/re-record views" + "as they are recycled, so won't upload much content (either glyphs, or bitmaps).", + TestScene::simpleCreateScene<ListOfFadedTextAnimation>}); class ListOfFadedTextAnimation : public TestListViewSceneBase { - void createListItem(RenderProperties& props, Canvas& canvas, int id, - int itemWidth, int itemHeight) override { + void createListItem(RenderProperties& props, Canvas& canvas, int id, int itemWidth, + int itemHeight) override { canvas.drawColor(Color::White, SkBlendMode::kSrcOver); int length = dp(100); canvas.saveLayer(0, 0, length, itemHeight, nullptr, SaveFlags::HasAlphaLayer); @@ -44,8 +43,8 @@ class ListOfFadedTextAnimation : public TestListViewSceneBase { pts[1].set(0, 1); SkColor colors[2] = {Color::Black, Color::Transparent}; - sk_sp<SkShader> s(SkGradientShader::MakeLinear(pts, colors, NULL, 2, - SkShader::kClamp_TileMode)); + sk_sp<SkShader> s( + SkGradientShader::MakeLinear(pts, colors, NULL, 2, SkShader::kClamp_TileMode)); SkMatrix matrix; matrix.setScale(1, length); diff --git a/libs/hwui/tests/common/scenes/ListViewAnimation.cpp b/libs/hwui/tests/common/scenes/ListViewAnimation.cpp index c1144be5b57c..fd8c252ff318 100644 --- a/libs/hwui/tests/common/scenes/ListViewAnimation.cpp +++ b/libs/hwui/tests/common/scenes/ListViewAnimation.cpp @@ -22,11 +22,11 @@ class ListViewAnimation; static TestScene::Registrar _ListView(TestScene::Info{ - "listview", - "A mock ListView of scrolling content. Doesn't re-bind/re-record views as they are recycled, so" - "won't upload much content (either glyphs, or bitmaps).", - TestScene::simpleCreateScene<ListViewAnimation> -}); + "listview", + "A mock ListView of scrolling content. Doesn't re-bind/re-record views as they are " + "recycled, so" + "won't upload much content (either glyphs, or bitmaps).", + TestScene::simpleCreateScene<ListViewAnimation>}); class ListViewAnimation : public TestListViewSceneBase { sk_sp<Bitmap> createRandomCharIcon(int cardHeight) { @@ -42,15 +42,15 @@ class ListViewAnimation : public TestListViewSceneBase { paint.setColor(randomColor); canvas.drawCircle(size / 2, size / 2, size / 2, paint); - bool bgDark = SkColorGetR(randomColor) + SkColorGetG(randomColor) + SkColorGetB(randomColor) - < 128 * 3; + bool bgDark = + SkColorGetR(randomColor) + SkColorGetG(randomColor) + SkColorGetB(randomColor) < + 128 * 3; paint.setColor(bgDark ? Color::White : Color::Grey_700); paint.setTextAlign(SkPaint::kCenter_Align); paint.setTextSize(size / 2); char charToShow = 'A' + (rand() % 26); - const SkPoint pos[] = {{ - SkIntToScalar(size / 2), - /*approximate centering*/ SkFloatToScalar(size * 0.7f)}}; + const SkPoint pos[] = {{SkIntToScalar(size / 2), + /*approximate centering*/ SkFloatToScalar(size * 0.7f)}}; canvas.drawPosText(&charToShow, 1, pos, paint); return bitmap; } @@ -72,8 +72,8 @@ class ListViewAnimation : public TestListViewSceneBase { return bitmap; } - void createListItem(RenderProperties& props, Canvas& canvas, int cardId, - int itemWidth, int itemHeight) override { + void createListItem(RenderProperties& props, Canvas& canvas, int cardId, int itemWidth, + int itemHeight) override { static sk_sp<Bitmap> filledBox(createBoxBitmap(true)); static sk_sp<Bitmap> strokedBox(createBoxBitmap(false)); // TODO: switch to using round rect clipping, once merging correctly handles that @@ -83,7 +83,6 @@ class ListViewAnimation : public TestListViewSceneBase { canvas.drawRoundRect(0, 0, itemWidth, itemHeight, dp(6), dp(6), roundRectPaint); SkPaint textPaint; - textPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); textPaint.setColor(rand() % 2 ? Color::Black : Color::Grey_500); textPaint.setTextSize(dp(20)); textPaint.setAntiAlias(true); @@ -92,7 +91,7 @@ class ListViewAnimation : public TestListViewSceneBase { TestUtils::drawUtf8ToCanvas(&canvas, buf, textPaint, itemHeight, dp(25)); textPaint.setTextSize(dp(15)); TestUtils::drawUtf8ToCanvas(&canvas, "This is some more text on the card", textPaint, - itemHeight, dp(45)); + itemHeight, dp(45)); auto randomIcon = createRandomCharIcon(itemHeight); canvas.drawBitmap(*randomIcon, dp(10), dp(10), nullptr); diff --git a/libs/hwui/tests/common/scenes/MagnifierAnimation.cpp b/libs/hwui/tests/common/scenes/MagnifierAnimation.cpp new file mode 100644 index 000000000000..aa537b4f329c --- /dev/null +++ b/libs/hwui/tests/common/scenes/MagnifierAnimation.cpp @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "TestSceneBase.h" +#include "renderthread/RenderProxy.h" +#include "utils/Color.h" + +class MagnifierAnimation; + +static TestScene::Registrar _Magnifier(TestScene::Info{ + "magnifier", "A sample magnifier using Readback", + TestScene::simpleCreateScene<MagnifierAnimation>}); + +class MagnifierAnimation : public TestScene { +public: + sp<RenderNode> card; + sp<RenderNode> zoomImageView; + + void createContent(int width, int height, Canvas& canvas) override { + magnifier = TestUtils::createBitmap(200, 100); + SkBitmap temp; + magnifier->getSkBitmap(&temp); + temp.eraseColor(Color::White); + canvas.drawColor(Color::White, SkBlendMode::kSrcOver); + card = TestUtils::createNode( + 0, 0, width, height, [&](RenderProperties& props, Canvas& canvas) { + SkPaint paint; + paint.setAntiAlias(true); + paint.setTextSize(50); + + paint.setColor(Color::Black); + TestUtils::drawUtf8ToCanvas(&canvas, "Test string", paint, 10, 400); + }); + canvas.drawRenderNode(card.get()); + zoomImageView = TestUtils::createNode( + 100, 100, 500, 300, [&](RenderProperties& props, Canvas& canvas) { + props.setElevation(dp(16)); + props.mutableOutline().setRoundRect(0, 0, props.getWidth(), props.getHeight(), + dp(6), 1); + props.mutableOutline().setShouldClip(true); + canvas.drawBitmap(*magnifier, 0.0f, 0.0f, (float)magnifier->width(), + (float)magnifier->height(), 0, 0, (float)props.getWidth(), + (float)props.getHeight(), nullptr); + }); + canvas.insertReorderBarrier(true); + canvas.drawRenderNode(zoomImageView.get()); + canvas.insertReorderBarrier(false); + } + + void doFrame(int frameNr) override { + int curFrame = frameNr % 150; + card->mutateStagingProperties().setTranslationX(curFrame); + card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y); + if (renderTarget) { + SkBitmap temp; + magnifier->getSkBitmap(&temp); + constexpr int x = 90; + constexpr int y = 325; + RenderProxy::copySurfaceInto(renderTarget, x, y, x + magnifier->width(), + y + magnifier->height(), &temp); + } + } + + sk_sp<Bitmap> magnifier; +}; diff --git a/libs/hwui/tests/common/scenes/OpPropAnimation.cpp b/libs/hwui/tests/common/scenes/OpPropAnimation.cpp index 68051d63e855..a24cae0ecf8a 100644 --- a/libs/hwui/tests/common/scenes/OpPropAnimation.cpp +++ b/libs/hwui/tests/common/scenes/OpPropAnimation.cpp @@ -20,10 +20,8 @@ class OpPropAnimation; static TestScene::Registrar _Shapes(TestScene::Info{ - "opprops", - "A minimal demonstration of CanvasProperty drawing operations.", - TestScene::simpleCreateScene<OpPropAnimation> -}); + "opprops", "A minimal demonstration of CanvasProperty drawing operations.", + TestScene::simpleCreateScene<OpPropAnimation>}); class OpPropAnimation : public TestScene { public: @@ -42,8 +40,9 @@ public: sp<RenderNode> content; void createContent(int width, int height, Canvas& canvas) override { - content = TestUtils::createNode(0, 0, width, height, - [this, width, height](RenderProperties& props, Canvas& canvas) { + content = TestUtils::createNode(0, 0, width, height, [this, width, height]( + RenderProperties& props, + Canvas& canvas) { mPaint->value.setAntiAlias(true); mPaint->value.setColor(Color::Blue_500); @@ -54,9 +53,9 @@ public: mCircleY->value = height * 0.75; canvas.drawColor(Color::White, SkBlendMode::kSrcOver); - canvas.drawRoundRect(mRoundRectLeft.get(), mRoundRectTop.get(), - mRoundRectRight.get(), mRoundRectBottom.get(), - mRoundRectRx.get(), mRoundRectRy.get(), mPaint.get()); + canvas.drawRoundRect(mRoundRectLeft.get(), mRoundRectTop.get(), mRoundRectRight.get(), + mRoundRectBottom.get(), mRoundRectRx.get(), mRoundRectRy.get(), + mPaint.get()); canvas.drawCircle(mCircleX.get(), mCircleY.get(), mCircleRadius.get(), mPaint.get()); }); canvas.drawRenderNode(content.get()); diff --git a/libs/hwui/tests/common/scenes/OvalAnimation.cpp b/libs/hwui/tests/common/scenes/OvalAnimation.cpp index d6fd60494812..4ff868b9d068 100644 --- a/libs/hwui/tests/common/scenes/OvalAnimation.cpp +++ b/libs/hwui/tests/common/scenes/OvalAnimation.cpp @@ -19,19 +19,15 @@ class OvalAnimation; -static TestScene::Registrar _Oval(TestScene::Info{ - "oval", - "Draws 1 oval.", - TestScene::simpleCreateScene<OvalAnimation> -}); +static TestScene::Registrar _Oval(TestScene::Info{"oval", "Draws 1 oval.", + TestScene::simpleCreateScene<OvalAnimation>}); class OvalAnimation : public TestScene { public: sp<RenderNode> card; void createContent(int width, int height, Canvas& canvas) override { canvas.drawColor(Color::White, SkBlendMode::kSrcOver); - card = TestUtils::createNode(0, 0, 200, 200, - [](RenderProperties& props, Canvas& canvas) { + card = TestUtils::createNode(0, 0, 200, 200, [](RenderProperties& props, Canvas& canvas) { SkPaint paint; paint.setAntiAlias(true); paint.setColor(Color::Black); diff --git a/libs/hwui/tests/common/scenes/PartialDamageAnimation.cpp b/libs/hwui/tests/common/scenes/PartialDamageAnimation.cpp index bc04d81296df..fb1b000a995e 100644 --- a/libs/hwui/tests/common/scenes/PartialDamageAnimation.cpp +++ b/libs/hwui/tests/common/scenes/PartialDamageAnimation.cpp @@ -19,22 +19,18 @@ class PartialDamageAnimation; static TestScene::Registrar _PartialDamage(TestScene::Info{ - "partialdamage", - "Tests the partial invalidation path. Draws a grid of rects and animates 1 " - "of them, should be low CPU & GPU load if EGL_EXT_buffer_age or " - "EGL_KHR_partial_update is supported by the device & are enabled in hwui.", - TestScene::simpleCreateScene<PartialDamageAnimation> -}); + "partialdamage", + "Tests the partial invalidation path. Draws a grid of rects and animates 1 " + "of them, should be low CPU & GPU load if EGL_EXT_buffer_age or " + "EGL_KHR_partial_update is supported by the device & are enabled in hwui.", + TestScene::simpleCreateScene<PartialDamageAnimation>}); class PartialDamageAnimation : public TestScene { public: - std::vector< sp<RenderNode> > cards; + std::vector<sp<RenderNode> > cards; void createContent(int width, int height, Canvas& canvas) override { static SkColor COLORS[] = { - 0xFFF44336, - 0xFF9C27B0, - 0xFF2196F3, - 0xFF4CAF50, + 0xFFF44336, 0xFF9C27B0, 0xFF2196F3, 0xFF4CAF50, }; canvas.drawColor(0xFFFFFFFF, SkBlendMode::kSrcOver); @@ -42,11 +38,11 @@ public: for (int x = dp(16); x < (width - dp(116)); x += dp(116)) { for (int y = dp(16); y < (height - dp(116)); y += dp(116)) { SkColor color = COLORS[static_cast<int>((y / dp(116))) % 4]; - sp<RenderNode> card = TestUtils::createNode(x, y, - x + dp(100), y + dp(100), - [color](RenderProperties& props, Canvas& canvas) { - canvas.drawColor(color, SkBlendMode::kSrcOver); - }); + sp<RenderNode> card = + TestUtils::createNode(x, y, x + dp(100), y + dp(100), + [color](RenderProperties& props, Canvas& canvas) { + canvas.drawColor(color, SkBlendMode::kSrcOver); + }); canvas.drawRenderNode(card.get()); cards.push_back(card); } @@ -59,8 +55,7 @@ public: cards[0]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y); TestUtils::recordNode(*cards[0], [curFrame](Canvas& canvas) { - SkColor color = TestUtils::interpolateColor( - curFrame / 150.0f, 0xFFF44336, 0xFFF8BBD0); + SkColor color = TestUtils::interpolateColor(curFrame / 150.0f, 0xFFF44336, 0xFFF8BBD0); canvas.drawColor(color, SkBlendMode::kSrcOver); }); } diff --git a/libs/hwui/tests/common/scenes/ReadbackFromHardwareBitmap.cpp b/libs/hwui/tests/common/scenes/ReadbackFromHardwareBitmap.cpp index bc6fc6452e90..1d17a021069b 100644 --- a/libs/hwui/tests/common/scenes/ReadbackFromHardwareBitmap.cpp +++ b/libs/hwui/tests/common/scenes/ReadbackFromHardwareBitmap.cpp @@ -19,10 +19,8 @@ class ReadbackFromHardware; static TestScene::Registrar _SaveLayer(TestScene::Info{ - "readbackFromHBitmap", - "Allocates hardware bitmap and readback data from it.", - TestScene::simpleCreateScene<ReadbackFromHardware> -}); + "readbackFromHBitmap", "Allocates hardware bitmap and readback data from it.", + TestScene::simpleCreateScene<ReadbackFromHardware>}); class ReadbackFromHardware : public TestScene { public: @@ -41,7 +39,7 @@ public: } void createContent(int width, int height, Canvas& canvas) override { - canvas.drawColor(Color::White, SkBlendMode::kSrcOver); // background + canvas.drawColor(Color::White, SkBlendMode::kSrcOver); // background sk_sp<Bitmap> hardwareBitmap(createHardwareBitmap()); @@ -50,7 +48,7 @@ public: SkBitmap canvasBitmap; sk_sp<Bitmap> heapBitmap(TestUtils::createBitmap(hardwareBitmap->width(), - hardwareBitmap->height(), &canvasBitmap)); + hardwareBitmap->height(), &canvasBitmap)); SkCanvas skCanvas(canvasBitmap); skCanvas.drawBitmap(readback, 0, 0); @@ -59,5 +57,5 @@ public: canvas.drawBitmap(*hardwareBitmap, 0, 500, nullptr); } - void doFrame(int frameNr) override { } + void doFrame(int frameNr) override {} }; diff --git a/libs/hwui/tests/common/scenes/RecentsAnimation.cpp b/libs/hwui/tests/common/scenes/RecentsAnimation.cpp index 825602466f92..3480a0f18407 100644 --- a/libs/hwui/tests/common/scenes/RecentsAnimation.cpp +++ b/libs/hwui/tests/common/scenes/RecentsAnimation.cpp @@ -20,20 +20,16 @@ class RecentsAnimation; static TestScene::Registrar _Recents(TestScene::Info{ - "recents", - "A recents-like scrolling list of textures. " - "Consists of updating a texture every frame", - TestScene::simpleCreateScene<RecentsAnimation> -}); + "recents", + "A recents-like scrolling list of textures. " + "Consists of updating a texture every frame", + TestScene::simpleCreateScene<RecentsAnimation>}); class RecentsAnimation : public TestScene { public: void createContent(int width, int height, Canvas& renderer) override { static SkColor COLORS[] = { - Color::Red_500, - Color::Purple_500, - Color::Blue_500, - Color::Green_500, + Color::Red_500, Color::Purple_500, Color::Blue_500, Color::Green_500, }; thumbnailSize = std::min(std::min(width, height) / 2, 720); @@ -65,25 +61,26 @@ public: mCards[ci]->mutateStagingProperties().setTranslationY(curFrame); mCards[ci]->setPropertyFieldsDirty(RenderNode::Y); } - mThumbnail.eraseColor(TestUtils::interpolateColor( - curFrame / 150.0f, Color::Green_500, Color::DeepOrange_500)); + mThumbnail.eraseColor(TestUtils::interpolateColor(curFrame / 150.0f, Color::Green_500, + Color::DeepOrange_500)); } private: sp<RenderNode> createCard(int x, int y, int width, int height, Bitmap& thumb) { - return TestUtils::createNode(x, y, x + width, y + height, + return TestUtils::createNode( + x, y, x + width, y + height, [&thumb, width, height](RenderProperties& props, Canvas& canvas) { - props.setElevation(dp(16)); - props.mutableOutline().setRoundRect(0, 0, width, height, dp(10), 1); - props.mutableOutline().setShouldClip(true); + props.setElevation(dp(16)); + props.mutableOutline().setRoundRect(0, 0, width, height, dp(10), 1); + props.mutableOutline().setShouldClip(true); - canvas.drawColor(Color::Grey_200, SkBlendMode::kSrcOver); - canvas.drawBitmap(thumb, 0, 0, thumb.width(), thumb.height(), - 0, 0, width, height, nullptr); - }); + canvas.drawColor(Color::Grey_200, SkBlendMode::kSrcOver); + canvas.drawBitmap(thumb, 0, 0, thumb.width(), thumb.height(), 0, 0, width, + height, nullptr); + }); } SkBitmap mThumbnail; - std::vector< sp<RenderNode> > mCards; + std::vector<sp<RenderNode> > mCards; int thumbnailSize; }; diff --git a/libs/hwui/tests/common/scenes/RectGridAnimation.cpp b/libs/hwui/tests/common/scenes/RectGridAnimation.cpp index 668eec69c2d0..6a3b6a57b28a 100644 --- a/libs/hwui/tests/common/scenes/RectGridAnimation.cpp +++ b/libs/hwui/tests/common/scenes/RectGridAnimation.cpp @@ -14,17 +14,15 @@ * limitations under the License. */ - #include "TestSceneBase.h" class RectGridAnimation; static TestScene::Registrar _RectGrid(TestScene::Info{ - "rectgrid", - "A dense grid of 1x1 rects that should visually look like a single rect. " - "Low CPU/GPU load.", - TestScene::simpleCreateScene<RectGridAnimation> -}); + "rectgrid", + "A dense grid of 1x1 rects that should visually look like a single rect. " + "Low CPU/GPU load.", + TestScene::simpleCreateScene<RectGridAnimation>}); class RectGridAnimation : public TestScene { public: @@ -33,13 +31,12 @@ public: canvas.drawColor(0xFFFFFFFF, SkBlendMode::kSrcOver); canvas.insertReorderBarrier(true); - card = TestUtils::createNode(50, 50, 250, 250, - [](RenderProperties& props, Canvas& canvas) { + card = TestUtils::createNode(50, 50, 250, 250, [](RenderProperties& props, Canvas& canvas) { canvas.drawColor(0xFFFF00FF, SkBlendMode::kSrcOver); SkRegion region; - for (int xOffset = 0; xOffset < 200; xOffset+=2) { - for (int yOffset = 0; yOffset < 200; yOffset+=2) { + for (int xOffset = 0; xOffset < 200; xOffset += 2) { + for (int yOffset = 0; yOffset < 200; yOffset += 2) { region.op(xOffset, yOffset, xOffset + 1, yOffset + 1, SkRegion::kUnion_Op); } } diff --git a/libs/hwui/tests/common/scenes/RoundRectClippingAnimation.cpp b/libs/hwui/tests/common/scenes/RoundRectClippingAnimation.cpp index 4b6632d244f5..314e922e9f38 100644 --- a/libs/hwui/tests/common/scenes/RoundRectClippingAnimation.cpp +++ b/libs/hwui/tests/common/scenes/RoundRectClippingAnimation.cpp @@ -14,7 +14,6 @@ * limitations under the License. */ - #include "TestSceneBase.h" #include <vector> @@ -23,10 +22,9 @@ class RoundRectClippingAnimation : public TestScene { public: int mSpacing, mSize; - RoundRectClippingAnimation(int spacing, int size) - : mSpacing(spacing), mSize(size) {} + RoundRectClippingAnimation(int spacing, int size) : mSpacing(spacing), mSize(size) {} - std::vector< sp<RenderNode> > cards; + std::vector<sp<RenderNode> > cards; void createContent(int width, int height, Canvas& canvas) override { canvas.drawColor(0xFFFFFFFF, SkBlendMode::kSrcOver); canvas.insertReorderBarrier(true); @@ -35,13 +33,13 @@ public: for (int x = 0; x < width; x += mSpacing) { for (int y = 0; y < height; y += mSpacing) { auto color = BrightColors[ci++ % BrightColorsCount]; - auto card = TestUtils::createNode(x, y, x + mSize, y + mSize, - [&](RenderProperties& props, Canvas& canvas) { - canvas.drawColor(color, SkBlendMode::kSrcOver); - props.mutableOutline().setRoundRect(0, 0, - props.getWidth(), props.getHeight(), mSize * .25, 1); - props.mutableOutline().setShouldClip(true); - }); + auto card = TestUtils::createNode( + x, y, x + mSize, y + mSize, [&](RenderProperties& props, Canvas& canvas) { + canvas.drawColor(color, SkBlendMode::kSrcOver); + props.mutableOutline().setRoundRect(0, 0, props.getWidth(), + props.getHeight(), mSize * .25, 1); + props.mutableOutline().setShouldClip(true); + }); canvas.drawRenderNode(card.get()); cards.push_back(card); } @@ -61,17 +59,15 @@ public: }; static TestScene::Registrar _RoundRectClippingGpu(TestScene::Info{ - "roundRectClipping-gpu", - "A bunch of RenderNodes with round rect clipping outlines that's GPU limited.", - [](const TestScene::Options&) -> test::TestScene* { - return new RoundRectClippingAnimation(dp(40), dp(200)); - } -}); + "roundRectClipping-gpu", + "A bunch of RenderNodes with round rect clipping outlines that's GPU limited.", + [](const TestScene::Options&) -> test::TestScene* { + return new RoundRectClippingAnimation(dp(40), dp(200)); + }}); static TestScene::Registrar _RoundRectClippingCpu(TestScene::Info{ - "roundRectClipping-cpu", - "A bunch of RenderNodes with round rect clipping outlines that's CPU limited.", - [](const TestScene::Options&) -> test::TestScene* { - return new RoundRectClippingAnimation(dp(20), dp(20)); - } -}); + "roundRectClipping-cpu", + "A bunch of RenderNodes with round rect clipping outlines that's CPU limited.", + [](const TestScene::Options&) -> test::TestScene* { + return new RoundRectClippingAnimation(dp(20), dp(20)); + }}); diff --git a/libs/hwui/tests/common/scenes/SaveLayer2Animation.cpp b/libs/hwui/tests/common/scenes/SaveLayer2Animation.cpp index ad0b1f15abff..3befce4a395f 100644 --- a/libs/hwui/tests/common/scenes/SaveLayer2Animation.cpp +++ b/libs/hwui/tests/common/scenes/SaveLayer2Animation.cpp @@ -14,19 +14,18 @@ * limitations under the License. */ -#include "TestSceneBase.h" -#include <string> #include <hwui/Paint.h> #include <minikin/Layout.h> +#include <string> +#include "TestSceneBase.h" class SaveLayer2Animation; static TestScene::Registrar _SaveLayer(TestScene::Info{ - "savelayer2", - "Interleaving 20 drawText/drawRect ops with saveLayer" - "Tests the clipped saveLayer performance and FBO switching overhead.", - TestScene::simpleCreateScene<SaveLayer2Animation> -}); + "savelayer2", + "Interleaving 20 drawText/drawRect ops with saveLayer" + "Tests the clipped saveLayer performance and FBO switching overhead.", + TestScene::simpleCreateScene<SaveLayer2Animation>}); class SaveLayer2Animation : public TestScene { public: @@ -37,7 +36,7 @@ public: canvas.drawColor(SkColorSetARGB(255, 255, 0, 0), SkBlendMode::kSrcOver); SkIRect bounds = SkIRect::MakeWH(width, height); int regions = 20; - int smallRectHeight = (bounds.height()/regions); + int smallRectHeight = (bounds.height() / regions); int padding = smallRectHeight / 4; int top = bounds.fTop; @@ -46,27 +45,23 @@ public: mGreenPaint.setColor(SkColorSetARGB(255, 0, 255, 0)); mGreenPaint.setTextSize(padding); - //interleave drawText and drawRect with saveLayer ops + // interleave drawText and drawRect with saveLayer ops for (int i = 0; i < regions; i++, top += smallRectHeight) { - canvas.saveLayer(bounds.fLeft, top, bounds.fRight, top + padding, - &mBluePaint, SaveFlags::ClipToLayer | SaveFlags::MatrixClip); + canvas.saveLayer(bounds.fLeft, top, bounds.fRight, top + padding, &mBluePaint, + SaveFlags::ClipToLayer | SaveFlags::MatrixClip); canvas.drawColor(SkColorSetARGB(255, 255, 255, 0), SkBlendMode::kSrcOver); std::string stri = std::to_string(i); std::string offscreen = "offscreen line " + stri; - std::unique_ptr<uint16_t[]> offtext = TestUtils::asciiToUtf16(offscreen.c_str()); - canvas.drawText(offtext.get(), 0, offscreen.length(), offscreen.length(), - bounds.fLeft, top + padding, minikin::kBidi_Force_LTR, mBluePaint, nullptr); + TestUtils::drawUtf8ToCanvas(&canvas, offscreen.c_str(), mBluePaint, bounds.fLeft, + top + padding); canvas.restore(); canvas.drawRect(bounds.fLeft, top + padding, bounds.fRight, - top + smallRectHeight - padding, mBluePaint); + top + smallRectHeight - padding, mBluePaint); std::string onscreen = "onscreen line " + stri; - std::unique_ptr<uint16_t[]> ontext = TestUtils::asciiToUtf16(onscreen.c_str()); - canvas.drawText(ontext.get(), 0, onscreen.length(), onscreen.length(), bounds.fLeft, - top + smallRectHeight - padding, minikin::kBidi_Force_LTR, mGreenPaint, - nullptr); + TestUtils::drawUtf8ToCanvas(&canvas, onscreen.c_str(), mGreenPaint, bounds.fLeft, + top + smallRectHeight - padding); } } - void doFrame(int frameNr) override { - } + void doFrame(int frameNr) override {} }; diff --git a/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp b/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp index 7e8a7d9d14f2..02dd42ff2ae8 100644 --- a/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp +++ b/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp @@ -19,20 +19,19 @@ class SaveLayerAnimation; static TestScene::Registrar _SaveLayer(TestScene::Info{ - "savelayer", - "A nested pair of clipped saveLayer operations. " - "Tests the clipped saveLayer codepath. Draws content into offscreen buffers and back again.", - TestScene::simpleCreateScene<SaveLayerAnimation> -}); + "savelayer", + "A nested pair of clipped saveLayer operations. " + "Tests the clipped saveLayer codepath. Draws content into offscreen buffers and back " + "again.", + TestScene::simpleCreateScene<SaveLayerAnimation>}); class SaveLayerAnimation : public TestScene { public: sp<RenderNode> card; void createContent(int width, int height, Canvas& canvas) override { - canvas.drawColor(Color::White, SkBlendMode::kSrcOver); // background + canvas.drawColor(Color::White, SkBlendMode::kSrcOver); // background - card = TestUtils::createNode(0, 0, 400, 800, - [](RenderProperties& props, Canvas& canvas) { + card = TestUtils::createNode(0, 0, 400, 800, [](RenderProperties& props, Canvas& canvas) { // nested clipped saveLayers canvas.saveLayerAlpha(0, 0, 400, 400, 200, SaveFlags::ClipToLayer); canvas.drawColor(Color::Green_700, SkBlendMode::kSrcOver); @@ -45,7 +44,7 @@ public: // single unclipped saveLayer canvas.save(SaveFlags::MatrixClip); canvas.translate(0, 400); - canvas.saveLayerAlpha(100, 100, 300, 300, 128, SaveFlags::Flags(0)); // unclipped + canvas.saveLayerAlpha(100, 100, 300, 300, 128, SaveFlags::Flags(0)); // unclipped SkPaint paint; paint.setAntiAlias(true); paint.setColor(Color::Green_700); diff --git a/libs/hwui/tests/common/scenes/ShadowGrid2Animation.cpp b/libs/hwui/tests/common/scenes/ShadowGrid2Animation.cpp index 0a69b62fe615..bdc991ba1890 100644 --- a/libs/hwui/tests/common/scenes/ShadowGrid2Animation.cpp +++ b/libs/hwui/tests/common/scenes/ShadowGrid2Animation.cpp @@ -19,15 +19,14 @@ class ShadowGrid2Animation; static TestScene::Registrar _ShadowGrid2(TestScene::Info{ - "shadowgrid2", - "A dense grid of rounded rects that cast a shadow. This is a higher CPU load " - "variant of shadowgrid. Very high CPU load, high GPU load.", - TestScene::simpleCreateScene<ShadowGrid2Animation> -}); + "shadowgrid2", + "A dense grid of rounded rects that cast a shadow. This is a higher CPU load " + "variant of shadowgrid. Very high CPU load, high GPU load.", + TestScene::simpleCreateScene<ShadowGrid2Animation>}); class ShadowGrid2Animation : public TestScene { public: - std::vector< sp<RenderNode> > cards; + std::vector<sp<RenderNode> > cards; void createContent(int width, int height, Canvas& canvas) override { canvas.drawColor(0xFFFFFFFF, SkBlendMode::kSrcOver); canvas.insertReorderBarrier(true); @@ -50,14 +49,16 @@ public: cards[ci]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y); } } + private: sp<RenderNode> createCard(int x, int y, int width, int height) { return TestUtils::createNode(x, y, x + width, y + height, - [width, height](RenderProperties& props, Canvas& canvas) { - props.setElevation(dp(16)); - props.mutableOutline().setRoundRect(0, 0, width, height, dp(6), 1); - props.mutableOutline().setShouldClip(true); - canvas.drawColor(0xFFEEEEEE, SkBlendMode::kSrcOver); - }); + [width, height](RenderProperties& props, Canvas& canvas) { + props.setElevation(dp(16)); + props.mutableOutline().setRoundRect(0, 0, width, height, + dp(6), 1); + props.mutableOutline().setShouldClip(true); + canvas.drawColor(0xFFEEEEEE, SkBlendMode::kSrcOver); + }); } }; diff --git a/libs/hwui/tests/common/scenes/ShadowGridAnimation.cpp b/libs/hwui/tests/common/scenes/ShadowGridAnimation.cpp index 4a024295cb25..a12fd4d69280 100644 --- a/libs/hwui/tests/common/scenes/ShadowGridAnimation.cpp +++ b/libs/hwui/tests/common/scenes/ShadowGridAnimation.cpp @@ -19,15 +19,14 @@ class ShadowGridAnimation; static TestScene::Registrar _ShadowGrid(TestScene::Info{ - "shadowgrid", - "A grid of rounded rects that cast a shadow. Simplified scenario of an " - "Android TV-style launcher interface. High CPU/GPU load.", - TestScene::simpleCreateScene<ShadowGridAnimation> -}); + "shadowgrid", + "A grid of rounded rects that cast a shadow. Simplified scenario of an " + "Android TV-style launcher interface. High CPU/GPU load.", + TestScene::simpleCreateScene<ShadowGridAnimation>}); class ShadowGridAnimation : public TestScene { public: - std::vector< sp<RenderNode> > cards; + std::vector<sp<RenderNode> > cards; void createContent(int width, int height, Canvas& canvas) override { canvas.drawColor(0xFFFFFFFF, SkBlendMode::kSrcOver); canvas.insertReorderBarrier(true); @@ -50,14 +49,16 @@ public: cards[ci]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y); } } + private: sp<RenderNode> createCard(int x, int y, int width, int height) { return TestUtils::createNode(x, y, x + width, y + height, - [width, height](RenderProperties& props, Canvas& canvas) { - props.setElevation(dp(16)); - props.mutableOutline().setRoundRect(0, 0, width, height, dp(6), 1); - props.mutableOutline().setShouldClip(true); - canvas.drawColor(0xFFEEEEEE, SkBlendMode::kSrcOver); - }); + [width, height](RenderProperties& props, Canvas& canvas) { + props.setElevation(dp(16)); + props.mutableOutline().setRoundRect(0, 0, width, height, + dp(6), 1); + props.mutableOutline().setShouldClip(true); + canvas.drawColor(0xFFEEEEEE, SkBlendMode::kSrcOver); + }); } }; diff --git a/libs/hwui/tests/common/scenes/ShadowShaderAnimation.cpp b/libs/hwui/tests/common/scenes/ShadowShaderAnimation.cpp index fac3968ff3d4..9f599100200e 100644 --- a/libs/hwui/tests/common/scenes/ShadowShaderAnimation.cpp +++ b/libs/hwui/tests/common/scenes/ShadowShaderAnimation.cpp @@ -19,23 +19,22 @@ class ShadowShaderAnimation; static TestScene::Registrar _ShadowShader(TestScene::Info{ - "shadowshader", - "A set of overlapping shadowed areas with simple tessellation useful for" - " benchmarking shadow shader performance.", - TestScene::simpleCreateScene<ShadowShaderAnimation> -}); + "shadowshader", + "A set of overlapping shadowed areas with simple tessellation useful for" + " benchmarking shadow shader performance.", + TestScene::simpleCreateScene<ShadowShaderAnimation>}); class ShadowShaderAnimation : public TestScene { public: - std::vector< sp<RenderNode> > cards; + std::vector<sp<RenderNode> > cards; void createContent(int width, int height, Canvas& canvas) override { canvas.drawColor(0xFFFFFFFF, SkBlendMode::kSrcOver); canvas.insertReorderBarrier(true); int outset = 50; for (int i = 0; i < 10; i++) { - sp<RenderNode> card = createCard(outset, outset, - width - (outset * 2), height - (outset * 2)); + sp<RenderNode> card = + createCard(outset, outset, width - (outset * 2), height - (outset * 2)); canvas.drawRenderNode(card.get()); cards.push_back(card); } @@ -50,19 +49,24 @@ public: cards[ci]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y); } } + private: sp<RenderNode> createCard(int x, int y, int width, int height) { return TestUtils::createNode(x, y, x + width, y + height, - [width, height](RenderProperties& props, Canvas& canvas) { - props.setElevation(1000); + [width, height](RenderProperties& props, Canvas& canvas) { + props.setElevation(1000); - // Set 0 radius, no clipping, so shadow is easy to compute. Slightly transparent outline - // to signal contents aren't opaque (not necessary though, as elevation is so high, no - // inner content to cut out) - props.mutableOutline().setRoundRect(0, 0, width, height, 0, 0.99f); - props.mutableOutline().setShouldClip(false); + // Set 0 radius, no clipping, so shadow is easy to compute. + // Slightly transparent outline + // to signal contents aren't opaque (not necessary though, + // as elevation is so high, no + // inner content to cut out) + props.mutableOutline().setRoundRect(0, 0, width, height, 0, + 0.99f); + props.mutableOutline().setShouldClip(false); - // don't draw anything to card's canvas - we just want the shadow - }); + // don't draw anything to card's canvas - we just want the + // shadow + }); } }; diff --git a/libs/hwui/tests/common/scenes/ShapeAnimation.cpp b/libs/hwui/tests/common/scenes/ShapeAnimation.cpp index 09e70ebf7b5f..0d87776e083e 100644 --- a/libs/hwui/tests/common/scenes/ShapeAnimation.cpp +++ b/libs/hwui/tests/common/scenes/ShapeAnimation.cpp @@ -21,79 +21,75 @@ class ShapeAnimation; -static TestScene::Registrar _Shapes(TestScene::Info{ - "shapes", - "A grid of shape drawing test cases.", - TestScene::simpleCreateScene<ShapeAnimation> -}); +static TestScene::Registrar _Shapes(TestScene::Info{"shapes", "A grid of shape drawing test cases.", + TestScene::simpleCreateScene<ShapeAnimation>}); class ShapeAnimation : public TestScene { public: sp<RenderNode> card; void createContent(int width, int height, Canvas& canvas) override { - card = TestUtils::createNode(0, 0, width, height, - [width](RenderProperties& props, Canvas& canvas) { - std::function<void(Canvas&, float, const SkPaint&)> ops[] = { - [](Canvas& canvas, float size, const SkPaint& paint) { - canvas.drawArc(0, 0, size, size, 50, 189, true, paint); - }, - [](Canvas& canvas, float size, const SkPaint& paint) { - canvas.drawOval(0, 0, size, size, paint); - }, - [](Canvas& canvas, float size, const SkPaint& paint) { - SkPath diamondPath; - diamondPath.moveTo(size / 2, 0); - diamondPath.lineTo(size, size / 2); - diamondPath.lineTo(size / 2, size); - diamondPath.lineTo(0, size / 2); - diamondPath.close(); - canvas.drawPath(diamondPath, paint); - }, - [](Canvas& canvas, float size, const SkPaint& paint) { - float data[] = {0, 0, size, size, 0, size, size, 0 }; - canvas.drawLines(data, sizeof(data) / sizeof(float), paint); - }, - [](Canvas& canvas, float size, const SkPaint& paint) { - float data[] = {0, 0, size, size, 0, size, size, 0 }; - canvas.drawPoints(data, sizeof(data) / sizeof(float), paint); - }, - [](Canvas& canvas, float size, const SkPaint& paint) { - canvas.drawRect(0, 0, size, size, paint); - }, - [](Canvas& canvas, float size, const SkPaint& paint) { - float rad = size / 4; - canvas.drawRoundRect(0, 0, size, size, rad, rad, paint); - } - }; - float cellSpace = dp(4); - float cellSize = floorf(width / 7 - cellSpace); + card = TestUtils::createNode( + 0, 0, width, height, [width](RenderProperties& props, Canvas& canvas) { + std::function<void(Canvas&, float, const SkPaint&)> ops[] = { + [](Canvas& canvas, float size, const SkPaint& paint) { + canvas.drawArc(0, 0, size, size, 50, 189, true, paint); + }, + [](Canvas& canvas, float size, const SkPaint& paint) { + canvas.drawOval(0, 0, size, size, paint); + }, + [](Canvas& canvas, float size, const SkPaint& paint) { + SkPath diamondPath; + diamondPath.moveTo(size / 2, 0); + diamondPath.lineTo(size, size / 2); + diamondPath.lineTo(size / 2, size); + diamondPath.lineTo(0, size / 2); + diamondPath.close(); + canvas.drawPath(diamondPath, paint); + }, + [](Canvas& canvas, float size, const SkPaint& paint) { + float data[] = {0, 0, size, size, 0, size, size, 0}; + canvas.drawLines(data, sizeof(data) / sizeof(float), paint); + }, + [](Canvas& canvas, float size, const SkPaint& paint) { + float data[] = {0, 0, size, size, 0, size, size, 0}; + canvas.drawPoints(data, sizeof(data) / sizeof(float), paint); + }, + [](Canvas& canvas, float size, const SkPaint& paint) { + canvas.drawRect(0, 0, size, size, paint); + }, + [](Canvas& canvas, float size, const SkPaint& paint) { + float rad = size / 4; + canvas.drawRoundRect(0, 0, size, size, rad, rad, paint); + }}; + float cellSpace = dp(4); + float cellSize = floorf(width / 7 - cellSpace); - // each combination of strokeWidth + style gets a column - int outerCount = canvas.save(SaveFlags::MatrixClip); - SkPaint paint; - paint.setAntiAlias(true); - SkPaint::Style styles[] = { - SkPaint::kStroke_Style, SkPaint::kFill_Style, SkPaint::kStrokeAndFill_Style }; - for (auto style : styles) { - paint.setStyle(style); - for (auto strokeWidth : { 0.0f, 0.5f, 8.0f }) { - paint.setStrokeWidth(strokeWidth); - // fill column with each op - int middleCount = canvas.save(SaveFlags::MatrixClip); - for (auto op : ops) { - int innerCount = canvas.save(SaveFlags::MatrixClip); - canvas.clipRect(0, 0, cellSize, cellSize, SkClipOp::kIntersect); - canvas.drawColor(Color::White, SkBlendMode::kSrcOver); - op(canvas, cellSize, paint); - canvas.restoreToCount(innerCount); - canvas.translate(cellSize + cellSpace, 0); + // each combination of strokeWidth + style gets a column + int outerCount = canvas.save(SaveFlags::MatrixClip); + SkPaint paint; + paint.setAntiAlias(true); + SkPaint::Style styles[] = {SkPaint::kStroke_Style, SkPaint::kFill_Style, + SkPaint::kStrokeAndFill_Style}; + for (auto style : styles) { + paint.setStyle(style); + for (auto strokeWidth : {0.0f, 0.5f, 8.0f}) { + paint.setStrokeWidth(strokeWidth); + // fill column with each op + int middleCount = canvas.save(SaveFlags::MatrixClip); + for (auto op : ops) { + int innerCount = canvas.save(SaveFlags::MatrixClip); + canvas.clipRect(0, 0, cellSize, cellSize, SkClipOp::kIntersect); + canvas.drawColor(Color::White, SkBlendMode::kSrcOver); + op(canvas, cellSize, paint); + canvas.restoreToCount(innerCount); + canvas.translate(cellSize + cellSpace, 0); + } + canvas.restoreToCount(middleCount); + canvas.translate(0, cellSize + cellSpace); + } } - canvas.restoreToCount(middleCount); - canvas.translate(0, cellSize + cellSpace); - } - } - canvas.restoreToCount(outerCount); - }); + canvas.restoreToCount(outerCount); + }); canvas.drawColor(Color::Grey_500, SkBlendMode::kSrcOver); canvas.drawRenderNode(card.get()); } diff --git a/libs/hwui/tests/common/scenes/SimpleColorMatrixAnimation.cpp b/libs/hwui/tests/common/scenes/SimpleColorMatrixAnimation.cpp index a63a5852a9ff..ff0cb3705cb8 100644 --- a/libs/hwui/tests/common/scenes/SimpleColorMatrixAnimation.cpp +++ b/libs/hwui/tests/common/scenes/SimpleColorMatrixAnimation.cpp @@ -22,15 +22,15 @@ class SimpleColorMatrixAnimation; static TestScene::Registrar _SimpleColorMatrix(TestScene::Info{ - "simpleColorMatrix", - "A color matrix shader benchmark for the simple scale/translate case, which has R, G, and B " - "all scaled and translated the same amount.", - TestScene::simpleCreateScene<SimpleColorMatrixAnimation> -}); + "simpleColorMatrix", + "A color matrix shader benchmark for the simple scale/translate case, which has R, G, and " + "B " + "all scaled and translated the same amount.", + TestScene::simpleCreateScene<SimpleColorMatrixAnimation>}); class SimpleColorMatrixAnimation : public TestScene { public: - std::vector< sp<RenderNode> > cards; + std::vector<sp<RenderNode> > cards; void createContent(int width, int height, Canvas& canvas) override { canvas.drawColor(Color::White, SkBlendMode::kSrcOver); @@ -46,38 +46,41 @@ public: cards[ci]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y); } } + private: sp<RenderNode> createCard(int x, int y, int width, int height) { - return TestUtils::createNode(x, y, x + width, y + height, + return TestUtils::createNode( + x, y, x + width, y + height, [width, height](RenderProperties& props, Canvas& canvas) { - SkPaint paint; - float matrix[20] = { 0 }; + SkPaint paint; + float matrix[20] = {0}; - // Simple scale/translate case where R, G, and B are all treated equivalently - matrix[SkColorMatrix::kR_Scale] = 1.1f; - matrix[SkColorMatrix::kG_Scale] = 1.1f; - matrix[SkColorMatrix::kB_Scale] = 1.1f; - matrix[SkColorMatrix::kA_Scale] = 0.5f; + // Simple scale/translate case where R, G, and B are all treated equivalently + matrix[SkColorMatrix::kR_Scale] = 1.1f; + matrix[SkColorMatrix::kG_Scale] = 1.1f; + matrix[SkColorMatrix::kB_Scale] = 1.1f; + matrix[SkColorMatrix::kA_Scale] = 0.5f; - matrix[SkColorMatrix::kR_Trans] = 5.0f; - matrix[SkColorMatrix::kG_Trans] = 5.0f; - matrix[SkColorMatrix::kB_Trans] = 5.0f; - matrix[SkColorMatrix::kA_Trans] = 10.0f; + matrix[SkColorMatrix::kR_Trans] = 5.0f; + matrix[SkColorMatrix::kG_Trans] = 5.0f; + matrix[SkColorMatrix::kB_Trans] = 5.0f; + matrix[SkColorMatrix::kA_Trans] = 10.0f; - paint.setColorFilter(SkColorFilter::MakeMatrixFilterRowMajor255(matrix)); + paint.setColorFilter(SkColorFilter::MakeMatrixFilterRowMajor255(matrix)); - // set a shader so it's not likely for the matrix to be optimized away (since a clever - // enough renderer might apply it directly to the paint color) - float pos[] = { 0, 1 }; - SkPoint pts[] = { SkPoint::Make(0, 0), SkPoint::Make(width, height) }; - SkColor colors[2] = { Color::DeepPurple_500, Color::DeepOrange_500 }; - paint.setShader(SkGradientShader::MakeLinear(pts, colors, pos, 2, - SkShader::kClamp_TileMode)); + // set a shader so it's not likely for the matrix to be optimized away (since a + // clever + // enough renderer might apply it directly to the paint color) + float pos[] = {0, 1}; + SkPoint pts[] = {SkPoint::Make(0, 0), SkPoint::Make(width, height)}; + SkColor colors[2] = {Color::DeepPurple_500, Color::DeepOrange_500}; + paint.setShader(SkGradientShader::MakeLinear(pts, colors, pos, 2, + SkShader::kClamp_TileMode)); - // overdraw several times to emphasize shader cost - for (int i = 0; i < 10; i++) { - canvas.drawRect(i, i, width, height, paint); - } - }); + // overdraw several times to emphasize shader cost + for (int i = 0; i < 10; i++) { + canvas.drawRect(i, i, width, height, paint); + } + }); } }; diff --git a/libs/hwui/tests/common/scenes/SimpleGradientAnimation.cpp b/libs/hwui/tests/common/scenes/SimpleGradientAnimation.cpp index 053eb6dee31c..016c65c17c4c 100644 --- a/libs/hwui/tests/common/scenes/SimpleGradientAnimation.cpp +++ b/libs/hwui/tests/common/scenes/SimpleGradientAnimation.cpp @@ -14,7 +14,6 @@ * limitations under the License. */ - #include "TestSceneBase.h" #include <SkGradientShader.h> @@ -22,14 +21,13 @@ class SimpleGradientAnimation; static TestScene::Registrar _SimpleGradient(TestScene::Info{ - "simpleGradient", - "A benchmark of shader performance of linear, 2 color gradients with black in them.", - TestScene::simpleCreateScene<SimpleGradientAnimation> -}); + "simpleGradient", + "A benchmark of shader performance of linear, 2 color gradients with black in them.", + TestScene::simpleCreateScene<SimpleGradientAnimation>}); class SimpleGradientAnimation : public TestScene { public: - std::vector< sp<RenderNode> > cards; + std::vector<sp<RenderNode> > cards; void createContent(int width, int height, Canvas& canvas) override { canvas.drawColor(Color::White, SkBlendMode::kSrcOver); @@ -45,21 +43,23 @@ public: cards[ci]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y); } } + private: sp<RenderNode> createCard(int x, int y, int width, int height) { - return TestUtils::createNode(x, y, x + width, y + height, + return TestUtils::createNode( + x, y, x + width, y + height, [width, height](RenderProperties& props, Canvas& canvas) { - float pos[] = { 0, 1 }; - SkPoint pts[] = { SkPoint::Make(0, 0), SkPoint::Make(width, height) }; - SkPaint paint; - // overdraw several times to emphasize shader cost - for (int i = 0; i < 10; i++) { - // use i%2 start position to pick 2 color combo with black in it - SkColor colors[3] = { Color::Transparent, Color::Black, Color::Cyan_500 }; - paint.setShader(SkGradientShader::MakeLinear(pts, colors + (i % 2), pos, 2, - SkShader::kClamp_TileMode)); - canvas.drawRect(i, i, width, height, paint); - } - }); + float pos[] = {0, 1}; + SkPoint pts[] = {SkPoint::Make(0, 0), SkPoint::Make(width, height)}; + SkPaint paint; + // overdraw several times to emphasize shader cost + for (int i = 0; i < 10; i++) { + // use i%2 start position to pick 2 color combo with black in it + SkColor colors[3] = {Color::Transparent, Color::Black, Color::Cyan_500}; + paint.setShader(SkGradientShader::MakeLinear(pts, colors + (i % 2), pos, 2, + SkShader::kClamp_TileMode)); + canvas.drawRect(i, i, width, height, paint); + } + }); } }; diff --git a/libs/hwui/tests/common/scenes/TextAnimation.cpp b/libs/hwui/tests/common/scenes/TextAnimation.cpp index 438f877deb3e..a16b17849fc6 100644 --- a/libs/hwui/tests/common/scenes/TextAnimation.cpp +++ b/libs/hwui/tests/common/scenes/TextAnimation.cpp @@ -15,25 +15,20 @@ */ #include "TestSceneBase.h" -#include "utils/Color.h" class TextAnimation; -static TestScene::Registrar _Text(TestScene::Info{ - "text", - "Draws a bunch of text.", - TestScene::simpleCreateScene<TextAnimation> -}); +static TestScene::Registrar _Text(TestScene::Info{"text", "Draws a bunch of text.", + TestScene::simpleCreateScene<TextAnimation>}); class TextAnimation : public TestScene { public: sp<RenderNode> card; void createContent(int width, int height, Canvas& canvas) override { canvas.drawColor(Color::White, SkBlendMode::kSrcOver); - card = TestUtils::createNode(0, 0, width, height, - [](RenderProperties& props, Canvas& canvas) { + card = TestUtils::createNode(0, 0, width, height, [](RenderProperties& props, + Canvas& canvas) { SkPaint paint; - paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); paint.setAntiAlias(true); paint.setTextSize(50); diff --git a/libs/hwui/tests/common/scenes/TvApp.cpp b/libs/hwui/tests/common/scenes/TvApp.cpp index 04fc2d46f946..003d8e92fd9f 100644 --- a/libs/hwui/tests/common/scenes/TvApp.cpp +++ b/libs/hwui/tests/common/scenes/TvApp.cpp @@ -14,40 +14,42 @@ * limitations under the License. */ +#include "SkBlendMode.h" #include "TestSceneBase.h" #include "tests/common/BitmapAllocationTestUtils.h" -#include "SkBlendMode.h" class TvApp; class TvAppNoRoundedCorner; class TvAppColorFilter; class TvAppNoRoundedCornerColorFilter; -static bool _TvApp( - BitmapAllocationTestUtils::registerBitmapAllocationScene<TvApp>( - "tvapp", "A dense grid of cards:" - "with rounded corner, using overlay RenderNode for dimming.")); +static bool _TvApp(BitmapAllocationTestUtils::registerBitmapAllocationScene<TvApp>( + "tvapp", + "A dense grid of cards:" + "with rounded corner, using overlay RenderNode for dimming.")); static bool _TvAppNoRoundedCorner( BitmapAllocationTestUtils::registerBitmapAllocationScene<TvAppNoRoundedCorner>( - "tvapp_norc", "A dense grid of cards:" + "tvapp_norc", + "A dense grid of cards:" "no rounded corner, using overlay RenderNode for dimming")); static bool _TvAppColorFilter( BitmapAllocationTestUtils::registerBitmapAllocationScene<TvAppColorFilter>( - "tvapp_cf", "A dense grid of cards:" + "tvapp_cf", + "A dense grid of cards:" "with rounded corner, using ColorFilter for dimming")); static bool _TvAppNoRoundedCornerColorFilter( BitmapAllocationTestUtils::registerBitmapAllocationScene<TvAppNoRoundedCornerColorFilter>( - "tvapp_norc_cf", "A dense grid of cards:" + "tvapp_norc_cf", + "A dense grid of cards:" "no rounded corner, using ColorFilter for dimming")); class TvApp : public TestScene { public: TvApp(BitmapAllocationTestUtils::BitmapAllocator allocator) - : TestScene() - , mAllocator(allocator) { } + : TestScene(), mAllocator(allocator) {} sp<RenderNode> mBg; std::vector<sp<RenderNode>> mCards; @@ -66,9 +68,7 @@ public: canvas.insertReorderBarrier(true); mSingleBitmap = mAllocator(dp(160), dp(120), kRGBA_8888_SkColorType, - [](SkBitmap& skBitmap) { - skBitmap.eraseColor(0xFF0000FF); - }); + [](SkBitmap& skBitmap) { skBitmap.eraseColor(0xFF0000FF); }); for (int y = dp(18) - dp(178); y < height - dp(18); y += dp(178)) { bool isFirstCard = true; @@ -90,69 +90,63 @@ public: } private: - sp<RenderNode> createBitmapNode(Canvas& canvas, SkColor color, int left, int top, - int width, int height) { - return TestUtils::createNode(left, top, left + width , top + height, + sp<RenderNode> createBitmapNode(Canvas& canvas, SkColor color, int left, int top, int width, + int height) { + return TestUtils::createNode( + left, top, left + width, top + height, [this, width, height, color](RenderProperties& props, Canvas& canvas) { - sk_sp<Bitmap> bitmap = mAllocator(width, height, kRGBA_8888_SkColorType, - [color](SkBitmap& skBitmap) { - skBitmap.eraseColor(color); - }); - canvas.drawBitmap(*bitmap, 0, 0, nullptr); - }); + sk_sp<Bitmap> bitmap = + mAllocator(width, height, kRGBA_8888_SkColorType, + [color](SkBitmap& skBitmap) { skBitmap.eraseColor(color); }); + canvas.drawBitmap(*bitmap, 0, 0, nullptr); + }); } - sp<RenderNode> createSharedBitmapNode(Canvas& canvas, int left, int top, - int width, int height, sk_sp<Bitmap>bitmap) { - return TestUtils::createNode(left, top, left + width , top + height, - [bitmap](RenderProperties& props, Canvas& canvas) { - canvas.drawBitmap(*bitmap, 0, 0, nullptr); - }); + sp<RenderNode> createSharedBitmapNode(Canvas& canvas, int left, int top, int width, int height, + sk_sp<Bitmap> bitmap) { + return TestUtils::createNode(left, top, left + width, top + height, + [bitmap](RenderProperties& props, Canvas& canvas) { + canvas.drawBitmap(*bitmap, 0, 0, nullptr); + }); } - sp<RenderNode> createInfoNode(Canvas& canvas, int left, int top, - int width, int height, const char* text, const char* text2) { - return TestUtils::createNode(left, top, left + width , top + height, - [text, text2](RenderProperties& props, Canvas& canvas) { - canvas.drawColor(0xFFFFEEEE, SkBlendMode::kSrcOver); + sp<RenderNode> createInfoNode(Canvas& canvas, int left, int top, int width, int height, + const char* text, const char* text2) { + return TestUtils::createNode(left, top, left + width, top + height, + [text, text2](RenderProperties& props, Canvas& canvas) { + canvas.drawColor(0xFFFFEEEE, SkBlendMode::kSrcOver); - SkPaint paint; - paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); - paint.setAntiAlias(true); - paint.setTextSize(24); + SkPaint paint; + paint.setAntiAlias(true); + paint.setTextSize(24); - paint.setColor(Color::Black); - TestUtils::drawUtf8ToCanvas(&canvas, text, paint, 10, 30); - paint.setTextSize(20); - TestUtils::drawUtf8ToCanvas(&canvas, text2, paint, 10, 54); + paint.setColor(Color::Black); + TestUtils::drawUtf8ToCanvas(&canvas, text, paint, 10, 30); + paint.setTextSize(20); + TestUtils::drawUtf8ToCanvas(&canvas, text2, paint, 10, 54); - }); + }); } - sp<RenderNode> createColorNode(Canvas& canvas, int left, int top, - int width, int height, SkColor color) { - return TestUtils::createNode(left, top, left + width , top + height, - [color](RenderProperties& props, Canvas& canvas) { - canvas.drawColor(color, SkBlendMode::kSrcOver); - }); + sp<RenderNode> createColorNode(Canvas& canvas, int left, int top, int width, int height, + SkColor color) { + return TestUtils::createNode(left, top, left + width, top + height, + [color](RenderProperties& props, Canvas& canvas) { + canvas.drawColor(color, SkBlendMode::kSrcOver); + }); } - virtual bool useSingleBitmap() { - return false; - } + virtual bool useSingleBitmap() { return false; } - virtual float roundedCornerRadius() { - return dp(2); - } + virtual float roundedCornerRadius() { return dp(2); } // when true, use overlay RenderNode for dimming, otherwise apply a ColorFilter to dim image - virtual bool useOverlay() { - return true; - } + virtual bool useOverlay() { return true; } sp<RenderNode> createCard(int x, int y, int width, int height, bool selected) { - return TestUtils::createNode(x, y, x + width, y + height, - [width, height, selected, this](RenderProperties& props, Canvas& canvas) { + return TestUtils::createNode(x, y, x + width, y + height, [width, height, selected, this]( + RenderProperties& props, + Canvas& canvas) { if (selected) { props.setElevation(dp(16)); props.setScaleX(1.2); @@ -161,12 +155,14 @@ private: props.mutableOutline().setRoundRect(0, 0, width, height, roundedCornerRadius(), 1); props.mutableOutline().setShouldClip(true); - sk_sp<Bitmap> bitmap = useSingleBitmap() ? mSingleBitmap - : mAllocator(width, dp(120), kRGBA_8888_SkColorType, [this](SkBitmap& skBitmap) { - skBitmap.eraseColor(0xFF000000 | ((mSeed << 3) & 0xFF)); - }); - sp<RenderNode> cardImage = createSharedBitmapNode(canvas, 0, 0, width, dp(120), - bitmap); + sk_sp<Bitmap> bitmap = + useSingleBitmap() ? mSingleBitmap + : mAllocator(width, dp(120), kRGBA_8888_SkColorType, + [this](SkBitmap& skBitmap) { + skBitmap.eraseColor(0xFF000000 | + ((mSeed << 3) & 0xFF)); + }); + sp<RenderNode> cardImage = createSharedBitmapNode(canvas, 0, 0, width, dp(120), bitmap); canvas.drawRenderNode(cardImage.get()); mCachedBitmaps.push_back(bitmap); mImages.push_back(cardImage); @@ -176,12 +172,14 @@ private: mSeed++; char buffer2[128]; sprintf(buffer2, "Studio %d", mSeed2++); - sp<RenderNode> infoArea = createInfoNode(canvas, 0, dp(120), width, height, buffer, buffer2); + sp<RenderNode> infoArea = + createInfoNode(canvas, 0, dp(120), width, height, buffer, buffer2); canvas.drawRenderNode(infoArea.get()); mInfoAreas.push_back(infoArea); if (useOverlay()) { - sp<RenderNode> overlayColor = createColorNode(canvas, 0, 0, width, height, 0x00000000); + sp<RenderNode> overlayColor = + createColorNode(canvas, 0, 0, width, height, 0x00000000); canvas.drawRenderNode(overlayColor.get()); mOverlays.push_back(overlayColor); } @@ -196,30 +194,28 @@ private: // re-recording card's canvas, not necessary but to add some burden to CPU std::unique_ptr<Canvas> cardcanvas(Canvas::create_recording_canvas( - card->stagingProperties().getWidth(), - card->stagingProperties().getHeight())); + card->stagingProperties().getWidth(), card->stagingProperties().getHeight())); sp<RenderNode> image = mImages[ci]; sp<RenderNode> infoArea = mInfoAreas[ci]; cardcanvas->drawRenderNode(infoArea.get()); if (useOverlay()) { - cardcanvas->drawRenderNode(image.get()); + cardcanvas->drawRenderNode(image.get()); // re-recording card overlay's canvas, animating overlay color alpha sp<RenderNode> overlay = mOverlays[ci]; - std::unique_ptr<Canvas> canvas(Canvas::create_recording_canvas( - overlay->stagingProperties().getWidth(), - overlay->stagingProperties().getHeight())); + std::unique_ptr<Canvas> canvas( + Canvas::create_recording_canvas(overlay->stagingProperties().getWidth(), + overlay->stagingProperties().getHeight())); canvas->drawColor((curFrame % 150) << 24, SkBlendMode::kSrcOver); overlay->setStagingDisplayList(canvas->finishRecording()); cardcanvas->drawRenderNode(overlay.get()); } else { // re-recording image node's canvas, animating ColorFilter std::unique_ptr<Canvas> canvas(Canvas::create_recording_canvas( - image->stagingProperties().getWidth(), - image->stagingProperties().getHeight())); + image->stagingProperties().getWidth(), image->stagingProperties().getHeight())); SkPaint paint; - sk_sp<SkColorFilter> filter(SkColorFilter::MakeModeFilter((curFrame % 150) << 24, - SkBlendMode::kSrcATop)); + sk_sp<SkColorFilter> filter( + SkColorFilter::MakeModeFilter((curFrame % 150) << 24, SkBlendMode::kSrcATop)); paint.setColorFilter(filter); sk_sp<Bitmap> bitmap = mCachedBitmaps[ci]; canvas->drawBitmap(*bitmap, 0, 0, &paint); @@ -233,42 +229,27 @@ private: class TvAppNoRoundedCorner : public TvApp { public: - TvAppNoRoundedCorner(BitmapAllocationTestUtils::BitmapAllocator allocator) - : TvApp(allocator) { } + TvAppNoRoundedCorner(BitmapAllocationTestUtils::BitmapAllocator allocator) : TvApp(allocator) {} private: - - virtual float roundedCornerRadius() override { - return dp(0); - } + virtual float roundedCornerRadius() override { return dp(0); } }; class TvAppColorFilter : public TvApp { public: - TvAppColorFilter(BitmapAllocationTestUtils::BitmapAllocator allocator) - : TvApp(allocator) { } + TvAppColorFilter(BitmapAllocationTestUtils::BitmapAllocator allocator) : TvApp(allocator) {} private: - - virtual bool useOverlay() override { - return false; - } + virtual bool useOverlay() override { return false; } }; class TvAppNoRoundedCornerColorFilter : public TvApp { public: TvAppNoRoundedCornerColorFilter(BitmapAllocationTestUtils::BitmapAllocator allocator) - : TvApp(allocator) { } + : TvApp(allocator) {} private: + virtual float roundedCornerRadius() override { return dp(0); } - virtual float roundedCornerRadius() override { - return dp(0); - } - - virtual bool useOverlay() override { - return false; - } + virtual bool useOverlay() override { return false; } }; - - diff --git a/libs/hwui/tests/macrobench/TestSceneRunner.cpp b/libs/hwui/tests/macrobench/TestSceneRunner.cpp index f8d63978bb53..9428f532434a 100644 --- a/libs/hwui/tests/macrobench/TestSceneRunner.cpp +++ b/libs/hwui/tests/macrobench/TestSceneRunner.cpp @@ -16,11 +16,11 @@ #include "AnimationContext.h" #include "RenderNode.h" +#include "renderthread/RenderProxy.h" +#include "renderthread/RenderTask.h" #include "tests/common/TestContext.h" #include "tests/common/TestScene.h" #include "tests/common/scenes/TestSceneBase.h" -#include "renderthread/RenderProxy.h" -#include "renderthread/RenderTask.h" #include <benchmark/benchmark.h> #include <gui/Surface.h> @@ -39,7 +39,7 @@ public: } }; -template<class T> +template <class T> class ModifiedMovingAverage { public: explicit ModifiedMovingAverage(int weight) : mWeight(weight) {} @@ -53,9 +53,7 @@ public: return mAverage; } - T average() { - return mAverage; - } + T average() { return mAverage; } private: bool mHasValue = false; @@ -64,8 +62,8 @@ private: }; void outputBenchmarkReport(const TestScene::Info& info, const TestScene::Options& opts, - benchmark::BenchmarkReporter* reporter, RenderProxy* proxy, - double durationInS) { + benchmark::BenchmarkReporter* reporter, RenderProxy* proxy, + double durationInS) { using namespace benchmark; struct ReportInfo { @@ -74,10 +72,8 @@ void outputBenchmarkReport(const TestScene::Info& info, const TestScene::Options }; static std::array<ReportInfo, 4> REPORTS = { - ReportInfo { 50, "_50th" }, - ReportInfo { 90, "_90th" }, - ReportInfo { 95, "_95th" }, - ReportInfo { 99, "_99th" }, + ReportInfo{50, "_50th"}, ReportInfo{90, "_90th"}, ReportInfo{95, "_95th"}, + ReportInfo{99, "_99th"}, }; // Although a vector is used, it must stay with only a single element @@ -111,12 +107,10 @@ void outputBenchmarkReport(const TestScene::Info& info, const TestScene::Options } void run(const TestScene::Info& info, const TestScene::Options& opts, - benchmark::BenchmarkReporter* reporter) { + benchmark::BenchmarkReporter* reporter) { // Switch to the real display gDisplay = getBuiltInDisplay(); - std::unique_ptr<TestScene> scene(info.createScene(opts)); - Properties::forceDrawFrame = true; TestContext testContext; testContext.setRenderOffscreen(opts.renderOffscreen); @@ -126,15 +120,17 @@ void run(const TestScene::Info& info, const TestScene::Options& opts, const int height = gDisplay.h; sp<Surface> surface = testContext.surface(); - sp<RenderNode> rootNode = TestUtils::createNode(0, 0, width, height, - [&scene, width, height](RenderProperties& props, Canvas& canvas) { - props.setClipToBounds(false); - scene->createContent(width, height, canvas); - }); + std::unique_ptr<TestScene> scene(info.createScene(opts)); + scene->renderTarget = surface; + + sp<RenderNode> rootNode = TestUtils::createNode( + 0, 0, width, height, [&scene, width, height](RenderProperties& props, Canvas& canvas) { + props.setClipToBounds(false); + scene->createContent(width, height, canvas); + }); ContextFactory factory; - std::unique_ptr<RenderProxy> proxy(new RenderProxy(false, - rootNode.get(), &factory)); + std::unique_ptr<RenderProxy> proxy(new RenderProxy(false, rootNode.get(), &factory)); proxy->loadSystemProperties(); proxy->initialize(surface); float lightX = width / 2.0; @@ -182,8 +178,7 @@ void run(const TestScene::Info& info, const TestScene::Options& opts, nsecs_t end = systemTime(CLOCK_MONOTONIC); if (reporter) { - outputBenchmarkReport(info, opts, reporter, proxy.get(), - (end - start) / (double) s2ns(1)); + outputBenchmarkReport(info, opts, reporter, proxy.get(), (end - start) / (double)s2ns(1)); } else { proxy->dumpProfileInfo(STDOUT_FILENO, DumpFlags::JankStats); } diff --git a/libs/hwui/tests/macrobench/main.cpp b/libs/hwui/tests/macrobench/main.cpp index 616558f29676..0a9a74eccfc9 100644 --- a/libs/hwui/tests/macrobench/main.cpp +++ b/libs/hwui/tests/macrobench/main.cpp @@ -45,7 +45,7 @@ static TestScene::Options gOpts; std::unique_ptr<benchmark::BenchmarkReporter> gBenchmarkReporter; void run(const TestScene::Info& info, const TestScene::Options& opts, - benchmark::BenchmarkReporter* reporter); + benchmark::BenchmarkReporter* reporter); static void printHelp() { printf(R"( @@ -67,6 +67,7 @@ OPTIONS: --onscreen Render tests on device screen. By default tests are offscreen rendered --benchmark_format Set output format. Possible values are tabular, json, csv + --renderer=TYPE Sets the render pipeline to use. May be opengl, skiagl, or skiavk )"); } @@ -81,7 +82,7 @@ static void listTests() { do { int toPrint = dlen; if (toPrint > 50) { - char* found = (char*) memrchr(col2, ' ', 50); + char* found = (char*)memrchr(col2, ' ', 50); if (found) { toPrint = found - col2; } else { @@ -93,7 +94,8 @@ static void listTests() { col2 += toPrint; dlen -= toPrint; while (*col2 == ' ') { - col2++; dlen--; + col2++; + dlen--; } } while (dlen > 0); printf("\n"); @@ -119,7 +121,7 @@ static void moveToCpuSet(const char* cpusetName) { } pid_t pid = getpid(); - int towrite = snprintf(buffer, BUF_SIZE, "%ld", (long) pid); + int towrite = snprintf(buffer, BUF_SIZE, "%ld", (long)pid); if (towrite >= BUF_SIZE) { fprintf(stderr, "Buffer wasn't large enough?\n"); } else { @@ -144,6 +146,20 @@ static bool setBenchmarkFormat(const char* format) { return true; } +static bool setRenderer(const char* renderer) { + if (!strcmp(renderer, "opengl")) { + Properties::overrideRenderPipelineType(RenderPipelineType::OpenGL); + } else if (!strcmp(renderer, "skiagl")) { + Properties::overrideRenderPipelineType(RenderPipelineType::SkiaGL); + } else if (!strcmp(renderer, "skiavk")) { + Properties::overrideRenderPipelineType(RenderPipelineType::SkiaVulkan); + } else { + fprintf(stderr, "Unknown format '%s'", renderer); + return false; + } + return true; +} + // For options that only exist in long-form. Anything in the // 0-255 range is reserved for short options (which just use their ASCII value) namespace LongOpts { @@ -156,22 +172,23 @@ enum { BenchmarkFormat, Onscreen, Offscreen, + Renderer, }; } static const struct option LONG_OPTIONS[] = { - { "frames", required_argument, nullptr, 'f' }, - { "repeat", required_argument, nullptr, 'r' }, - { "help", no_argument, nullptr, 'h' }, - { "list", no_argument, nullptr, LongOpts::List }, - { "wait-for-gpu", no_argument, nullptr, LongOpts::WaitForGpu }, - { "report-frametime", optional_argument, nullptr, LongOpts::ReportFrametime }, - { "cpuset", required_argument, nullptr, LongOpts::CpuSet }, - { "benchmark_format", required_argument, nullptr, LongOpts::BenchmarkFormat }, - { "onscreen", no_argument, nullptr, LongOpts::Onscreen }, - { "offscreen", no_argument, nullptr, LongOpts::Offscreen }, - { 0, 0, 0, 0 } -}; + {"frames", required_argument, nullptr, 'f'}, + {"repeat", required_argument, nullptr, 'r'}, + {"help", no_argument, nullptr, 'h'}, + {"list", no_argument, nullptr, LongOpts::List}, + {"wait-for-gpu", no_argument, nullptr, LongOpts::WaitForGpu}, + {"report-frametime", optional_argument, nullptr, LongOpts::ReportFrametime}, + {"cpuset", required_argument, nullptr, LongOpts::CpuSet}, + {"benchmark_format", required_argument, nullptr, LongOpts::BenchmarkFormat}, + {"onscreen", no_argument, nullptr, LongOpts::Onscreen}, + {"offscreen", no_argument, nullptr, LongOpts::Offscreen}, + {"renderer", required_argument, nullptr, LongOpts::Renderer}, + {0, 0, 0, 0}}; static const char* SHORT_OPTIONS = "c:r:h"; @@ -181,97 +198,105 @@ void parseOptions(int argc, char* argv[]) { opterr = 0; while (true) { - /* getopt_long stores the option index here. */ int option_index = 0; c = getopt_long(argc, argv, SHORT_OPTIONS, LONG_OPTIONS, &option_index); - if (c == -1) - break; + if (c == -1) break; switch (c) { - case 0: - // Option set a flag, don't need to do anything - // (although none of the current LONG_OPTIONS do this...) - break; - - case LongOpts::List: - listTests(); - exit(EXIT_SUCCESS); - break; - - case 'c': - gOpts.count = atoi(optarg); - if (!gOpts.count) { - fprintf(stderr, "Invalid frames argument '%s'\n", optarg); - error = true; - } - break; + case 0: + // Option set a flag, don't need to do anything + // (although none of the current LONG_OPTIONS do this...) + break; - case 'r': - gRepeatCount = atoi(optarg); - if (!gRepeatCount) { - fprintf(stderr, "Invalid repeat argument '%s'\n", optarg); - error = true; - } else { - gRepeatCount = (gRepeatCount > 0 ? gRepeatCount : INT_MAX); - } - break; + case LongOpts::List: + listTests(); + exit(EXIT_SUCCESS); + break; - case LongOpts::ReportFrametime: - if (optarg) { - gOpts.reportFrametimeWeight = atoi(optarg); - if (!gOpts.reportFrametimeWeight) { - fprintf(stderr, "Invalid report frametime weight '%s'\n", optarg); + case 'c': + gOpts.count = atoi(optarg); + if (!gOpts.count) { + fprintf(stderr, "Invalid frames argument '%s'\n", optarg); error = true; } - } else { - gOpts.reportFrametimeWeight = 10; - } - break; + break; - case LongOpts::WaitForGpu: - Properties::waitForGpuCompletion = true; - break; + case 'r': + gRepeatCount = atoi(optarg); + if (!gRepeatCount) { + fprintf(stderr, "Invalid repeat argument '%s'\n", optarg); + error = true; + } else { + gRepeatCount = (gRepeatCount > 0 ? gRepeatCount : INT_MAX); + } + break; - case LongOpts::CpuSet: - if (!optarg) { - error = true; + case LongOpts::ReportFrametime: + if (optarg) { + gOpts.reportFrametimeWeight = atoi(optarg); + if (!gOpts.reportFrametimeWeight) { + fprintf(stderr, "Invalid report frametime weight '%s'\n", optarg); + error = true; + } + } else { + gOpts.reportFrametimeWeight = 10; + } break; - } - moveToCpuSet(optarg); - break; - case LongOpts::BenchmarkFormat: - if (!optarg) { - error = true; + case LongOpts::WaitForGpu: + Properties::waitForGpuCompletion = true; break; - } - if (!setBenchmarkFormat(optarg)) { - error = true; - } - break; - case LongOpts::Onscreen: - gOpts.renderOffscreen = false; - break; + case LongOpts::CpuSet: + if (!optarg) { + error = true; + break; + } + moveToCpuSet(optarg); + break; - case LongOpts::Offscreen: - gOpts.renderOffscreen = true; - break; + case LongOpts::BenchmarkFormat: + if (!optarg) { + error = true; + break; + } + if (!setBenchmarkFormat(optarg)) { + error = true; + } + break; - case 'h': - printHelp(); - exit(EXIT_SUCCESS); - break; + case LongOpts::Renderer: + if (!optarg) { + error = true; + break; + } + if (!setRenderer(optarg)) { + error = true; + } + break; + + case LongOpts::Onscreen: + gOpts.renderOffscreen = false; + break; - case '?': - fprintf(stderr, "Unrecognized option '%s'\n", argv[optind - 1]); + case LongOpts::Offscreen: + gOpts.renderOffscreen = true; + break; + + case 'h': + printHelp(); + exit(EXIT_SUCCESS); + break; + + case '?': + fprintf(stderr, "Unrecognized option '%s'\n", argv[optind - 1]); // fall-through - default: - error = true; - break; + default: + error = true; + break; } } diff --git a/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp b/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp index f166c5ceb974..0aaf7731c927 100644 --- a/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp +++ b/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp @@ -114,7 +114,7 @@ void BM_DisplayListCanvas_record_simpleBitmapView(benchmark::State& benchState) } BENCHMARK(BM_DisplayListCanvas_record_simpleBitmapView); -class NullClient: public CanvasStateClient { +class NullClient : public CanvasStateClient { void onViewportInitialized() override {} void onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) {} GLuint getTargetFbo() const override { return 0; } @@ -161,8 +161,7 @@ void BM_CanvasState_translate(benchmark::State& benchState) { BENCHMARK(BM_CanvasState_translate); void BM_DisplayListCanvas_basicViewGroupDraw(benchmark::State& benchState) { - sp<RenderNode> child = TestUtils::createNode(50, 50, 100, 100, - [](auto& props, auto& canvas) { + sp<RenderNode> child = TestUtils::createNode(50, 50, 100, 100, [](auto& props, auto& canvas) { canvas.drawColor(0xFFFFFFFF, SkBlendMode::kSrcOver); }); @@ -171,8 +170,7 @@ void BM_DisplayListCanvas_basicViewGroupDraw(benchmark::State& benchState) { while (benchState.KeepRunning()) { canvas->resetRecording(200, 200); - canvas->setHighContrastText(false); - canvas->translate(0, 0); // mScrollX, mScrollY + canvas->translate(0, 0); // mScrollX, mScrollY // Clip to padding // Can expect ~25% of views to have clip to padding with a non-null padding diff --git a/libs/hwui/tests/microbench/FontBench.cpp b/libs/hwui/tests/microbench/FontBench.cpp index df3d041e722d..4e9b540a5c3d 100644 --- a/libs/hwui/tests/microbench/FontBench.cpp +++ b/libs/hwui/tests/microbench/FontBench.cpp @@ -37,8 +37,8 @@ void BM_FontRenderer_precache_cachehits(benchmark::State& state) { std::vector<float> positions; float totalAdvance; uirenderer::Rect bounds; - TestUtils::layoutTextUnscaled(paint, "This is a test", - &glyphs, &positions, &totalAdvance, &bounds); + TestUtils::layoutTextUnscaled(paint, "This is a test", &glyphs, &positions, &totalAdvance, + &bounds); fontRenderer.precache(&paint, glyphs.data(), glyphs.size(), SkMatrix::I()); diff --git a/libs/hwui/tests/microbench/FrameBuilderBench.cpp b/libs/hwui/tests/microbench/FrameBuilderBench.cpp index a5e85df22c8e..b6217665d743 100644 --- a/libs/hwui/tests/microbench/FrameBuilderBench.cpp +++ b/libs/hwui/tests/microbench/FrameBuilderBench.cpp @@ -16,17 +16,17 @@ #include <benchmark/benchmark.h> -#include "BakedOpState.h" #include "BakedOpDispatcher.h" #include "BakedOpRenderer.h" +#include "BakedOpState.h" #include "FrameBuilder.h" #include "LayerUpdateQueue.h" #include "RecordedOp.h" #include "RecordingCanvas.h" +#include "Vector.h" #include "tests/common/TestContext.h" #include "tests/common/TestScene.h" #include "tests/common/TestUtils.h" -#include "Vector.h" #include <vector> @@ -35,25 +35,25 @@ using namespace android::uirenderer; using namespace android::uirenderer::renderthread; using namespace android::uirenderer::test; -const FrameBuilder::LightGeometry sLightGeometry = { {100, 100, 100}, 50}; -const BakedOpRenderer::LightInfo sLightInfo = { 128, 128 }; +const FrameBuilder::LightGeometry sLightGeometry = {{100, 100, 100}, 50}; +const BakedOpRenderer::LightInfo sLightInfo = {128, 128}; static sp<RenderNode> createTestNode() { - auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200, - [](RenderProperties& props, RecordingCanvas& canvas) { - sk_sp<Bitmap> bitmap(TestUtils::createBitmap(10, 10)); - SkPaint paint; - - // Alternate between drawing rects and bitmaps, with bitmaps overlapping rects. - // Rects don't overlap bitmaps, so bitmaps should be brought to front as a group. - canvas.save(SaveFlags::MatrixClip); - for (int i = 0; i < 30; i++) { - canvas.translate(0, 10); - canvas.drawRect(0, 0, 10, 10, paint); - canvas.drawBitmap(*bitmap, 5, 0, nullptr); - } - canvas.restore(); - }); + auto node = TestUtils::createNode<RecordingCanvas>( + 0, 0, 200, 200, [](RenderProperties& props, RecordingCanvas& canvas) { + sk_sp<Bitmap> bitmap(TestUtils::createBitmap(10, 10)); + SkPaint paint; + + // Alternate between drawing rects and bitmaps, with bitmaps overlapping rects. + // Rects don't overlap bitmaps, so bitmaps should be brought to front as a group. + canvas.save(SaveFlags::MatrixClip); + for (int i = 0; i < 30; i++) { + canvas.translate(0, 10); + canvas.drawRect(0, 0, 10, 10, paint); + canvas.drawBitmap(*bitmap, 5, 0, nullptr); + } + canvas.restore(); + }); TestUtils::syncHierarchyPropertiesAndDisplayList(node); return node; } @@ -62,8 +62,8 @@ void BM_FrameBuilder_defer(benchmark::State& state) { TestUtils::runOnRenderThread([&state](RenderThread& thread) { auto node = createTestNode(); while (state.KeepRunning()) { - FrameBuilder frameBuilder(SkRect::MakeWH(100, 200), 100, 200, - sLightGeometry, Caches::getInstance()); + FrameBuilder frameBuilder(SkRect::MakeWH(100, 200), 100, 200, sLightGeometry, + Caches::getInstance()); frameBuilder.deferRenderNode(*node); benchmark::DoNotOptimize(&frameBuilder); } @@ -79,8 +79,7 @@ void BM_FrameBuilder_deferAndRender(benchmark::State& state) { Caches& caches = Caches::getInstance(); while (state.KeepRunning()) { - FrameBuilder frameBuilder(SkRect::MakeWH(100, 200), 100, 200, - sLightGeometry, caches); + FrameBuilder frameBuilder(SkRect::MakeWH(100, 200), 100, 200, sLightGeometry, caches); frameBuilder.deferRenderNode(*node); BakedOpRenderer renderer(caches, renderState, true, false, sLightInfo); @@ -92,16 +91,17 @@ void BM_FrameBuilder_deferAndRender(benchmark::State& state) { BENCHMARK(BM_FrameBuilder_deferAndRender); static sp<RenderNode> getSyncedSceneNode(const char* sceneName) { - gDisplay = getBuiltInDisplay(); // switch to real display if present + gDisplay = getBuiltInDisplay(); // switch to real display if present TestContext testContext; TestScene::Options opts; std::unique_ptr<TestScene> scene(TestScene::testMap()[sceneName].createScene(opts)); - sp<RenderNode> rootNode = TestUtils::createNode<RecordingCanvas>(0, 0, gDisplay.w, gDisplay.h, - [&scene](RenderProperties& props, RecordingCanvas& canvas) { - scene->createContent(gDisplay.w, gDisplay.h, canvas); - }); + sp<RenderNode> rootNode = TestUtils::createNode<RecordingCanvas>( + 0, 0, gDisplay.w, gDisplay.h, + [&scene](RenderProperties& props, RecordingCanvas& canvas) { + scene->createContent(gDisplay.w, gDisplay.h, canvas); + }); TestUtils::syncHierarchyPropertiesAndDisplayList(rootNode); return rootNode; @@ -117,9 +117,8 @@ void BM_FrameBuilder_defer_scene(benchmark::State& state) { state.SetLabel(sceneName); auto node = getSyncedSceneNode(sceneName); while (state.KeepRunning()) { - FrameBuilder frameBuilder(SkRect::MakeWH(gDisplay.w, gDisplay.h), - gDisplay.w, gDisplay.h, - sLightGeometry, Caches::getInstance()); + FrameBuilder frameBuilder(SkRect::MakeWH(gDisplay.w, gDisplay.h), gDisplay.w, + gDisplay.h, sLightGeometry, Caches::getInstance()); frameBuilder.deferRenderNode(*node); benchmark::DoNotOptimize(&frameBuilder); } @@ -137,9 +136,8 @@ void BM_FrameBuilder_deferAndRender_scene(benchmark::State& state) { Caches& caches = Caches::getInstance(); while (state.KeepRunning()) { - FrameBuilder frameBuilder(SkRect::MakeWH(gDisplay.w, gDisplay.h), - gDisplay.w, gDisplay.h, - sLightGeometry, Caches::getInstance()); + FrameBuilder frameBuilder(SkRect::MakeWH(gDisplay.w, gDisplay.h), gDisplay.w, + gDisplay.h, sLightGeometry, Caches::getInstance()); frameBuilder.deferRenderNode(*node); BakedOpRenderer renderer(caches, renderState, true, false, sLightInfo); diff --git a/libs/hwui/tests/microbench/PathParserBench.cpp b/libs/hwui/tests/microbench/PathParserBench.cpp index b43c4c3b63c1..00ae8c11124f 100644 --- a/libs/hwui/tests/microbench/PathParserBench.cpp +++ b/libs/hwui/tests/microbench/PathParserBench.cpp @@ -24,7 +24,9 @@ using namespace android; using namespace android::uirenderer; -static const char* sPathString = "M 1 1 m 2 2, l 3 3 L 3 3 H 4 h4 V5 v5, Q6 6 6 6 q 6 6 6 6t 7 7 T 7 7 C 8 8 8 8 8 8 c 8 8 8 8 8 8 S 9 9 9 9 s 9 9 9 9 A 10 10 0 1 1 10 10 a 10 10 0 1 1 10 10"; +static const char* sPathString = + "M 1 1 m 2 2, l 3 3 L 3 3 H 4 h4 V5 v5, Q6 6 6 6 q 6 6 6 6t 7 7 T 7 7 C 8 8 8 8 8 8 c 8 8 " + "8 8 8 8 S 9 9 9 9 s 9 9 9 9 A 10 10 0 1 1 10 10 a 10 10 0 1 1 10 10"; void BM_PathParser_parseStringPathForSkPath(benchmark::State& state) { SkPath skPath; diff --git a/libs/hwui/tests/microbench/RenderNodeBench.cpp b/libs/hwui/tests/microbench/RenderNodeBench.cpp index a5bed0026b1c..206dcd58d785 100644 --- a/libs/hwui/tests/microbench/RenderNodeBench.cpp +++ b/libs/hwui/tests/microbench/RenderNodeBench.cpp @@ -30,4 +30,3 @@ void BM_RenderNode_create(benchmark::State& state) { } } BENCHMARK(BM_RenderNode_create); - diff --git a/libs/hwui/tests/microbench/ShadowBench.cpp b/libs/hwui/tests/microbench/ShadowBench.cpp index a0fc6e8f9f53..12da7837e2e9 100644 --- a/libs/hwui/tests/microbench/ShadowBench.cpp +++ b/libs/hwui/tests/microbench/ShadowBench.cpp @@ -18,9 +18,9 @@ #include "Matrix.h" #include "Rect.h" +#include "TessellationCache.h" #include "Vector.h" #include "VertexBuffer.h" -#include "TessellationCache.h" #include <SkPath.h> @@ -40,22 +40,13 @@ struct ShadowTestData { void createShadowTestData(ShadowTestData* out) { static float SAMPLE_DRAW_TRANSFORM[] = { - 1, 0, 0, 0, - 0, 1, 0, 0, - 0, 0, 1, 0, - 0, 0, 0, 1, + 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, }; static float SAMPLE_CASTERXY[] = { - 1, 0, 0, 0, - 0, 1, 0, 0, - 0, 0, 1, 0, - 32, 32, 0, 1, + 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 32, 32, 0, 1, }; static float SAMPLE_CASTERZ[] = { - 1, 0, 0, 0, - 0, 1, 0, 0, - 0, 0, 1, 0, - 32, 32, 32, 1, + 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 32, 32, 32, 1, }; static Rect SAMPLE_CLIP(0, 0, 1536, 2048); static Vector3 SAMPLE_LIGHT_CENTER{768, -400, 1600}; @@ -69,12 +60,11 @@ void createShadowTestData(ShadowTestData* out) { out->lightRadius = SAMPLE_LIGHT_RADIUS; } -static inline void tessellateShadows(ShadowTestData& testData, bool opaque, - const SkPath& shape, VertexBuffer* ambient, VertexBuffer* spot) { - tessellateShadows(&testData.drawTransform, &testData.localClip, - opaque, &shape, &testData.casterTransformXY, - &testData.casterTransformZ, testData.lightCenter, - testData.lightRadius, *ambient, *spot); +static inline void tessellateShadows(ShadowTestData& testData, bool opaque, const SkPath& shape, + VertexBuffer* ambient, VertexBuffer* spot) { + tessellateShadows(&testData.drawTransform, &testData.localClip, opaque, &shape, + &testData.casterTransformXY, &testData.casterTransformZ, testData.lightCenter, + testData.lightRadius, *ambient, *spot); } void BM_TessellateShadows_roundrect_opaque(benchmark::State& state) { diff --git a/libs/hwui/tests/microbench/TaskManagerBench.cpp b/libs/hwui/tests/microbench/TaskManagerBench.cpp index cf47f273c144..4153baec22b5 100644 --- a/libs/hwui/tests/microbench/TaskManagerBench.cpp +++ b/libs/hwui/tests/microbench/TaskManagerBench.cpp @@ -19,7 +19,9 @@ #include "thread/Task.h" #include "thread/TaskManager.h" #include "thread/TaskProcessor.h" +#include "thread/ThreadBase.h" +#include <atomic> #include <vector> using namespace android; @@ -29,17 +31,18 @@ class TrivialTask : public Task<char> {}; class TrivialProcessor : public TaskProcessor<char> { public: - explicit TrivialProcessor(TaskManager* manager) - : TaskProcessor(manager) {} + explicit TrivialProcessor(TaskManager* manager) : TaskProcessor(manager) {} virtual ~TrivialProcessor() {} - virtual void onProcess(const sp<Task<char> >& task) override { + virtual void onProcess(const sp<Task<char>>& task) override { TrivialTask* t = static_cast<TrivialTask*>(task.get()); t->setResult(reinterpret_cast<intptr_t>(t) % 16 == 0 ? 'a' : 'b'); } }; +class TestThread : public ThreadBase, public virtual RefBase {}; + void BM_TaskManager_allocateTask(benchmark::State& state) { - std::vector<sp<TrivialTask> > tasks; + std::vector<sp<TrivialTask>> tasks; tasks.reserve(state.max_iterations); while (state.KeepRunning()) { @@ -52,7 +55,7 @@ BENCHMARK(BM_TaskManager_allocateTask); void BM_TaskManager_enqueueTask(benchmark::State& state) { TaskManager taskManager; sp<TrivialProcessor> processor(new TrivialProcessor(&taskManager)); - std::vector<sp<TrivialTask> > tasks; + std::vector<sp<TrivialTask>> tasks; tasks.reserve(state.max_iterations); while (state.KeepRunning()) { @@ -70,7 +73,7 @@ BENCHMARK(BM_TaskManager_enqueueTask); void BM_TaskManager_enqueueRunDeleteTask(benchmark::State& state) { TaskManager taskManager; sp<TrivialProcessor> processor(new TrivialProcessor(&taskManager)); - std::vector<sp<TrivialTask> > tasks; + std::vector<sp<TrivialTask>> tasks; tasks.reserve(state.max_iterations); while (state.KeepRunning()) { @@ -86,3 +89,46 @@ void BM_TaskManager_enqueueRunDeleteTask(benchmark::State& state) { state.PauseTiming(); } BENCHMARK(BM_TaskManager_enqueueRunDeleteTask); + +void BM_Thread_enqueueTask(benchmark::State& state) { + sp<TestThread> thread{new TestThread}; + thread->start(); + + atomic_int counter(0); + int expected = 0; + while (state.KeepRunning()) { + expected++; + thread->queue().post([&counter]() { counter++; }); + } + thread->queue().runSync([]() {}); + + thread->requestExit(); + thread->join(); + if (counter != expected) { + printf("Ran %d lambads, should have been %d\n", counter.load(), expected); + } +} +BENCHMARK(BM_Thread_enqueueTask); + +void BM_Thread_enqueueRunDeleteTask(benchmark::State& state) { + sp<TestThread> thread{new TestThread}; + thread->start(); + std::vector<std::future<int>> tasks; + tasks.reserve(state.max_iterations); + + int expected = 0; + while (state.KeepRunning()) { + tasks.emplace_back(thread->queue().async([expected]() -> int { return expected + 1; })); + expected++; + } + state.ResumeTiming(); + expected = 0; + for (auto& future : tasks) { + if (future.get() != ++expected) { + printf("Mismatch expected %d vs. observed %d\n", expected, future.get()); + } + } + tasks.clear(); + state.PauseTiming(); +} +BENCHMARK(BM_Thread_enqueueRunDeleteTask);
\ No newline at end of file diff --git a/libs/hwui/tests/scripts/prep_taieye.sh b/libs/hwui/tests/scripts/prep_taieye.sh new file mode 100755 index 000000000000..503f6d5d5239 --- /dev/null +++ b/libs/hwui/tests/scripts/prep_taieye.sh @@ -0,0 +1,50 @@ +nr=$(adb shell cat /proc/cpuinfo | grep processor | wc -l) +cpubase=/sys/devices/system/cpu + +adb root +adb wait-for-device +adb shell stop vendor.perfd +adb shell stop thermal-engine + +S=1036800 +cpu=0 +# Changing governor and frequency in one core will be automatically applied +# to other cores in the cluster +while [ $((cpu < 4)) -eq 1 ]; do + echo "Setting cpu ${cpu} to $S hz" + adb shell "echo userspace > $cpubase/cpu${cpu}/cpufreq/scaling_governor" + adb shell "echo 1 > $cpubase/cpu${cpu}/online" + adb shell "echo $S > $cpubase/cpu${cpu}/cpufreq/scaling_max_freq" + adb shell "echo $S > $cpubase/cpu${cpu}/cpufreq/scaling_min_freq" + cpu=$(($cpu + 1)) +done + +while [ $((cpu < $nr)) -eq 1 ]; do + echo "disable cpu $cpu" + adb shell "echo 0 > $cpubase/cpu${cpu}/online" + cpu=$(($cpu + 1)) +done + +echo "setting GPU bus and idle timer" +adb shell "echo 0 > /sys/class/kgsl/kgsl-3d0/bus_split" +adb shell "echo 1 > /sys/class/kgsl/kgsl-3d0/force_clk_on" +adb shell "echo 10000 > /sys/class/kgsl/kgsl-3d0/idle_timer" + +#0 762 1144 1525 2288 3143 4173 5195 5859 7759 9887 11863 13763 +adb shell "echo 7759 > /sys/class/devfreq/soc\:qcom,gpubw/min_freq" +adb shell "echo 7759 > /sys/class/devfreq/soc\:qcom,gpubw/max_freq" +adb shell "echo 7759 > /sys/class/devfreq/soc\:qcom,cpubw/min_freq" +adb shell "echo 7759 > /sys/class/devfreq/soc\:qcom,cpubw/max_freq" +adb shell "echo 7759 > /sys/class/devfreq/soc\:qcom,mincpubw/min_freq" +adb shell "echo 7759 > /sys/class/devfreq/soc\:qcom,mincpubw/max_freq" +adb shell "echo 7759 > /sys/class/devfreq/soc\:qcom,memlat-cpu0/min_freq" +adb shell "echo 7759 > /sys/class/devfreq/soc\:qcom,memlat-cpu0/max_freq" + +# 180000000 257000000 342000000 414000000 515000000 596000000 670000000 710000000 +echo "performance mode, 342 MHz" +adb shell "echo performance > /sys/class/kgsl/kgsl-3d0/devfreq/governor" +adb shell "echo 342000000 > /sys/class/kgsl/kgsl-3d0/devfreq/min_freq" +adb shell "echo 342000000 > /sys/class/kgsl/kgsl-3d0/devfreq/max_freq" + +adb shell "echo 4 > /sys/class/kgsl/kgsl-3d0/min_pwrlevel" +adb shell "echo 4 > /sys/class/kgsl/kgsl-3d0/max_pwrlevel" diff --git a/libs/hwui/tests/scripts/skp-capture.sh b/libs/hwui/tests/scripts/skp-capture.sh new file mode 100755 index 000000000000..54fa22929586 --- /dev/null +++ b/libs/hwui/tests/scripts/skp-capture.sh @@ -0,0 +1,116 @@ +#!/bin/sh + +# Copyright 2015 Google Inc. +# +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +if [ -z "$1" ]; then + printf 'Usage:\n skp-capture.sh PACKAGE_NAME OPTIONAL_FRAME_COUNT\n\n' + printf "Use \`adb shell 'pm list packages'\` to get a listing.\n\n" + exit 1 +fi +if ! command -v adb > /dev/null 2>&1; then + if [ -x "${ANDROID_SDK_ROOT}/platform-tools/adb" ]; then + adb() { + "${ANDROID_SDK_ROOT}/platform-tools/adb" "$@" + } + else + echo 'adb missing' + exit 2 + fi +fi +phase1_timeout_seconds=15 +phase2_timeout_seconds=60 +package="$1" +filename="$(date '+%H%M%S').skp" +remote_path="/data/data/${package}/cache/${filename}" +local_path_prefix="$(date '+%Y-%m-%d_%H%M%S')_${package}" +local_path="${local_path_prefix}.skp" +enable_capture_key='debug.hwui.capture_skp_enabled' +enable_capture_value=$(adb shell "getprop '${enable_capture_key}'") +#printf 'captureflag=' "$enable_capture_value" '\n' +if [ -z "$enable_capture_value" ]; then + printf 'Capture SKP property need to be enabled first. Please use\n' + printf "\"adb shell setprop debug.hwui.capture_skp_enabled true\" and then restart\n" + printf "the process.\n\n" + exit 1 +fi +if [ ! -z "$2" ]; then + adb shell "setprop 'debug.hwui.capture_skp_frames' $2" +fi +filename_key='debug.hwui.skp_filename' +adb shell "setprop '${filename_key}' '${remote_path}'" +spin() { + case "$spin" in + 1) printf '\b|';; + 2) printf '\b\\';; + 3) printf '\b-';; + *) printf '\b/';; + esac + spin=$(( ( ${spin:-0} + 1 ) % 4 )) + sleep $1 +} + +banner() { + printf '\n=====================\n' + printf ' %s' "$*" + printf '\n=====================\n' +} +banner '...WAITING...' +adb_test_exist() { + test '0' = "$(adb shell "test -e \"$1\"; echo \$?")"; +} +timeout=$(( $(date +%s) + $phase1_timeout_seconds)) +while ! adb_test_exist "$remote_path"; do + spin 0.05 + if [ $(date +%s) -gt $timeout ] ; then + printf '\bTimed out.\n' + adb shell "setprop '${filename_key}' ''" + exit 3 + fi +done +printf '\b' + +#read -n1 -r -p "Press any key to continue..." key + +banner '...SAVING...' +adb_test_file_nonzero() { + # grab first byte of `du` output + X="$(adb shell "du \"$1\" 2> /dev/null | dd bs=1 count=1 2> /dev/null")" + test "$X" && test "$X" -ne 0 +} +#adb_filesize() { +# adb shell "wc -c \"$1\"" 2> /dev/null | awk '{print $1}' +#} +timeout=$(( $(date +%s) + $phase2_timeout_seconds)) +while ! adb_test_file_nonzero "$remote_path"; do + spin 0.05 + if [ $(date +%s) -gt $timeout ] ; then + printf '\bTimed out.\n' + adb shell "setprop '${filename_key}' ''" + exit 3 + fi +done +printf '\b' + +adb shell "setprop '${filename_key}' ''" + +i=0; while [ $i -lt 10 ]; do spin 0.10; i=$(($i + 1)); done; echo + +adb pull "$remote_path" "$local_path" +if ! [ -f "$local_path" ] ; then + printf "something went wrong with `adb pull`." + exit 4 +fi +adb shell rm "$remote_path" +printf '\nSKP saved to file:\n %s\n\n' "$local_path" +if [ ! -z "$2" ]; then + bridge="_" + adb shell "setprop 'debug.hwui.capture_skp_frames' ''" + for i in $(seq 2 $2); do + adb pull "${remote_path}_${i}" "${local_path_prefix}_${i}.skp" + adb shell rm "${remote_path}_${i}" + done +fi + diff --git a/libs/hwui/tests/unit/BakedOpDispatcherTests.cpp b/libs/hwui/tests/unit/BakedOpDispatcherTests.cpp index b0ef11f26bdd..09f0b06ded39 100644 --- a/libs/hwui/tests/unit/BakedOpDispatcherTests.cpp +++ b/libs/hwui/tests/unit/BakedOpDispatcherTests.cpp @@ -20,8 +20,8 @@ #include <BakedOpRenderer.h> #include <FrameBuilder.h> #include <LayerUpdateQueue.h> -#include <hwui/Paint.h> #include <RecordedOp.h> +#include <hwui/Paint.h> #include <tests/common/TestUtils.h> #include <utils/Color.h> @@ -32,19 +32,20 @@ using namespace android::uirenderer; static BakedOpRenderer::LightInfo sLightInfo; -const FrameBuilder::LightGeometry sLightGeometry = { {100, 100, 100}, 50}; +const FrameBuilder::LightGeometry sLightGeometry = {{100, 100, 100}, 50}; class ValidatingBakedOpRenderer : public BakedOpRenderer { public: - ValidatingBakedOpRenderer(RenderState& renderState, std::function<void(const Glop& glop)> validator) + ValidatingBakedOpRenderer(RenderState& renderState, + std::function<void(const Glop& glop)> validator) : BakedOpRenderer(Caches::getInstance(), renderState, true, false, sLightInfo) , mValidator(validator) { mGlopReceiver = ValidatingGlopReceiver; } + private: static void ValidatingGlopReceiver(BakedOpRenderer& renderer, const Rect* dirtyBounds, - const ClipBase* clip, const Glop& glop) { - + const ClipBase* clip, const Glop& glop) { auto vbor = reinterpret_cast<ValidatingBakedOpRenderer*>(&renderer); vbor->mValidator(glop); } @@ -54,7 +55,8 @@ private: typedef void (*TestBakedOpReceiver)(BakedOpRenderer&, const BakedOpState&); static void testUnmergedGlopDispatch(renderthread::RenderThread& renderThread, RecordedOp* op, - std::function<void(const Glop& glop)> glopVerifier, int expectedGlopCount = 1) { + std::function<void(const Glop& glop)> glopVerifier, + int expectedGlopCount = 1) { // Create op, and wrap with basic state. LinearAllocator allocator; auto snapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(100, 100)); @@ -62,22 +64,22 @@ static void testUnmergedGlopDispatch(renderthread::RenderThread& renderThread, R ASSERT_NE(nullptr, state); int glopCount = 0; - auto glopReceiver = [&glopVerifier, &glopCount, &expectedGlopCount] (const Glop& glop) { + auto glopReceiver = [&glopVerifier, &glopCount, &expectedGlopCount](const Glop& glop) { ASSERT_LE(glopCount++, expectedGlopCount) << expectedGlopCount << "glop(s) expected"; glopVerifier(glop); }; ValidatingBakedOpRenderer renderer(renderThread.renderState(), glopReceiver); - // Dispatch based on op type created, similar to Frame/LayerBuilder dispatch behavior -#define X(Type) \ - [](BakedOpRenderer& renderer, const BakedOpState& state) { \ - BakedOpDispatcher::on##Type(renderer, static_cast<const Type&>(*(state.op)), state); \ - }, +// Dispatch based on op type created, similar to Frame/LayerBuilder dispatch behavior +#define X(Type) \ + [](BakedOpRenderer& renderer, const BakedOpState& state) { \ + BakedOpDispatcher::on##Type(renderer, static_cast<const Type&>(*(state.op)), state); \ + }, static TestBakedOpReceiver unmergedReceivers[] = BUILD_RENDERABLE_OP_LUT(X); #undef X unmergedReceivers[op->opId](renderer, *state); ASSERT_EQ(expectedGlopCount, glopCount) << "Exactly " << expectedGlopCount - << "Glop(s) expected"; + << "Glop(s) expected"; } RENDERTHREAD_OPENGL_PIPELINE_TEST(BakedOpDispatcher, pathTexture_positionOvalArc) { @@ -88,7 +90,7 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(BakedOpDispatcher, pathTexture_positionOvalArc float intervals[] = {1.0f, 1.0f}; strokePaint.setPathEffect(SkDashPathEffect::Make(intervals, 2, 0)); - auto textureGlopVerifier = [] (const Glop& glop) { + auto textureGlopVerifier = [](const Glop& glop) { // validate glop produced by renderPathTexture (so texture, unit quad) auto texture = glop.fill.texture.texture; ASSERT_NE(nullptr, texture); @@ -116,16 +118,15 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(BakedOpDispatcher, pathTexture_positionOvalArc RENDERTHREAD_OPENGL_PIPELINE_TEST(BakedOpDispatcher, onLayerOp_bufferless) { SkPaint layerPaint; layerPaint.setAlpha(128); - OffscreenBuffer* buffer = nullptr; // no providing a buffer, should hit rect fallback case + OffscreenBuffer* buffer = nullptr; // no providing a buffer, should hit rect fallback case LayerOp op(Rect(10, 10), Matrix4::identity(), nullptr, &layerPaint, &buffer); - testUnmergedGlopDispatch(renderThread, &op, [] (const Glop& glop) { - ADD_FAILURE() << "Nothing should happen"; - }, 0); + testUnmergedGlopDispatch(renderThread, &op, + [](const Glop& glop) { ADD_FAILURE() << "Nothing should happen"; }, 0); } static int getGlopTransformFlags(renderthread::RenderThread& renderThread, RecordedOp* op) { int result = 0; - testUnmergedGlopDispatch(renderThread, op, [&result] (const Glop& glop) { + testUnmergedGlopDispatch(renderThread, op, [&result](const Glop& glop) { result = glop.transform.transformFlags; }); return result; @@ -144,7 +145,7 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(BakedOpDispatcher, offsetFlags) { const float points[4] = {0.5, 0.5, 1.0, 1.0}; PointsOp antiAliasedPointsOp(bounds, Matrix4::identity(), nullptr, &aaPaint, points, 4); EXPECT_EQ(TransformFlags::None, getGlopTransformFlags(renderThread, &antiAliasedPointsOp)) - << "Expect no offset for AA points."; + << "Expect no offset for AA points."; PointsOp pointsOp(bounds, Matrix4::identity(), nullptr, &paint, points, 4); EXPECT_EQ(TransformFlags::OffsetByFudgeFactor, getGlopTransformFlags(renderThread, &pointsOp)) << "Expect an offset for non-AA points."; @@ -158,21 +159,21 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(BakedOpDispatcher, offsetFlags) { } RENDERTHREAD_OPENGL_PIPELINE_TEST(BakedOpDispatcher, renderTextWithShadow) { - auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100, - [](RenderProperties& props, RecordingCanvas& canvas) { + auto node = TestUtils::createNode<RecordingCanvas>( + 0, 0, 100, 100, [](RenderProperties& props, RecordingCanvas& canvas) { - android::Paint shadowPaint; - shadowPaint.setColor(SK_ColorRED); + android::Paint shadowPaint; + shadowPaint.setColor(SK_ColorRED); - SkScalar sigma = Blur::convertRadiusToSigma(5); - shadowPaint.setLooper(SkBlurDrawLooper::Make(SK_ColorWHITE, sigma, 3, 3)); + SkScalar sigma = Blur::convertRadiusToSigma(5); + shadowPaint.setLooper(SkBlurDrawLooper::Make(SK_ColorWHITE, sigma, 3, 3)); - TestUtils::drawUtf8ToCanvas(&canvas, "A", shadowPaint, 25, 25); - TestUtils::drawUtf8ToCanvas(&canvas, "B", shadowPaint, 50, 50); - }); + TestUtils::drawUtf8ToCanvas(&canvas, "A", shadowPaint, 25, 25); + TestUtils::drawUtf8ToCanvas(&canvas, "B", shadowPaint, 50, 50); + }); - int glopCount = 0; - auto glopReceiver = [&glopCount] (const Glop& glop) { + int glopCount = 0; + auto glopReceiver = [&glopCount](const Glop& glop) { if (glopCount < 2) { // two white shadows EXPECT_EQ(FloatColor({1, 1, 1, 1}), glop.fill.color); @@ -185,8 +186,8 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(BakedOpDispatcher, renderTextWithShadow) { ValidatingBakedOpRenderer renderer(renderThread.renderState(), glopReceiver); - FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, - sLightGeometry, Caches::getInstance()); + FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, sLightGeometry, + Caches::getInstance()); frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer); @@ -194,15 +195,15 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(BakedOpDispatcher, renderTextWithShadow) { } static void validateLayerDraw(renderthread::RenderThread& renderThread, - std::function<void(const Glop& glop)> validator) { - auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100, - [](RenderProperties& props, RecordingCanvas& canvas) { - props.mutateLayerProperties().setType(LayerType::RenderLayer); - - // provide different blend mode, so decoration draws contrast - props.mutateLayerProperties().setXferMode(SkBlendMode::kSrc); - canvas.drawColor(Color::Black, SkBlendMode::kSrcOver); - }); + std::function<void(const Glop& glop)> validator) { + auto node = TestUtils::createNode<RecordingCanvas>( + 0, 0, 100, 100, [](RenderProperties& props, RecordingCanvas& canvas) { + props.mutateLayerProperties().setType(LayerType::RenderLayer); + + // provide different blend mode, so decoration draws contrast + props.mutateLayerProperties().setXferMode(SkBlendMode::kSrc); + canvas.drawColor(Color::Black, SkBlendMode::kSrcOver); + }); OffscreenBuffer** layerHandle = node->getLayerHandle(); auto syncedNode = TestUtils::getSyncedNode(node); @@ -211,12 +212,12 @@ static void validateLayerDraw(renderthread::RenderThread& renderThread, OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 100, 100); *layerHandle = &layer; { - LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid + LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid layerUpdateQueue.enqueueLayerWithDamage(node.get(), Rect(0, 0, 100, 100)); ValidatingBakedOpRenderer renderer(renderThread.renderState(), validator); - FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, - sLightGeometry, Caches::getInstance()); + FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, sLightGeometry, + Caches::getInstance()); frameBuilder.deferLayers(layerUpdateQueue); frameBuilder.deferRenderNode(*syncedNode); frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer); @@ -233,8 +234,8 @@ static FloatColor makeFloatColor(uint32_t color) { } RENDERTHREAD_OPENGL_PIPELINE_TEST(BakedOpDispatcher, layerUpdateProperties) { - for (bool debugOverdraw : { false, true }) { - for (bool debugLayersUpdates : { false, true }) { + for (bool debugOverdraw : {false, true}) { + for (bool debugLayersUpdates : {false, true}) { ScopedProperty<bool> ovdProp(Properties::debugOverdraw, debugOverdraw); ScopedProperty<bool> lupProp(Properties::debugLayersUpdates, debugLayersUpdates); @@ -253,8 +254,8 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(BakedOpDispatcher, layerUpdateProperties) { // blend srcover, different from that of layer EXPECT_EQ(GLenum(GL_ONE), glop.blend.src); EXPECT_EQ(GLenum(GL_ONE_MINUS_SRC_ALPHA), glop.blend.dst); - EXPECT_EQ(makeFloatColor(debugLayersUpdates ? 0x7f00ff00 : 0), - glop.fill.color) << "Should be transparent green if debugLayersUpdates"; + EXPECT_EQ(makeFloatColor(debugLayersUpdates ? 0x7f00ff00 : 0), glop.fill.color) + << "Should be transparent green if debugLayersUpdates"; } else if (glopCount < 7) { // 3 - 6 - overdraw indicator overlays, if present EXPECT_TRUE(glop.fill.colorEnabled); @@ -279,7 +280,7 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(BakedOpDispatcher, pathTextureSnapping) { SkPath path; path.addRect(SkRect::MakeXYWH(1.5, 3.8, 100, 90)); PathOp op(bounds, Matrix4::identity(), nullptr, &paint, &path); - testUnmergedGlopDispatch(renderThread, &op, [] (const Glop& glop) { + testUnmergedGlopDispatch(renderThread, &op, [](const Glop& glop) { auto texture = glop.fill.texture.texture; ASSERT_NE(nullptr, texture); EXPECT_EQ(1, reinterpret_cast<PathTexture*>(texture)->left); diff --git a/libs/hwui/tests/unit/BakedOpRendererTests.cpp b/libs/hwui/tests/unit/BakedOpRendererTests.cpp index 38e106a8ab77..1a3ec39a00d0 100644 --- a/libs/hwui/tests/unit/BakedOpRendererTests.cpp +++ b/libs/hwui/tests/unit/BakedOpRendererTests.cpp @@ -22,11 +22,11 @@ using namespace android::uirenderer; -const BakedOpRenderer::LightInfo sLightInfo = { 128, 128 }; +const BakedOpRenderer::LightInfo sLightInfo = {128, 128}; RENDERTHREAD_OPENGL_PIPELINE_TEST(BakedOpRenderer, startRepaintLayer_clear) { - BakedOpRenderer renderer(Caches::getInstance(), renderThread.renderState(), - true, false, sLightInfo); + BakedOpRenderer renderer(Caches::getInstance(), renderThread.renderState(), true, false, + sLightInfo); OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 200u, 200u); layer.dirty(Rect(200, 200)); @@ -38,9 +38,9 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(BakedOpRenderer, startRepaintLayer_clear) { layer.dirty(Rect(200, 200)); { - renderer.startRepaintLayer(&layer, Rect(100, 200)); // repainting left side + renderer.startRepaintLayer(&layer, Rect(100, 200)); // repainting left side EXPECT_TRUE(layer.region.isRect()); - //ALOGD("bounds %d %d %d %d", RECT_ARGS(layer.region.getBounds())); + // ALOGD("bounds %d %d %d %d", RECT_ARGS(layer.region.getBounds())); EXPECT_EQ(android::Rect(100, 0, 200, 200), layer.region.getBounds()) << "Left side being repainted, so right side should be clear"; renderer.endLayer(); @@ -48,7 +48,7 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(BakedOpRenderer, startRepaintLayer_clear) { // right side is now only dirty portion { - renderer.startRepaintLayer(&layer, Rect(100, 0, 200, 200)); // repainting right side + renderer.startRepaintLayer(&layer, Rect(100, 0, 200, 200)); // repainting right side EXPECT_TRUE(layer.region.isEmpty()) << "Now right side being repainted, so region should be entirely clear"; renderer.endLayer(); diff --git a/libs/hwui/tests/unit/BakedOpStateTests.cpp b/libs/hwui/tests/unit/BakedOpStateTests.cpp index d51db2ebb169..6f8e24917767 100644 --- a/libs/hwui/tests/unit/BakedOpStateTests.cpp +++ b/libs/hwui/tests/unit/BakedOpStateTests.cpp @@ -38,7 +38,7 @@ TEST(ResolvedRenderState, construct) { ResolvedRenderState state(allocator, *parentSnapshot, recordedOp, false, false); EXPECT_MATRIX_APPROX_EQ(state.transform, translate10x20); EXPECT_EQ(Rect(100, 200), state.clipRect()); - EXPECT_EQ(Rect(40, 60, 100, 200), state.clippedBounds); // translated and also clipped + EXPECT_EQ(Rect(40, 60, 100, 200), state.clippedBounds); // translated and also clipped EXPECT_EQ(OpClipSideFlags::Right | OpClipSideFlags::Bottom, state.clipSideFlags); } { @@ -72,14 +72,14 @@ TEST(ResolvedRenderState, computeLocalSpaceClip) { auto parentSnapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(100, 200)); ResolvedRenderState state(allocator, *parentSnapshot, recordedOp, false, false); EXPECT_EQ(Rect(-10, -20, 90, 180), state.computeLocalSpaceClip()) - << "Local clip rect should be 100x200, offset by -10,-20"; + << "Local clip rect should be 100x200, offset by -10,-20"; } { // recorded with transform + parent transform auto parentSnapshot = TestUtils::makeSnapshot(translate10x20, Rect(100, 200)); ResolvedRenderState state(allocator, *parentSnapshot, recordedOp, false, false); EXPECT_EQ(Rect(-10, -20, 80, 160), state.computeLocalSpaceClip()) - << "Local clip rect should be 90x190, offset by -10,-20"; + << "Local clip rect should be 90x190, offset by -10,-20"; } } @@ -96,62 +96,51 @@ struct StrokeTestCase { }; const static StrokeTestCase sStrokeTestCases[] = { - { - 1, HAIRLINE, [](const ResolvedRenderState& state) { - EXPECT_EQ(Rect(49.5f, 49.5f, 150.5f, 150.5f), state.clippedBounds); - } - }, - { - 1, SEMI_HAIRLINE, [](const ResolvedRenderState& state) { - EXPECT_TRUE(state.clippedBounds.contains(49.5f, 49.5f, 150.5f, 150.5f)); - EXPECT_TRUE(Rect(49, 49, 151, 151).contains(state.clippedBounds)); - } - }, - { - 1, 20, [](const ResolvedRenderState& state) { - EXPECT_EQ(Rect(40, 40, 160, 160), state.clippedBounds); - } - }, - - // 3x3 scale: - { - 3, HAIRLINE, [](const ResolvedRenderState& state) { - EXPECT_EQ(Rect(149.5f, 149.5f, 200, 200), state.clippedBounds); - EXPECT_EQ(OpClipSideFlags::Right | OpClipSideFlags::Bottom, state.clipSideFlags); - } - }, - { - 3, SEMI_HAIRLINE, [](const ResolvedRenderState& state) { - EXPECT_TRUE(state.clippedBounds.contains(149.5f, 149.5f, 200, 200)); - EXPECT_TRUE(Rect(149, 149, 200, 200).contains(state.clippedBounds)); - } - }, - { - 3, 20, [](const ResolvedRenderState& state) { - EXPECT_TRUE(state.clippedBounds.contains(120, 120, 200, 200)); - EXPECT_TRUE(Rect(119, 119, 200, 200).contains(state.clippedBounds)); - } - }, - - // 0.5f x 0.5f scale - { - 0.5f, HAIRLINE, [](const ResolvedRenderState& state) { - EXPECT_EQ(Rect(24.5f, 24.5f, 75.5f, 75.5f), state.clippedBounds); - } - }, - { - 0.5f, SEMI_HAIRLINE, [](const ResolvedRenderState& state) { - EXPECT_TRUE(state.clippedBounds.contains(24.5f, 24.5f, 75.5f, 75.5f)); - EXPECT_TRUE(Rect(24, 24, 76, 76).contains(state.clippedBounds)); - } - }, - { - 0.5f, 20, [](const ResolvedRenderState& state) { - EXPECT_TRUE(state.clippedBounds.contains(19.5f, 19.5f, 80.5f, 80.5f)); - EXPECT_TRUE(Rect(19, 19, 81, 81).contains(state.clippedBounds)); - } - } -}; + {1, HAIRLINE, + [](const ResolvedRenderState& state) { + EXPECT_EQ(Rect(49.5f, 49.5f, 150.5f, 150.5f), state.clippedBounds); + }}, + {1, SEMI_HAIRLINE, + [](const ResolvedRenderState& state) { + EXPECT_TRUE(state.clippedBounds.contains(49.5f, 49.5f, 150.5f, 150.5f)); + EXPECT_TRUE(Rect(49, 49, 151, 151).contains(state.clippedBounds)); + }}, + {1, 20, + [](const ResolvedRenderState& state) { + EXPECT_EQ(Rect(40, 40, 160, 160), state.clippedBounds); + }}, + + // 3x3 scale: + {3, HAIRLINE, + [](const ResolvedRenderState& state) { + EXPECT_EQ(Rect(149.5f, 149.5f, 200, 200), state.clippedBounds); + EXPECT_EQ(OpClipSideFlags::Right | OpClipSideFlags::Bottom, state.clipSideFlags); + }}, + {3, SEMI_HAIRLINE, + [](const ResolvedRenderState& state) { + EXPECT_TRUE(state.clippedBounds.contains(149.5f, 149.5f, 200, 200)); + EXPECT_TRUE(Rect(149, 149, 200, 200).contains(state.clippedBounds)); + }}, + {3, 20, + [](const ResolvedRenderState& state) { + EXPECT_TRUE(state.clippedBounds.contains(120, 120, 200, 200)); + EXPECT_TRUE(Rect(119, 119, 200, 200).contains(state.clippedBounds)); + }}, + + // 0.5f x 0.5f scale + {0.5f, HAIRLINE, + [](const ResolvedRenderState& state) { + EXPECT_EQ(Rect(24.5f, 24.5f, 75.5f, 75.5f), state.clippedBounds); + }}, + {0.5f, SEMI_HAIRLINE, + [](const ResolvedRenderState& state) { + EXPECT_TRUE(state.clippedBounds.contains(24.5f, 24.5f, 75.5f, 75.5f)); + EXPECT_TRUE(Rect(24, 24, 76, 76).contains(state.clippedBounds)); + }}, + {0.5f, 20, [](const ResolvedRenderState& state) { + EXPECT_TRUE(state.clippedBounds.contains(19.5f, 19.5f, 80.5f, 80.5f)); + EXPECT_TRUE(Rect(19, 19, 81, 81).contains(state.clippedBounds)); + }}}; TEST(ResolvedRenderState, construct_expandForStroke) { LinearAllocator allocator; @@ -163,8 +152,7 @@ TEST(ResolvedRenderState, construct_expandForStroke) { strokedPaint.setStrokeWidth(testCase.strokeWidth); ClipRect clip(Rect(200, 200)); - RectOp recordedOp(Rect(50, 50, 150, 150), - Matrix4::identity(), &clip, &strokedPaint); + RectOp recordedOp(Rect(50, 50, 150, 150), Matrix4::identity(), &clip, &strokedPaint); Matrix4 snapshotMatrix; snapshotMatrix.loadScale(testCase.scale, testCase.scale, 1); @@ -204,16 +192,18 @@ TEST(BakedOpState, tryShadowOpConstruct) { LinearAllocator allocator; { - auto snapshot = TestUtils::makeSnapshot(translate10x20, Rect()); // Note: empty clip - BakedOpState* bakedState = BakedOpState::tryShadowOpConstruct(allocator, *snapshot, (ShadowOp*)0x1234); + auto snapshot = TestUtils::makeSnapshot(translate10x20, Rect()); // Note: empty clip + BakedOpState* bakedState = + BakedOpState::tryShadowOpConstruct(allocator, *snapshot, (ShadowOp*)0x1234); EXPECT_EQ(nullptr, bakedState) << "op should be rejected by clip, so not constructed"; EXPECT_EQ(0u, allocator.usedSize()) << "no serialization, even for clip," - "since op is quick rejected based on snapshot clip"; + "since op is quick rejected based on snapshot clip"; } { auto snapshot = TestUtils::makeSnapshot(translate10x20, Rect(100, 200)); - BakedOpState* bakedState = BakedOpState::tryShadowOpConstruct(allocator, *snapshot, (ShadowOp*)0x1234); + BakedOpState* bakedState = + BakedOpState::tryShadowOpConstruct(allocator, *snapshot, (ShadowOp*)0x1234); ASSERT_NE(nullptr, bakedState) << "NOT rejected by clip, so op should be constructed"; EXPECT_LE(64u, allocator.usedSize()) << "relatively large alloc for non-rejected op"; @@ -232,12 +222,13 @@ TEST(BakedOpState, tryStrokeableOpConstruct) { paint.setStrokeWidth(0.0f); ClipRect clip(Rect(100, 200)); RectOp rejectOp(Rect(100, 200), Matrix4::identity(), &clip, &paint); - auto snapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect()); // Note: empty clip - auto bakedState = BakedOpState::tryStrokeableOpConstruct(allocator, *snapshot, rejectOp, - BakedOpState::StrokeBehavior::StyleDefined, false); + auto snapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect()); // Note: empty clip + auto bakedState = BakedOpState::tryStrokeableOpConstruct( + allocator, *snapshot, rejectOp, BakedOpState::StrokeBehavior::StyleDefined, false); EXPECT_EQ(nullptr, bakedState); - EXPECT_GT(8u, allocator.usedSize()); // no significant allocation space used for rejected op + EXPECT_GT(8u, + allocator.usedSize()); // no significant allocation space used for rejected op } { // check simple unscaled expansion @@ -247,8 +238,8 @@ TEST(BakedOpState, tryStrokeableOpConstruct) { ClipRect clip(Rect(200, 200)); RectOp rejectOp(Rect(50, 50, 150, 150), Matrix4::identity(), &clip, &paint); auto snapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(200, 200)); - auto bakedState = BakedOpState::tryStrokeableOpConstruct(allocator, *snapshot, rejectOp, - BakedOpState::StrokeBehavior::StyleDefined, false); + auto bakedState = BakedOpState::tryStrokeableOpConstruct( + allocator, *snapshot, rejectOp, BakedOpState::StrokeBehavior::StyleDefined, false); ASSERT_NE(nullptr, bakedState); EXPECT_EQ(Rect(45, 45, 155, 155), bakedState->computedState.clippedBounds); @@ -262,8 +253,8 @@ TEST(BakedOpState, tryStrokeableOpConstruct) { ClipRect clip(Rect(200, 200)); RectOp rejectOp(Rect(50, 50, 150, 150), Matrix4::identity(), &clip, &paint); auto snapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(200, 200)); - auto bakedState = BakedOpState::tryStrokeableOpConstruct(allocator, *snapshot, rejectOp, - BakedOpState::StrokeBehavior::Forced, false); + auto bakedState = BakedOpState::tryStrokeableOpConstruct( + allocator, *snapshot, rejectOp, BakedOpState::StrokeBehavior::Forced, false); ASSERT_NE(nullptr, bakedState); EXPECT_EQ(Rect(45, 45, 155, 155), bakedState->computedState.clippedBounds); @@ -271,5 +262,5 @@ TEST(BakedOpState, tryStrokeableOpConstruct) { } } -} // namespace uirenderer -} // namespace android +} // namespace uirenderer +} // namespace android diff --git a/libs/hwui/tests/unit/CacheManagerTests.cpp b/libs/hwui/tests/unit/CacheManagerTests.cpp index 6115162c8f81..b1106f0d6a71 100644 --- a/libs/hwui/tests/unit/CacheManagerTests.cpp +++ b/libs/hwui/tests/unit/CacheManagerTests.cpp @@ -35,7 +35,8 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(CacheManager, trimMemory) { GrContext* grContext = renderThread.getGrContext(); ASSERT_TRUE(grContext != nullptr); - // create pairs of offscreen render targets and images until we exceed the backgroundCacheSizeLimit + // create pairs of offscreen render targets and images until we exceed the + // backgroundCacheSizeLimit std::vector<sk_sp<SkSurface>> surfaces; while (getCacheUsage(grContext) <= renderThread.cacheManager().getBackgroundCacheSize()) { diff --git a/libs/hwui/tests/unit/CanvasContextTests.cpp b/libs/hwui/tests/unit/CanvasContextTests.cpp index ef5ce0d9968e..28cff5b9b154 100644 --- a/libs/hwui/tests/unit/CanvasContextTests.cpp +++ b/libs/hwui/tests/unit/CanvasContextTests.cpp @@ -35,8 +35,8 @@ public: RENDERTHREAD_TEST(CanvasContext, create) { auto rootNode = TestUtils::createNode(0, 0, 200, 400, nullptr); ContextFactory contextFactory; - std::unique_ptr<CanvasContext> canvasContext(CanvasContext::create( - renderThread, false, rootNode.get(), &contextFactory)); + std::unique_ptr<CanvasContext> canvasContext( + CanvasContext::create(renderThread, false, rootNode.get(), &contextFactory)); ASSERT_FALSE(canvasContext->hasSurface()); diff --git a/libs/hwui/tests/unit/CanvasStateTests.cpp b/libs/hwui/tests/unit/CanvasStateTests.cpp index c41313aebb5d..4c03811b0c96 100644 --- a/libs/hwui/tests/unit/CanvasStateTests.cpp +++ b/libs/hwui/tests/unit/CanvasStateTests.cpp @@ -21,14 +21,14 @@ #include "hwui/Canvas.h" #include "utils/LinearAllocator.h" -#include <gtest/gtest.h> -#include <SkPath.h> #include <SkClipOp.h> +#include <SkPath.h> +#include <gtest/gtest.h> namespace android { namespace uirenderer { -class NullClient: public CanvasStateClient { +class NullClient : public CanvasStateClient { void onViewportInitialized() override {} void onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) {} GLuint getTargetFbo() const override { return 0; } @@ -47,8 +47,7 @@ static bool approxEqual(const Matrix4& a, const Matrix4& b) { TEST(CanvasState, gettersAndSetters) { CanvasState state(sNullClient); - state.initializeSaveStack(200, 200, - 0, 0, 200, 200, Vector3()); + state.initializeSaveStack(200, 200, 0, 0, 200, 200, Vector3()); ASSERT_EQ(state.getWidth(), 200); ASSERT_EQ(state.getHeight(), 200); @@ -65,8 +64,7 @@ TEST(CanvasState, gettersAndSetters) { TEST(CanvasState, simpleClipping) { CanvasState state(sNullClient); - state.initializeSaveStack(200, 200, - 0, 0, 200, 200, Vector3()); + state.initializeSaveStack(200, 200, 0, 0, 200, 200, Vector3()); state.clipRect(0, 0, 100, 100, SkClipOp::kIntersect); ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(100, 100)); @@ -80,8 +78,7 @@ TEST(CanvasState, simpleClipping) { TEST(CanvasState, complexClipping) { CanvasState state(sNullClient); - state.initializeSaveStack(200, 200, - 0, 0, 200, 200, Vector3()); + state.initializeSaveStack(200, 200, 0, 0, 200, 200, Vector3()); state.save(SaveFlags::MatrixClip); { @@ -116,8 +113,7 @@ TEST(CanvasState, complexClipping) { TEST(CanvasState, saveAndRestore) { CanvasState state(sNullClient); - state.initializeSaveStack(200, 200, - 0, 0, 200, 200, Vector3()); + state.initializeSaveStack(200, 200, 0, 0, 200, 200, Vector3()); state.save(SaveFlags::Clip); { @@ -125,7 +121,7 @@ TEST(CanvasState, saveAndRestore) { ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(10, 10)); } state.restore(); - ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(200, 200)); // verify restore + ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(200, 200)); // verify restore Matrix4 simpleTranslate; simpleTranslate.loadTranslate(10, 10, 0); @@ -140,27 +136,25 @@ TEST(CanvasState, saveAndRestore) { TEST(CanvasState, saveAndRestoreButNotTooMuch) { CanvasState state(sNullClient); - state.initializeSaveStack(200, 200, - 0, 0, 200, 200, Vector3()); + state.initializeSaveStack(200, 200, 0, 0, 200, 200, Vector3()); - state.save(SaveFlags::Matrix); // NOTE: clip not saved + state.save(SaveFlags::Matrix); // NOTE: clip not saved { state.clipRect(0, 0, 10, 10, SkClipOp::kIntersect); ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(10, 10)); } state.restore(); - ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(10, 10)); // verify not restored + ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(10, 10)); // verify not restored Matrix4 simpleTranslate; simpleTranslate.loadTranslate(10, 10, 0); - state.save(SaveFlags::Clip); // NOTE: matrix not saved + state.save(SaveFlags::Clip); // NOTE: matrix not saved { state.translate(10, 10, 0); EXPECT_TRUE(approxEqual(*state.currentTransform(), simpleTranslate)); } state.restore(); - EXPECT_TRUE(approxEqual(*state.currentTransform(), simpleTranslate)); // verify not restored + EXPECT_TRUE(approxEqual(*state.currentTransform(), simpleTranslate)); // verify not restored } - } } diff --git a/libs/hwui/tests/unit/ClipAreaTests.cpp b/libs/hwui/tests/unit/ClipAreaTests.cpp index d4d7919f9eee..450bb679a45f 100644 --- a/libs/hwui/tests/unit/ClipAreaTests.cpp +++ b/libs/hwui/tests/unit/ClipAreaTests.cpp @@ -14,9 +14,9 @@ * limitations under the License. */ -#include <gtest/gtest.h> #include <SkPath.h> #include <SkRegion.h> +#include <gtest/gtest.h> #include "ClipArea.h" @@ -194,7 +194,8 @@ TEST(ClipArea, serializeIntersectedClip) { { auto origRectClip = area.serializeClip(allocator); ASSERT_NE(nullptr, origRectClip); - EXPECT_EQ(origRectClip, area.serializeIntersectedClip(allocator, nullptr, Matrix4::identity())); + EXPECT_EQ(origRectClip, + area.serializeIntersectedClip(allocator, nullptr, Matrix4::identity())); } // rect @@ -208,11 +209,13 @@ TEST(ClipArea, serializeIntersectedClip) { ASSERT_EQ(ClipMode::Rectangle, resolvedClip->mode); EXPECT_EQ(Rect(100, 100, 200, 200), resolvedClip->rect); - EXPECT_EQ(resolvedClip, area.serializeIntersectedClip(allocator, &recordedClip, translateScale)) + EXPECT_EQ(resolvedClip, + area.serializeIntersectedClip(allocator, &recordedClip, translateScale)) << "Must return previous serialization, since input is same"; ClipRect recordedClip2(Rect(100, 100)); - EXPECT_NE(resolvedClip, area.serializeIntersectedClip(allocator, &recordedClip2, translateScale)) + EXPECT_NE(resolvedClip, + area.serializeIntersectedClip(allocator, &recordedClip2, translateScale)) << "Shouldn't return previous serialization, since matrix location is different"; } @@ -222,7 +225,8 @@ TEST(ClipArea, serializeIntersectedClip) { area.clipRectWithTransform(Rect(200, 200), &rotate, SkRegion::kIntersect_Op); { ClipRect recordedClip(Rect(100, 100)); - auto resolvedClip = area.serializeIntersectedClip(allocator, &recordedClip, Matrix4::identity()); + auto resolvedClip = + area.serializeIntersectedClip(allocator, &recordedClip, Matrix4::identity()); ASSERT_NE(nullptr, resolvedClip); ASSERT_EQ(ClipMode::RectangleList, resolvedClip->mode); auto clipRectList = reinterpret_cast<const ClipRectList*>(resolvedClip); @@ -243,8 +247,9 @@ TEST(ClipArea, serializeIntersectedClip) { Matrix4 translate10x20; translate10x20.loadTranslate(10, 20, 0); - auto resolvedClip = area.serializeIntersectedClip(allocator, &recordedClip, - translate10x20); // Note: only translate for now, others not handled correctly + auto resolvedClip = area.serializeIntersectedClip( + allocator, &recordedClip, + translate10x20); // Note: only translate for now, others not handled correctly ASSERT_NE(nullptr, resolvedClip); ASSERT_EQ(ClipMode::Region, resolvedClip->mode); auto clipRegion = reinterpret_cast<const ClipRegion*>(resolvedClip); @@ -267,7 +272,8 @@ TEST(ClipArea, serializeIntersectedClip_snap) { ClipRect recordedClip(Rect(100.12, 100.74)); Matrix4 translateScale; translateScale.loadTranslate(100, 100, 0); - translateScale.scale(2, 3, 1); // recorded clip will have non-int coords, even after transform + translateScale.scale(2, 3, + 1); // recorded clip will have non-int coords, even after transform auto resolvedClip = area.serializeIntersectedClip(allocator, &recordedClip, translateScale); ASSERT_NE(nullptr, resolvedClip); EXPECT_EQ(ClipMode::Rectangle, resolvedClip->mode); @@ -343,5 +349,5 @@ TEST(ClipArea, applyTransformToRegion_rotate90) { EXPECT_EQ(SkIRect::MakeLTRB(-4, 1, -2, 3), region.getBounds()); } -} // namespace uirenderer -} // namespace android +} // namespace uirenderer +} // namespace android diff --git a/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp b/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp index 87d897ee6a7b..b8b5050faa11 100644 --- a/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp +++ b/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp @@ -50,7 +50,6 @@ RENDERTHREAD_TEST(DeferredLayerUpdater, updateLayer) { glLayer->setRenderTarget(GL_TEXTURE_EXTERNAL_OES); } - // the backing layer should now have all the properties applied. if (layerUpdater->backingLayer()->getApi() == Layer::Api::OpenGL) { GlLayer* glLayer = static_cast<GlLayer*>(layerUpdater->backingLayer()); diff --git a/libs/hwui/tests/unit/FatVectorTests.cpp b/libs/hwui/tests/unit/FatVectorTests.cpp index 64b0ba13562d..8523e6c9e973 100644 --- a/libs/hwui/tests/unit/FatVectorTests.cpp +++ b/libs/hwui/tests/unit/FatVectorTests.cpp @@ -22,12 +22,11 @@ using namespace android; using namespace android::uirenderer; -template<class VectorType> +template <class VectorType> static bool allocationIsInternal(VectorType& v) { // allocation array (from &v[0] to &v[0] + v.capacity) is // located within the vector object itself - return (char*)(&v) <= (char*)(&v[0]) - && (char*)(&v + 1) >= (char*)(&v[0] + v.capacity()); + return (char*)(&v) <= (char*)(&v[0]) && (char*)(&v + 1) >= (char*)(&v[0] + v.capacity()); } TEST(FatVector, baseline) { diff --git a/libs/hwui/tests/unit/FatalTestCanvas.h b/libs/hwui/tests/unit/FatalTestCanvas.h index 03d94964ac76..9693ce7b6784 100644 --- a/libs/hwui/tests/unit/FatalTestCanvas.h +++ b/libs/hwui/tests/unit/FatalTestCanvas.h @@ -16,15 +16,14 @@ #pragma once -#include <gtest/gtest.h> #include <SkCanvas.h> +#include <gtest/gtest.h> namespace { class TestCanvasBase : public SkCanvas { public: - TestCanvasBase(int width, int height) : SkCanvas(width, height) { - } + TestCanvasBase(int width, int height) : SkCanvas(width, height) {} void onDrawAnnotation(const SkRect&, const char key[], SkData* value) { ADD_FAILURE() << "onDrawAnnotation not expected in this test"; } @@ -32,35 +31,33 @@ public: ADD_FAILURE() << "onDrawDRRect not expected in this test"; } void onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y, - const SkPaint& paint) { + const SkPaint& paint) { ADD_FAILURE() << "onDrawText not expected in this test"; } void onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[], - const SkPaint& paint) { + const SkPaint& paint) { ADD_FAILURE() << "onDrawPosText not expected in this test"; } void onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[], SkScalar constY, - const SkPaint& paint) { + const SkPaint& paint) { ADD_FAILURE() << "onDrawPosTextH not expected in this test"; } void onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path, - const SkMatrix* matrix, const SkPaint& paint) { + const SkMatrix* matrix, const SkPaint& paint) { ADD_FAILURE() << "onDrawTextOnPath not expected in this test"; } void onDrawTextRSXform(const void* text, size_t byteLength, const SkRSXform[], - const SkRect* cullRect, const SkPaint& paint) { + const SkRect* cullRect, const SkPaint& paint) { ADD_FAILURE() << "onDrawTextRSXform not expected in this test"; } void onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y, const SkPaint& paint) { ADD_FAILURE() << "onDrawTextBlob not expected in this test"; } void onDrawPatch(const SkPoint cubics[12], const SkColor colors[4], const SkPoint texCoords[4], - SkBlendMode, const SkPaint& paint) { + SkBlendMode, const SkPaint& paint) { ADD_FAILURE() << "onDrawPatch not expected in this test"; } - void onDrawPaint(const SkPaint&) { - ADD_FAILURE() << "onDrawPaint not expected in this test"; - } + void onDrawPaint(const SkPaint&) { ADD_FAILURE() << "onDrawPaint not expected in this test"; } void onDrawRect(const SkRect&, const SkPaint&) { ADD_FAILURE() << "onDrawRect not expected in this test"; } @@ -71,7 +68,7 @@ public: ADD_FAILURE() << "onDrawOval not expected in this test"; } void onDrawArc(const SkRect&, SkScalar startAngle, SkScalar sweepAngle, bool useCenter, - const SkPaint&) { + const SkPaint&) { ADD_FAILURE() << "onDrawArc not expected in this test"; } void onDrawRRect(const SkRRect&, const SkPaint&) { @@ -84,7 +81,7 @@ public: ADD_FAILURE() << "onDrawVertices not expected in this test"; } void onDrawAtlas(const SkImage*, const SkRSXform[], const SkRect[], const SkColor[], int count, - SkBlendMode, const SkRect* cull, const SkPaint*) { + SkBlendMode, const SkRect* cull, const SkPaint*) { ADD_FAILURE() << "onDrawAtlas not expected in this test"; } void onDrawPath(const SkPath&, const SkPaint&) { @@ -94,29 +91,29 @@ public: ADD_FAILURE() << "onDrawImage not expected in this test"; } void onDrawImageRect(const SkImage*, const SkRect*, const SkRect&, const SkPaint*, - SrcRectConstraint) { + SrcRectConstraint) { ADD_FAILURE() << "onDrawImageRect not expected in this test"; } void onDrawImageNine(const SkImage*, const SkIRect& center, const SkRect& dst, const SkPaint*) { ADD_FAILURE() << "onDrawImageNine not expected in this test"; } void onDrawImageLattice(const SkImage*, const Lattice& lattice, const SkRect& dst, - const SkPaint*) { + const SkPaint*) { ADD_FAILURE() << "onDrawImageLattice not expected in this test"; } void onDrawBitmap(const SkBitmap&, SkScalar dx, SkScalar dy, const SkPaint*) { ADD_FAILURE() << "onDrawBitmap not expected in this test"; } void onDrawBitmapRect(const SkBitmap&, const SkRect*, const SkRect&, const SkPaint*, - SrcRectConstraint) { + SrcRectConstraint) { ADD_FAILURE() << "onDrawBitmapRect not expected in this test"; } void onDrawBitmapNine(const SkBitmap&, const SkIRect& center, const SkRect& dst, - const SkPaint*) { + const SkPaint*) { ADD_FAILURE() << "onDrawBitmapNine not expected in this test"; } void onDrawBitmapLattice(const SkBitmap&, const Lattice& lattice, const SkRect& dst, - const SkPaint*) { + const SkPaint*) { ADD_FAILURE() << "onDrawBitmapLattice not expected in this test"; } void onClipRRect(const SkRRect& rrect, SkClipOp, ClipEdgeStyle) { @@ -128,14 +125,11 @@ public: void onClipRegion(const SkRegion& deviceRgn, SkClipOp) { ADD_FAILURE() << "onClipRegion not expected in this test"; } - void onDiscard() { - ADD_FAILURE() << "onDiscard not expected in this test"; - } + void onDiscard() { ADD_FAILURE() << "onDiscard not expected in this test"; } void onDrawPicture(const SkPicture*, const SkMatrix*, const SkPaint*) { ADD_FAILURE() << "onDrawPicture not expected in this test"; } - int mDrawCounter = 0; //counts how may draw calls of any kind were made to this canvas + int mDrawCounter = 0; // counts how may draw calls of any kind were made to this canvas }; - }
\ No newline at end of file diff --git a/libs/hwui/tests/unit/FontRendererTests.cpp b/libs/hwui/tests/unit/FontRendererTests.cpp index ee202367d73e..c78f131ce2ce 100644 --- a/libs/hwui/tests/unit/FontRendererTests.cpp +++ b/libs/hwui/tests/unit/FontRendererTests.cpp @@ -40,16 +40,16 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(FontRenderer, renderDropShadow) { std::vector<float> positions; float totalAdvance; Rect bounds; - TestUtils::layoutTextUnscaled(paint, "This is a test", - &glyphs, &positions, &totalAdvance, &bounds); + TestUtils::layoutTextUnscaled(paint, "This is a test", &glyphs, &positions, &totalAdvance, + &bounds); for (int radius : {28, 20, 2}) { - auto result = fontRenderer.renderDropShadow(&paint, glyphs.data(), glyphs.size(), - radius, positions.data()); + auto result = fontRenderer.renderDropShadow(&paint, glyphs.data(), glyphs.size(), radius, + positions.data()); ASSERT_NE(nullptr, result.image); EXPECT_FALSE(isZero(result.image, result.width * result.height)); - EXPECT_LE(bounds.getWidth() + radius * 2, (int) result.width); - EXPECT_LE(bounds.getHeight() + radius * 2, (int) result.height); + EXPECT_LE(bounds.getWidth() + radius * 2, (int)result.width); + EXPECT_LE(bounds.getHeight() + radius * 2, (int)result.height); delete result.image; } } diff --git a/libs/hwui/tests/unit/FrameBuilderTests.cpp b/libs/hwui/tests/unit/FrameBuilderTests.cpp index fcdd814bb63e..4eb77514f4ae 100644 --- a/libs/hwui/tests/unit/FrameBuilderTests.cpp +++ b/libs/hwui/tests/unit/FrameBuilderTests.cpp @@ -30,7 +30,7 @@ namespace android { namespace uirenderer { -const FrameBuilder::LightGeometry sLightGeometry = { {100, 100, 100}, 50}; +const FrameBuilder::LightGeometry sLightGeometry = {{100, 100, 100}, 50}; /** * Virtual class implemented by each test to redirect static operation / state transitions to @@ -56,23 +56,21 @@ public: virtual void startRepaintLayer(OffscreenBuffer*, const Rect& repaintRect) { ADD_FAILURE() << "Layer repaint not expected in this test"; } - virtual void endLayer() { - ADD_FAILURE() << "Layer updates not expected in this test"; - } + virtual void endLayer() { ADD_FAILURE() << "Layer updates not expected in this test"; } virtual void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) {} virtual void endFrame(const Rect& repaintRect) {} - // define virtual defaults for single draw methods -#define X(Type) \ +// define virtual defaults for single draw methods +#define X(Type) \ virtual void on##Type(const Type&, const BakedOpState&) { \ - ADD_FAILURE() << #Type " not expected in this test"; \ + ADD_FAILURE() << #Type " not expected in this test"; \ } MAP_RENDERABLE_OPS(X) #undef X - // define virtual defaults for merged draw methods -#define X(Type) \ - virtual void onMerged##Type##s(const MergedBakedOpList& opList) { \ +// define virtual defaults for merged draw methods +#define X(Type) \ + virtual void onMerged##Type##s(const MergedBakedOpList& opList) { \ ADD_FAILURE() << "Merged " #Type "s not expected in this test"; \ } MAP_MERGEABLE_OPS(X) @@ -90,18 +88,18 @@ protected: */ class TestDispatcher { public: - // define single op methods, which redirect to TestRendererBase -#define X(Type) \ +// define single op methods, which redirect to TestRendererBase +#define X(Type) \ static void on##Type(TestRendererBase& renderer, const Type& op, const BakedOpState& state) { \ - renderer.on##Type(op, state); \ + renderer.on##Type(op, state); \ } MAP_RENDERABLE_OPS(X); #undef X - // define merged op methods, which redirect to TestRendererBase -#define X(Type) \ +// define merged op methods, which redirect to TestRendererBase +#define X(Type) \ static void onMerged##Type##s(TestRendererBase& renderer, const MergedBakedOpList& opList) { \ - renderer.onMerged##Type##s(opList); \ + renderer.onMerged##Type##s(opList); \ } MAP_MERGEABLE_OPS(X); #undef X @@ -123,24 +121,22 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, simple) { void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override { EXPECT_EQ(2, mIndex++); } - void endFrame(const Rect& repaintRect) override { - EXPECT_EQ(3, mIndex++); - } + void endFrame(const Rect& repaintRect) override { EXPECT_EQ(3, mIndex++); } }; - auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 200, - [](RenderProperties& props, RecordingCanvas& canvas) { - sk_sp<Bitmap> bitmap(TestUtils::createBitmap(25, 25)); - canvas.drawRect(0, 0, 100, 200, SkPaint()); - canvas.drawBitmap(*bitmap, 10, 10, nullptr); - }); - FrameBuilder frameBuilder(SkRect::MakeWH(100, 200), 100, 200, - sLightGeometry, Caches::getInstance()); + auto node = TestUtils::createNode<RecordingCanvas>( + 0, 0, 100, 200, [](RenderProperties& props, RecordingCanvas& canvas) { + sk_sp<Bitmap> bitmap(TestUtils::createBitmap(25, 25)); + canvas.drawRect(0, 0, 100, 200, SkPaint()); + canvas.drawBitmap(*bitmap, 10, 10, nullptr); + }); + FrameBuilder frameBuilder(SkRect::MakeWH(100, 200), 100, 200, sLightGeometry, + Caches::getInstance()); frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); SimpleTestRenderer renderer; frameBuilder.replayBakedOps<TestDispatcher>(renderer); - EXPECT_EQ(4, renderer.getIndex()); // 2 ops + start + end + EXPECT_EQ(4, renderer.getIndex()); // 2 ops + start + end } RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, simpleStroke) { @@ -156,14 +152,14 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, simpleStroke) { } }; - auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 200, - [](RenderProperties& props, RecordingCanvas& canvas) { - SkPaint strokedPaint; - strokedPaint.setStrokeWidth(10); - canvas.drawPoint(50, 50, strokedPaint); - }); - FrameBuilder frameBuilder(SkRect::MakeWH(100, 200), 100, 200, - sLightGeometry, Caches::getInstance()); + auto node = TestUtils::createNode<RecordingCanvas>( + 0, 0, 100, 200, [](RenderProperties& props, RecordingCanvas& canvas) { + SkPaint strokedPaint; + strokedPaint.setStrokeWidth(10); + canvas.drawPoint(50, 50, strokedPaint); + }); + FrameBuilder frameBuilder(SkRect::MakeWH(100, 200), 100, 200, sLightGeometry, + Caches::getInstance()); frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); SimpleStrokeTestRenderer renderer; @@ -171,7 +167,6 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, simpleStroke) { EXPECT_EQ(1, renderer.getIndex()); } - RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, arcStrokeClip) { class ArcStrokeClipTestRenderer : public TestRendererBase { public: @@ -184,15 +179,15 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, arcStrokeClip) { } }; - auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200, - [](RenderProperties& props, RecordingCanvas& canvas) { - canvas.clipRect(25, 25, 175, 175, SkClipOp::kIntersect); - SkPaint aaPaint; - aaPaint.setAntiAlias(true); - canvas.drawArc(25, 25, 175, 175, 40, 180, true, aaPaint); - }); - FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, - sLightGeometry, Caches::getInstance()); + auto node = TestUtils::createNode<RecordingCanvas>( + 0, 0, 200, 200, [](RenderProperties& props, RecordingCanvas& canvas) { + canvas.clipRect(25, 25, 175, 175, SkClipOp::kIntersect); + SkPaint aaPaint; + aaPaint.setAntiAlias(true); + canvas.drawArc(25, 25, 175, 175, 40, 180, true, aaPaint); + }); + FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, sLightGeometry, + Caches::getInstance()); frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); ArcStrokeClipTestRenderer renderer; @@ -201,15 +196,15 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, arcStrokeClip) { } RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, simpleRejection) { - auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200, - [](RenderProperties& props, RecordingCanvas& canvas) { + auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200, [](RenderProperties& props, + RecordingCanvas& canvas) { canvas.save(SaveFlags::MatrixClip); - canvas.clipRect(200, 200, 400, 400, SkClipOp::kIntersect); // intersection should be empty + canvas.clipRect(200, 200, 400, 400, SkClipOp::kIntersect); // intersection should be empty canvas.drawRect(0, 0, 400, 400, SkPaint()); canvas.restore(); }); - FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, - sLightGeometry, Caches::getInstance()); + FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, sLightGeometry, + Caches::getInstance()); frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); FailRenderer renderer; @@ -228,30 +223,30 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, simpleBatching) { } }; - auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200, - [](RenderProperties& props, RecordingCanvas& canvas) { - - sk_sp<Bitmap> bitmap(TestUtils::createBitmap(10, 10, - kAlpha_8_SkColorType)); // Disable merging by using alpha 8 bitmap - - // Alternate between drawing rects and bitmaps, with bitmaps overlapping rects. - // Rects don't overlap bitmaps, so bitmaps should be brought to front as a group. - canvas.save(SaveFlags::MatrixClip); - for (int i = 0; i < LOOPS; i++) { - canvas.translate(0, 10); - canvas.drawRect(0, 0, 10, 10, SkPaint()); - canvas.drawBitmap(*bitmap, 5, 0, nullptr); - } - canvas.restore(); - }); - FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, - sLightGeometry, Caches::getInstance()); + auto node = TestUtils::createNode<RecordingCanvas>( + 0, 0, 200, 200, [](RenderProperties& props, RecordingCanvas& canvas) { + + sk_sp<Bitmap> bitmap(TestUtils::createBitmap( + 10, 10, + kAlpha_8_SkColorType)); // Disable merging by using alpha 8 bitmap + + // Alternate between drawing rects and bitmaps, with bitmaps overlapping rects. + // Rects don't overlap bitmaps, so bitmaps should be brought to front as a group. + canvas.save(SaveFlags::MatrixClip); + for (int i = 0; i < LOOPS; i++) { + canvas.translate(0, 10); + canvas.drawRect(0, 0, 10, 10, SkPaint()); + canvas.drawBitmap(*bitmap, 5, 0, nullptr); + } + canvas.restore(); + }); + FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, sLightGeometry, + Caches::getInstance()); frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); SimpleBatchingTestRenderer renderer; frameBuilder.replayBakedOps<TestDispatcher>(renderer); - EXPECT_EQ(2 * LOOPS, renderer.getIndex()) - << "Expect number of ops = 2 * loop count"; + EXPECT_EQ(2 * LOOPS, renderer.getIndex()) << "Expect number of ops = 2 * loop count"; } RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, deferRenderNode_translateClip) { @@ -261,19 +256,19 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, deferRenderNode_translateClip) { EXPECT_EQ(0, mIndex++); EXPECT_EQ(Rect(5, 10, 55, 60), state.computedState.clippedBounds); EXPECT_EQ(OpClipSideFlags::Right | OpClipSideFlags::Bottom, - state.computedState.clipSideFlags); + state.computedState.clipSideFlags); } }; - auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100, - [](RenderProperties& props, RecordingCanvas& canvas) { - canvas.drawRect(0, 0, 100, 100, SkPaint()); - }); + auto node = TestUtils::createNode<RecordingCanvas>( + 0, 0, 100, 100, [](RenderProperties& props, RecordingCanvas& canvas) { + canvas.drawRect(0, 0, 100, 100, SkPaint()); + }); - FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, - sLightGeometry, Caches::getInstance()); - frameBuilder.deferRenderNode(5, 10, Rect(50, 50), // translate + clip node - *TestUtils::getSyncedNode(node)); + FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, sLightGeometry, + Caches::getInstance()); + frameBuilder.deferRenderNode(5, 10, Rect(50, 50), // translate + clip node + *TestUtils::getSyncedNode(node)); DeferRenderNodeTranslateClipTestRenderer renderer; frameBuilder.replayBakedOps<TestDispatcher>(renderer); @@ -287,27 +282,27 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, deferRenderNodeScene) { const Rect& clippedBounds = state.computedState.clippedBounds; Matrix4 expected; switch (mIndex++) { - case 0: - // background - left side - EXPECT_EQ(Rect(600, 100, 700, 500), clippedBounds); - expected.loadTranslate(100, 100, 0); - break; - case 1: - // background - top side - EXPECT_EQ(Rect(100, 400, 600, 500), clippedBounds); - expected.loadTranslate(100, 100, 0); - break; - case 2: - // content - EXPECT_EQ(Rect(100, 100, 700, 500), clippedBounds); - expected.loadTranslate(-50, -50, 0); - break; - case 3: - // overlay - EXPECT_EQ(Rect(0, 0, 800, 200), clippedBounds); - break; - default: - ADD_FAILURE() << "Too many rects observed"; + case 0: + // background - left side + EXPECT_EQ(Rect(600, 100, 700, 500), clippedBounds); + expected.loadTranslate(100, 100, 0); + break; + case 1: + // background - top side + EXPECT_EQ(Rect(100, 400, 600, 500), clippedBounds); + expected.loadTranslate(100, 100, 0); + break; + case 2: + // content + EXPECT_EQ(Rect(100, 100, 700, 500), clippedBounds); + expected.loadTranslate(-50, -50, 0); + break; + case 3: + // overlay + EXPECT_EQ(Rect(0, 0, 800, 200), clippedBounds); + break; + default: + ADD_FAILURE() << "Too many rects observed"; } EXPECT_EQ(expected, state.computedState.transform); } @@ -318,31 +313,32 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, deferRenderNodeScene) { transparentPaint.setAlpha(128); // backdrop - nodes.push_back(TestUtils::createNode<RecordingCanvas>(100, 100, 700, 500, // 600x400 + nodes.push_back(TestUtils::createNode<RecordingCanvas>( + 100, 100, 700, 500, // 600x400 [&transparentPaint](RenderProperties& props, RecordingCanvas& canvas) { - canvas.drawRect(0, 0, 600, 400, transparentPaint); - })); + canvas.drawRect(0, 0, 600, 400, transparentPaint); + })); // content - Rect contentDrawBounds(150, 150, 650, 450); // 500x300 - nodes.push_back(TestUtils::createNode<RecordingCanvas>(0, 0, 800, 600, - [&transparentPaint](RenderProperties& props, RecordingCanvas& canvas) { - canvas.drawRect(0, 0, 800, 600, transparentPaint); - })); + Rect contentDrawBounds(150, 150, 650, 450); // 500x300 + nodes.push_back(TestUtils::createNode<RecordingCanvas>( + 0, 0, 800, 600, [&transparentPaint](RenderProperties& props, RecordingCanvas& canvas) { + canvas.drawRect(0, 0, 800, 600, transparentPaint); + })); // overlay - nodes.push_back(TestUtils::createNode<RecordingCanvas>(0, 0, 800, 600, - [&transparentPaint](RenderProperties& props, RecordingCanvas& canvas) { - canvas.drawRect(0, 0, 800, 200, transparentPaint); - })); + nodes.push_back(TestUtils::createNode<RecordingCanvas>( + 0, 0, 800, 600, [&transparentPaint](RenderProperties& props, RecordingCanvas& canvas) { + canvas.drawRect(0, 0, 800, 200, transparentPaint); + })); for (auto& node : nodes) { TestUtils::syncHierarchyPropertiesAndDisplayList(node); } { - FrameBuilder frameBuilder(SkRect::MakeWH(800, 600), 800, 600, - sLightGeometry, Caches::getInstance()); + FrameBuilder frameBuilder(SkRect::MakeWH(800, 600), 800, 600, sLightGeometry, + Caches::getInstance()); frameBuilder.deferRenderNodeScene(nodes, contentDrawBounds); DeferRenderNodeSceneTestRenderer renderer; @@ -363,8 +359,8 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, deferRenderNodeScene) { { // Validate no crashes if any nodes are missing DisplayLists - FrameBuilder frameBuilder(SkRect::MakeWH(800, 600), 800, 600, - sLightGeometry, Caches::getInstance()); + FrameBuilder frameBuilder(SkRect::MakeWH(800, 600), 800, 600, sLightGeometry, + Caches::getInstance()); frameBuilder.deferRenderNodeScene(nodes, contentDrawBounds); FailRenderer renderer; @@ -396,24 +392,22 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, empty_withFbo0) { void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override { EXPECT_EQ(0, mIndex++); } - void endFrame(const Rect& repaintRect) override { - EXPECT_EQ(1, mIndex++); - } + void endFrame(const Rect& repaintRect) override { EXPECT_EQ(1, mIndex++); } }; - auto node = TestUtils::createNode<RecordingCanvas>(10, 10, 110, 110, - [](RenderProperties& props, RecordingCanvas& canvas) { - // no drawn content - }); + auto node = TestUtils::createNode<RecordingCanvas>( + 10, 10, 110, 110, [](RenderProperties& props, RecordingCanvas& canvas) { + // no drawn content + }); // Draw, but pass node without draw content, so no work is done for primary frame - FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, - sLightGeometry, Caches::getInstance()); + FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, sLightGeometry, + Caches::getInstance()); frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); EmptyWithFbo0TestRenderer renderer; frameBuilder.replayBakedOps<TestDispatcher>(renderer); EXPECT_EQ(2, renderer.getIndex()) << "No drawing content produced," - " but fbo0 update lifecycle should still be observed"; + " but fbo0 update lifecycle should still be observed"; } RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, avoidOverdraw_rects) { @@ -425,17 +419,17 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, avoidOverdraw_rects) { << "Last rect should occlude others."; } }; - auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200, - [](RenderProperties& props, RecordingCanvas& canvas) { - canvas.drawRect(0, 0, 200, 200, SkPaint()); - canvas.drawRect(0, 0, 200, 200, SkPaint()); - canvas.drawRect(10, 10, 190, 190, SkPaint()); - }); + auto node = TestUtils::createNode<RecordingCanvas>( + 0, 0, 200, 200, [](RenderProperties& props, RecordingCanvas& canvas) { + canvas.drawRect(0, 0, 200, 200, SkPaint()); + canvas.drawRect(0, 0, 200, 200, SkPaint()); + canvas.drawRect(10, 10, 190, 190, SkPaint()); + }); // Damage (and therefore clip) is same as last draw, subset of renderable area. // This means last op occludes other contents, and they'll be rejected to avoid overdraw. - FrameBuilder frameBuilder(SkRect::MakeLTRB(10, 10, 190, 190), 200, 200, - sLightGeometry, Caches::getInstance()); + FrameBuilder frameBuilder(SkRect::MakeLTRB(10, 10, 190, 190), 200, 200, sLightGeometry, + Caches::getInstance()); frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); EXPECT_EQ(3u, node->getDisplayList()->getOps().size()) @@ -447,38 +441,38 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, avoidOverdraw_rects) { } RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, avoidOverdraw_bitmaps) { - static sk_sp<Bitmap> opaqueBitmap(TestUtils::createBitmap(50, 50, - SkColorType::kRGB_565_SkColorType)); - static sk_sp<Bitmap> transpBitmap(TestUtils::createBitmap(50, 50, - SkColorType::kAlpha_8_SkColorType)); + static sk_sp<Bitmap> opaqueBitmap( + TestUtils::createBitmap(50, 50, SkColorType::kRGB_565_SkColorType)); + static sk_sp<Bitmap> transpBitmap( + TestUtils::createBitmap(50, 50, SkColorType::kAlpha_8_SkColorType)); class AvoidOverdrawBitmapsTestRenderer : public TestRendererBase { public: void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override { - switch(mIndex++) { - case 0: - EXPECT_EQ(opaqueBitmap.get(), op.bitmap); - break; - case 1: - EXPECT_EQ(transpBitmap.get(), op.bitmap); - break; - default: - ADD_FAILURE() << "Only two ops expected."; + switch (mIndex++) { + case 0: + EXPECT_EQ(opaqueBitmap.get(), op.bitmap); + break; + case 1: + EXPECT_EQ(transpBitmap.get(), op.bitmap); + break; + default: + ADD_FAILURE() << "Only two ops expected."; } } }; - auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 50, 50, - [](RenderProperties& props, RecordingCanvas& canvas) { - canvas.drawRect(0, 0, 50, 50, SkPaint()); - canvas.drawRect(0, 0, 50, 50, SkPaint()); - canvas.drawBitmap(*transpBitmap, 0, 0, nullptr); - - // only the below draws should remain, since they're - canvas.drawBitmap(*opaqueBitmap, 0, 0, nullptr); - canvas.drawBitmap(*transpBitmap, 0, 0, nullptr); - }); - FrameBuilder frameBuilder(SkRect::MakeWH(50, 50), 50, 50, - sLightGeometry, Caches::getInstance()); + auto node = TestUtils::createNode<RecordingCanvas>( + 0, 0, 50, 50, [](RenderProperties& props, RecordingCanvas& canvas) { + canvas.drawRect(0, 0, 50, 50, SkPaint()); + canvas.drawRect(0, 0, 50, 50, SkPaint()); + canvas.drawBitmap(*transpBitmap, 0, 0, nullptr); + + // only the below draws should remain, since they're + canvas.drawBitmap(*opaqueBitmap, 0, 0, nullptr); + canvas.drawBitmap(*transpBitmap, 0, 0, nullptr); + }); + FrameBuilder frameBuilder(SkRect::MakeWH(50, 50), 50, 50, sLightGeometry, + Caches::getInstance()); frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); EXPECT_EQ(5u, node->getDisplayList()->getOps().size()) @@ -498,32 +492,32 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, clippedMerging) { EXPECT_EQ(4u, opList.count); EXPECT_EQ(Rect(10, 10, 90, 90), opList.clip); EXPECT_EQ(OpClipSideFlags::Left | OpClipSideFlags::Top | OpClipSideFlags::Right, - opList.clipSideFlags); + opList.clipSideFlags); } }; - auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100, - [](RenderProperties& props, RecordingCanvas& canvas) { - sk_sp<Bitmap> bitmap(TestUtils::createBitmap(20, 20)); + auto node = TestUtils::createNode<RecordingCanvas>( + 0, 0, 100, 100, [](RenderProperties& props, RecordingCanvas& canvas) { + sk_sp<Bitmap> bitmap(TestUtils::createBitmap(20, 20)); - // left side clipped (to inset left half) - canvas.clipRect(10, 0, 50, 100, SkClipOp::kReplace_deprecated); - canvas.drawBitmap(*bitmap, 0, 40, nullptr); + // left side clipped (to inset left half) + canvas.clipRect(10, 0, 50, 100, SkClipOp::kReplace_deprecated); + canvas.drawBitmap(*bitmap, 0, 40, nullptr); - // top side clipped (to inset top half) - canvas.clipRect(0, 10, 100, 50, SkClipOp::kReplace_deprecated); - canvas.drawBitmap(*bitmap, 40, 0, nullptr); + // top side clipped (to inset top half) + canvas.clipRect(0, 10, 100, 50, SkClipOp::kReplace_deprecated); + canvas.drawBitmap(*bitmap, 40, 0, nullptr); - // right side clipped (to inset right half) - canvas.clipRect(50, 0, 90, 100, SkClipOp::kReplace_deprecated); - canvas.drawBitmap(*bitmap, 80, 40, nullptr); + // right side clipped (to inset right half) + canvas.clipRect(50, 0, 90, 100, SkClipOp::kReplace_deprecated); + canvas.drawBitmap(*bitmap, 80, 40, nullptr); - // bottom not clipped, just abutting (inset bottom half) - canvas.clipRect(0, 50, 100, 90, SkClipOp::kReplace_deprecated); - canvas.drawBitmap(*bitmap, 40, 70, nullptr); - }); + // bottom not clipped, just abutting (inset bottom half) + canvas.clipRect(0, 50, 100, 90, SkClipOp::kReplace_deprecated); + canvas.drawBitmap(*bitmap, 40, 70, nullptr); + }); - FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, - sLightGeometry, Caches::getInstance()); + FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, sLightGeometry, + Caches::getInstance()); frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); ClippedMergingTestRenderer renderer; @@ -536,23 +530,22 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, regionClipStopsMerge) { public: void onTextOp(const TextOp& op, const BakedOpState& state) override { mIndex++; } }; - auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 400, 400, - [](RenderProperties& props, RecordingCanvas& canvas) { - SkPath path; - path.addCircle(200, 200, 200, SkPath::kCW_Direction); - canvas.save(SaveFlags::MatrixClip); - canvas.clipPath(&path, SkClipOp::kIntersect); - SkPaint paint; - paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); - paint.setAntiAlias(true); - paint.setTextSize(50); - TestUtils::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 100); - TestUtils::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 200); - canvas.restore(); - }); - - FrameBuilder frameBuilder(SkRect::MakeWH(400, 400), 400, 400, - sLightGeometry, Caches::getInstance()); + auto node = TestUtils::createNode<RecordingCanvas>( + 0, 0, 400, 400, [](RenderProperties& props, RecordingCanvas& canvas) { + SkPath path; + path.addCircle(200, 200, 200, SkPath::kCW_Direction); + canvas.save(SaveFlags::MatrixClip); + canvas.clipPath(&path, SkClipOp::kIntersect); + SkPaint paint; + paint.setAntiAlias(true); + paint.setTextSize(50); + TestUtils::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 100); + TestUtils::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 200); + canvas.restore(); + }); + + FrameBuilder frameBuilder(SkRect::MakeWH(400, 400), 400, 400, sLightGeometry, + Caches::getInstance()); frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); RegionClipStopsMergeTestRenderer renderer; @@ -572,17 +565,16 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, textMerging) { EXPECT_EQ(OpClipSideFlags::None, opList.states[1]->computedState.clipSideFlags); } }; - auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 400, 400, - [](RenderProperties& props, RecordingCanvas& canvas) { + auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 400, 400, [](RenderProperties& props, + RecordingCanvas& canvas) { SkPaint paint; - paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); paint.setAntiAlias(true); paint.setTextSize(50); - TestUtils::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 0); // will be top clipped - TestUtils::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 100); // not clipped + TestUtils::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 0); // will be top clipped + TestUtils::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 100); // not clipped }); - FrameBuilder frameBuilder(SkRect::MakeWH(400, 400), 400, 400, - sLightGeometry, Caches::getInstance()); + FrameBuilder frameBuilder(SkRect::MakeWH(400, 400), 400, 400, sLightGeometry, + Caches::getInstance()); frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); TextMergingTestRenderer renderer; @@ -603,30 +595,27 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, textStrikethrough) { EXPECT_EQ(5u, opList.count); } }; - auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 2000, - [](RenderProperties& props, RecordingCanvas& canvas) { - SkPaint textPaint; - textPaint.setAntiAlias(true); - textPaint.setTextSize(20); - textPaint.setFlags(textPaint.getFlags() | SkPaint::kStrikeThruText_ReserveFlag); - textPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); - for (int i = 0; i < LOOPS; i++) { - TestUtils::drawUtf8ToCanvas(&canvas, "test text", textPaint, 10, 100 * (i + 1)); - } - }); - - FrameBuilder frameBuilder(SkRect::MakeWH(200, 2000), 200, 2000, - sLightGeometry, Caches::getInstance()); + auto node = TestUtils::createNode<RecordingCanvas>( + 0, 0, 200, 2000, [](RenderProperties& props, RecordingCanvas& canvas) { + SkPaint textPaint; + textPaint.setAntiAlias(true); + textPaint.setTextSize(20); + textPaint.setFlags(textPaint.getFlags() | SkPaint::kStrikeThruText_ReserveFlag); + for (int i = 0; i < LOOPS; i++) { + TestUtils::drawUtf8ToCanvas(&canvas, "test text", textPaint, 10, 100 * (i + 1)); + } + }); + + FrameBuilder frameBuilder(SkRect::MakeWH(200, 2000), 200, 2000, sLightGeometry, + Caches::getInstance()); frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); TextStrikethroughTestRenderer renderer; frameBuilder.replayBakedOps<TestDispatcher>(renderer); - EXPECT_EQ(2 * LOOPS, renderer.getIndex()) - << "Expect number of ops = 2 * loop count"; + EXPECT_EQ(2 * LOOPS, renderer.getIndex()) << "Expect number of ops = 2 * loop count"; } -static auto styles = { - SkPaint::kFill_Style, SkPaint::kStroke_Style, SkPaint::kStrokeAndFill_Style }; +static auto styles = {SkPaint::kFill_Style, SkPaint::kStroke_Style, SkPaint::kStrokeAndFill_Style}; RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, textStyle) { class TextStyleTestRenderer : public TestRendererBase { @@ -659,23 +648,22 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, textStyle) { EXPECT_EQ(stroke, outsetFill); } }; - auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 400, 400, - [](RenderProperties& props, RecordingCanvas& canvas) { - SkPaint paint; - paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); - paint.setAntiAlias(true); - paint.setTextSize(50); - paint.setStrokeWidth(10); - - // draw 3 copies of the same text overlapping, each with a different style. - // They'll get merged, but with - for (auto style : styles) { - paint.setStyle(style); - TestUtils::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 100); - } - }); - FrameBuilder frameBuilder(SkRect::MakeWH(400, 400), 400, 400, - sLightGeometry, Caches::getInstance()); + auto node = TestUtils::createNode<RecordingCanvas>( + 0, 0, 400, 400, [](RenderProperties& props, RecordingCanvas& canvas) { + SkPaint paint; + paint.setAntiAlias(true); + paint.setTextSize(50); + paint.setStrokeWidth(10); + + // draw 3 copies of the same text overlapping, each with a different style. + // They'll get merged, but with + for (auto style : styles) { + paint.setStyle(style); + TestUtils::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 100); + } + }); + FrameBuilder frameBuilder(SkRect::MakeWH(400, 400), 400, 400, sLightGeometry, + Caches::getInstance()); frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); TextStyleTestRenderer renderer; frameBuilder.replayBakedOps<TestDispatcher>(renderer); @@ -696,19 +684,19 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, textureLayer_clipLocalMatrix) { } }; - auto layerUpdater = TestUtils::createTextureLayerUpdater(renderThread, 100, 100, - SkMatrix::MakeTrans(5, 5)); + auto layerUpdater = + TestUtils::createTextureLayerUpdater(renderThread, 100, 100, SkMatrix::MakeTrans(5, 5)); - auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200, - [&layerUpdater](RenderProperties& props, RecordingCanvas& canvas) { - canvas.save(SaveFlags::MatrixClip); - canvas.clipRect(50, 50, 150, 150, SkClipOp::kIntersect); - canvas.drawLayer(layerUpdater.get()); - canvas.restore(); - }); + auto node = TestUtils::createNode<RecordingCanvas>( + 0, 0, 200, 200, [&layerUpdater](RenderProperties& props, RecordingCanvas& canvas) { + canvas.save(SaveFlags::MatrixClip); + canvas.clipRect(50, 50, 150, 150, SkClipOp::kIntersect); + canvas.drawLayer(layerUpdater.get()); + canvas.restore(); + }); - FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, - sLightGeometry, Caches::getInstance()); + FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, sLightGeometry, + Caches::getInstance()); frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); TextureLayerClipLocalMatrixTestRenderer renderer; @@ -728,19 +716,19 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, textureLayer_combineMatrices) { } }; - auto layerUpdater = TestUtils::createTextureLayerUpdater(renderThread, 100, 100, - SkMatrix::MakeTrans(5, 5)); + auto layerUpdater = + TestUtils::createTextureLayerUpdater(renderThread, 100, 100, SkMatrix::MakeTrans(5, 5)); - auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200, - [&layerUpdater](RenderProperties& props, RecordingCanvas& canvas) { - canvas.save(SaveFlags::MatrixClip); - canvas.translate(30, 40); - canvas.drawLayer(layerUpdater.get()); - canvas.restore(); - }); + auto node = TestUtils::createNode<RecordingCanvas>( + 0, 0, 200, 200, [&layerUpdater](RenderProperties& props, RecordingCanvas& canvas) { + canvas.save(SaveFlags::MatrixClip); + canvas.translate(30, 40); + canvas.drawLayer(layerUpdater.get()); + canvas.restore(); + }); - FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, - sLightGeometry, Caches::getInstance()); + FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, sLightGeometry, + Caches::getInstance()); frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); TextureLayerCombineMatricesTestRenderer renderer; @@ -749,20 +737,20 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, textureLayer_combineMatrices) { } RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, textureLayer_reject) { - auto layerUpdater = TestUtils::createTextureLayerUpdater(renderThread, 100, 100, - SkMatrix::MakeTrans(5, 5)); + auto layerUpdater = + TestUtils::createTextureLayerUpdater(renderThread, 100, 100, SkMatrix::MakeTrans(5, 5)); EXPECT_EQ(Layer::Api::OpenGL, layerUpdater->backingLayer()->getApi()); GlLayer* glLayer = static_cast<GlLayer*>(layerUpdater->backingLayer()); - glLayer->setRenderTarget(GL_NONE); // Should be rejected + glLayer->setRenderTarget(GL_NONE); // Should be rejected - auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200, - [&layerUpdater](RenderProperties& props, RecordingCanvas& canvas) { - canvas.drawLayer(layerUpdater.get()); - }); + auto node = TestUtils::createNode<RecordingCanvas>( + 0, 0, 200, 200, [&layerUpdater](RenderProperties& props, RecordingCanvas& canvas) { + canvas.drawLayer(layerUpdater.get()); + }); - FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, - sLightGeometry, Caches::getInstance()); + FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, sLightGeometry, + Caches::getInstance()); frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); FailRenderer renderer; @@ -779,14 +767,14 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, functor_reject) { Functor noopFunctor; // 1 million pixel tall view, scrolled down 80% - auto scrolledFunctorView = TestUtils::createNode<RecordingCanvas>(0, 0, 400, 1000000, - [&noopFunctor](RenderProperties& props, RecordingCanvas& canvas) { - canvas.translate(0, -800000); - canvas.callDrawGLFunction(&noopFunctor, nullptr); - }); - - FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, - sLightGeometry, Caches::getInstance()); + auto scrolledFunctorView = TestUtils::createNode<RecordingCanvas>( + 0, 0, 400, 1000000, [&noopFunctor](RenderProperties& props, RecordingCanvas& canvas) { + canvas.translate(0, -800000); + canvas.callDrawGLFunction(&noopFunctor, nullptr); + }); + + FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, sLightGeometry, + Caches::getInstance()); frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(scrolledFunctorView)); FunctorTestRenderer renderer; @@ -804,14 +792,14 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, deferColorOp_unbounded) { } }; - auto unclippedColorView = TestUtils::createNode<RecordingCanvas>(0, 0, 10, 10, - [](RenderProperties& props, RecordingCanvas& canvas) { - props.setClipToBounds(false); - canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver); - }); + auto unclippedColorView = TestUtils::createNode<RecordingCanvas>( + 0, 0, 10, 10, [](RenderProperties& props, RecordingCanvas& canvas) { + props.setClipToBounds(false); + canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver); + }); - FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, - sLightGeometry, Caches::getInstance()); + FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, sLightGeometry, + Caches::getInstance()); frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(unclippedColorView)); ColorTestRenderer renderer; @@ -823,42 +811,42 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, renderNode) { class RenderNodeTestRenderer : public TestRendererBase { public: void onRectOp(const RectOp& op, const BakedOpState& state) override { - switch(mIndex++) { - case 0: - EXPECT_EQ(Rect(200, 200), state.computedState.clippedBounds); - EXPECT_EQ(SK_ColorDKGRAY, op.paint->getColor()); - break; - case 1: - EXPECT_EQ(Rect(50, 50, 150, 150), state.computedState.clippedBounds); - EXPECT_EQ(SK_ColorWHITE, op.paint->getColor()); - break; - default: - ADD_FAILURE(); + switch (mIndex++) { + case 0: + EXPECT_EQ(Rect(200, 200), state.computedState.clippedBounds); + EXPECT_EQ(SK_ColorDKGRAY, op.paint->getColor()); + break; + case 1: + EXPECT_EQ(Rect(50, 50, 150, 150), state.computedState.clippedBounds); + EXPECT_EQ(SK_ColorWHITE, op.paint->getColor()); + break; + default: + ADD_FAILURE(); } } }; - auto child = TestUtils::createNode<RecordingCanvas>(10, 10, 110, 110, - [](RenderProperties& props, RecordingCanvas& canvas) { - SkPaint paint; - paint.setColor(SK_ColorWHITE); - canvas.drawRect(0, 0, 100, 100, paint); - }); - - auto parent = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200, - [&child](RenderProperties& props, RecordingCanvas& canvas) { - SkPaint paint; - paint.setColor(SK_ColorDKGRAY); - canvas.drawRect(0, 0, 200, 200, paint); - - canvas.save(SaveFlags::MatrixClip); - canvas.translate(40, 40); - canvas.drawRenderNode(child.get()); - canvas.restore(); - }); - - FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, - sLightGeometry, Caches::getInstance()); + auto child = TestUtils::createNode<RecordingCanvas>( + 10, 10, 110, 110, [](RenderProperties& props, RecordingCanvas& canvas) { + SkPaint paint; + paint.setColor(SK_ColorWHITE); + canvas.drawRect(0, 0, 100, 100, paint); + }); + + auto parent = TestUtils::createNode<RecordingCanvas>( + 0, 0, 200, 200, [&child](RenderProperties& props, RecordingCanvas& canvas) { + SkPaint paint; + paint.setColor(SK_ColorDKGRAY); + canvas.drawRect(0, 0, 200, 200, paint); + + canvas.save(SaveFlags::MatrixClip); + canvas.translate(40, 40); + canvas.drawRenderNode(child.get()); + canvas.restore(); + }); + + FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, sLightGeometry, + Caches::getInstance()); frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent)); RenderNodeTestRenderer renderer; @@ -877,15 +865,15 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, clipped) { } }; - auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200, - [](RenderProperties& props, RecordingCanvas& canvas) { - sk_sp<Bitmap> bitmap(TestUtils::createBitmap(200, 200)); - canvas.drawBitmap(*bitmap, 0, 0, nullptr); - }); + auto node = TestUtils::createNode<RecordingCanvas>( + 0, 0, 200, 200, [](RenderProperties& props, RecordingCanvas& canvas) { + sk_sp<Bitmap> bitmap(TestUtils::createBitmap(200, 200)); + canvas.drawBitmap(*bitmap, 0, 0, nullptr); + }); // clip to small area, should see in receiver - FrameBuilder frameBuilder(SkRect::MakeLTRB(10, 20, 30, 40), 200, 200, - sLightGeometry, Caches::getInstance()); + FrameBuilder frameBuilder(SkRect::MakeLTRB(10, 20, 30, 40), 200, 200, sLightGeometry, + Caches::getInstance()); frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); ClippedTestRenderer renderer; @@ -901,9 +889,7 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, saveLayer_simple) { EXPECT_EQ(180u, height); return nullptr; } - void endLayer() override { - EXPECT_EQ(2, mIndex++); - } + void endLayer() override { EXPECT_EQ(2, mIndex++); } void onRectOp(const RectOp& op, const BakedOpState& state) override { EXPECT_EQ(1, mIndex++); EXPECT_EQ(Rect(10, 10, 190, 190), op.unmappedBounds); @@ -926,15 +912,15 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, saveLayer_simple) { } }; - auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200, - [](RenderProperties& props, RecordingCanvas& canvas) { - canvas.saveLayerAlpha(10, 10, 190, 190, 128, SaveFlags::ClipToLayer); - canvas.drawRect(10, 10, 190, 190, SkPaint()); - canvas.restore(); - }); + auto node = TestUtils::createNode<RecordingCanvas>( + 0, 0, 200, 200, [](RenderProperties& props, RecordingCanvas& canvas) { + canvas.saveLayerAlpha(10, 10, 190, 190, 128, SaveFlags::ClipToLayer); + canvas.drawRect(10, 10, 190, 190, SkPaint()); + canvas.restore(); + }); - FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, - sLightGeometry, Caches::getInstance()); + FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, sLightGeometry, + Caches::getInstance()); frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); SaveLayerSimpleTestRenderer renderer; @@ -955,13 +941,15 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, saveLayer_nested) { if (index == 0) { EXPECT_EQ(400u, width); EXPECT_EQ(400u, height); - return (OffscreenBuffer*) 0x400; + return (OffscreenBuffer*)0x400; } else if (index == 3) { EXPECT_EQ(800u, width); EXPECT_EQ(800u, height); - return (OffscreenBuffer*) 0x800; - } else { ADD_FAILURE(); } - return (OffscreenBuffer*) nullptr; + return (OffscreenBuffer*)0x800; + } else { + ADD_FAILURE(); + } + return (OffscreenBuffer*)nullptr; } void endLayer() override { int index = mIndex++; @@ -970,26 +958,28 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, saveLayer_nested) { void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override { EXPECT_EQ(7, mIndex++); } - void endFrame(const Rect& repaintRect) override { - EXPECT_EQ(9, mIndex++); - } + void endFrame(const Rect& repaintRect) override { EXPECT_EQ(9, mIndex++); } void onRectOp(const RectOp& op, const BakedOpState& state) override { const int index = mIndex++; if (index == 1) { - EXPECT_EQ(Rect(400, 400), op.unmappedBounds); // inner rect + EXPECT_EQ(Rect(400, 400), op.unmappedBounds); // inner rect } else if (index == 4) { - EXPECT_EQ(Rect(800, 800), op.unmappedBounds); // outer rect - } else { ADD_FAILURE(); } + EXPECT_EQ(Rect(800, 800), op.unmappedBounds); // outer rect + } else { + ADD_FAILURE(); + } } void onLayerOp(const LayerOp& op, const BakedOpState& state) override { const int index = mIndex++; if (index == 5) { EXPECT_EQ((OffscreenBuffer*)0x400, *op.layerHandle); - EXPECT_EQ(Rect(400, 400), op.unmappedBounds); // inner layer + EXPECT_EQ(Rect(400, 400), op.unmappedBounds); // inner layer } else if (index == 8) { EXPECT_EQ((OffscreenBuffer*)0x800, *op.layerHandle); - EXPECT_EQ(Rect(800, 800), op.unmappedBounds); // outer layer - } else { ADD_FAILURE(); } + EXPECT_EQ(Rect(800, 800), op.unmappedBounds); // outer layer + } else { + ADD_FAILURE(); + } } void recycleTemporaryLayer(OffscreenBuffer* offscreenBuffer) override { const int index = mIndex++; @@ -998,26 +988,26 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, saveLayer_nested) { EXPECT_EQ((OffscreenBuffer*)0x400, offscreenBuffer); } else if (index == 11) { EXPECT_EQ((OffscreenBuffer*)0x800, offscreenBuffer); - } else { ADD_FAILURE(); } - } - }; - - auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 800, 800, - [](RenderProperties& props, RecordingCanvas& canvas) { - canvas.saveLayerAlpha(0, 0, 800, 800, 128, SaveFlags::ClipToLayer); - { - canvas.drawRect(0, 0, 800, 800, SkPaint()); - canvas.saveLayerAlpha(0, 0, 400, 400, 128, SaveFlags::ClipToLayer); - { - canvas.drawRect(0, 0, 400, 400, SkPaint()); + } else { + ADD_FAILURE(); } - canvas.restore(); } - canvas.restore(); - }); + }; - FrameBuilder frameBuilder(SkRect::MakeWH(800, 800), 800, 800, - sLightGeometry, Caches::getInstance()); + auto node = TestUtils::createNode<RecordingCanvas>( + 0, 0, 800, 800, [](RenderProperties& props, RecordingCanvas& canvas) { + canvas.saveLayerAlpha(0, 0, 800, 800, 128, SaveFlags::ClipToLayer); + { + canvas.drawRect(0, 0, 800, 800, SkPaint()); + canvas.saveLayerAlpha(0, 0, 400, 400, 128, SaveFlags::ClipToLayer); + { canvas.drawRect(0, 0, 400, 400, SkPaint()); } + canvas.restore(); + } + canvas.restore(); + }); + + FrameBuilder frameBuilder(SkRect::MakeWH(800, 800), 800, 800, sLightGeometry, + Caches::getInstance()); frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); SaveLayerNestedTestRenderer renderer; @@ -1026,21 +1016,21 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, saveLayer_nested) { } RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, saveLayer_contentRejection) { - auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200, - [](RenderProperties& props, RecordingCanvas& canvas) { - canvas.save(SaveFlags::MatrixClip); - canvas.clipRect(200, 200, 400, 400, SkClipOp::kIntersect); - canvas.saveLayerAlpha(200, 200, 400, 400, 128, SaveFlags::ClipToLayer); + auto node = TestUtils::createNode<RecordingCanvas>( + 0, 0, 200, 200, [](RenderProperties& props, RecordingCanvas& canvas) { + canvas.save(SaveFlags::MatrixClip); + canvas.clipRect(200, 200, 400, 400, SkClipOp::kIntersect); + canvas.saveLayerAlpha(200, 200, 400, 400, 128, SaveFlags::ClipToLayer); - // draw within save layer may still be recorded, but shouldn't be drawn - canvas.drawRect(200, 200, 400, 400, SkPaint()); + // draw within save layer may still be recorded, but shouldn't be drawn + canvas.drawRect(200, 200, 400, 400, SkPaint()); - canvas.restore(); - canvas.restore(); - }); + canvas.restore(); + canvas.restore(); + }); - FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, - sLightGeometry, Caches::getInstance()); + FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, sLightGeometry, + Caches::getInstance()); frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); FailRenderer renderer; @@ -1077,15 +1067,15 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, saveLayerUnclipped_simple) { } }; - auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200, - [](RenderProperties& props, RecordingCanvas& canvas) { - canvas.saveLayerAlpha(10, 10, 190, 190, 128, (SaveFlags::Flags)(0)); - canvas.drawRect(0, 0, 200, 200, SkPaint()); - canvas.restore(); - }); + auto node = TestUtils::createNode<RecordingCanvas>( + 0, 0, 200, 200, [](RenderProperties& props, RecordingCanvas& canvas) { + canvas.saveLayerAlpha(10, 10, 190, 190, 128, (SaveFlags::Flags)(0)); + canvas.drawRect(0, 0, 200, 200, SkPaint()); + canvas.restore(); + }); - FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, - sLightGeometry, Caches::getInstance()); + FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, sLightGeometry, + Caches::getInstance()); frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); SaveLayerUnclippedSimpleTestRenderer renderer; @@ -1110,16 +1100,16 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, saveLayerUnclipped_round) { } }; - auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200, - [](RenderProperties& props, RecordingCanvas& canvas) { - canvas.saveLayerAlpha(10.95f, 10.5f, 189.75f, 189.25f, // values should all round out - 128, (SaveFlags::Flags)(0)); + auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200, [](RenderProperties& props, + RecordingCanvas& canvas) { + canvas.saveLayerAlpha(10.95f, 10.5f, 189.75f, 189.25f, // values should all round out + 128, (SaveFlags::Flags)(0)); canvas.drawRect(0, 0, 200, 200, SkPaint()); canvas.restore(); }); - FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, - sLightGeometry, Caches::getInstance()); + FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, sLightGeometry, + Caches::getInstance()); frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); SaveLayerUnclippedRoundTestRenderer renderer; @@ -1162,21 +1152,21 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, saveLayerUnclipped_mergedClears) } }; - auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200, - [](RenderProperties& props, RecordingCanvas& canvas) { - - int restoreTo = canvas.save(SaveFlags::MatrixClip); - canvas.scale(2, 2); - canvas.saveLayerAlpha(0, 0, 5, 5, 128, SaveFlags::MatrixClip); - canvas.saveLayerAlpha(95, 0, 100, 5, 128, SaveFlags::MatrixClip); - canvas.saveLayerAlpha(0, 95, 5, 100, 128, SaveFlags::MatrixClip); - canvas.saveLayerAlpha(95, 95, 100, 100, 128, SaveFlags::MatrixClip); - canvas.drawRect(0, 0, 100, 100, SkPaint()); - canvas.restoreToCount(restoreTo); - }); - - FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, - sLightGeometry, Caches::getInstance()); + auto node = TestUtils::createNode<RecordingCanvas>( + 0, 0, 200, 200, [](RenderProperties& props, RecordingCanvas& canvas) { + + int restoreTo = canvas.save(SaveFlags::MatrixClip); + canvas.scale(2, 2); + canvas.saveLayerAlpha(0, 0, 5, 5, 128, SaveFlags::MatrixClip); + canvas.saveLayerAlpha(95, 0, 100, 5, 128, SaveFlags::MatrixClip); + canvas.saveLayerAlpha(0, 95, 5, 100, 128, SaveFlags::MatrixClip); + canvas.saveLayerAlpha(95, 95, 100, 100, 128, SaveFlags::MatrixClip); + canvas.drawRect(0, 0, 100, 100, SkPaint()); + canvas.restoreToCount(restoreTo); + }); + + FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, sLightGeometry, + Caches::getInstance()); frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); SaveLayerUnclippedMergedClearsTestRenderer renderer; @@ -1209,17 +1199,17 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, saveLayerUnclipped_clearClip) { } }; - auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200, - [](RenderProperties& props, RecordingCanvas& canvas) { - // save smaller than clip, so we get unclipped behavior - canvas.saveLayerAlpha(10, 10, 190, 190, 128, (SaveFlags::Flags)(0)); - canvas.drawRect(0, 0, 200, 200, SkPaint()); - canvas.restore(); - }); + auto node = TestUtils::createNode<RecordingCanvas>( + 0, 0, 200, 200, [](RenderProperties& props, RecordingCanvas& canvas) { + // save smaller than clip, so we get unclipped behavior + canvas.saveLayerAlpha(10, 10, 190, 190, 128, (SaveFlags::Flags)(0)); + canvas.drawRect(0, 0, 200, 200, SkPaint()); + canvas.restore(); + }); // draw with partial screen dirty, and assert we see that rect later - FrameBuilder frameBuilder(SkRect::MakeLTRB(50, 50, 150, 150), 200, 200, - sLightGeometry, Caches::getInstance()); + FrameBuilder frameBuilder(SkRect::MakeLTRB(50, 50, 150, 150), 200, 200, sLightGeometry, + Caches::getInstance()); frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); SaveLayerUnclippedClearClipTestRenderer renderer; @@ -1228,17 +1218,17 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, saveLayerUnclipped_clearClip) { } RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, saveLayerUnclipped_reject) { - auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200, - [](RenderProperties& props, RecordingCanvas& canvas) { - // unclipped savelayer + rect both in area that won't intersect with dirty - canvas.saveLayerAlpha(100, 100, 200, 200, 128, (SaveFlags::Flags)(0)); - canvas.drawRect(100, 100, 200, 200, SkPaint()); - canvas.restore(); - }); + auto node = TestUtils::createNode<RecordingCanvas>( + 0, 0, 200, 200, [](RenderProperties& props, RecordingCanvas& canvas) { + // unclipped savelayer + rect both in area that won't intersect with dirty + canvas.saveLayerAlpha(100, 100, 200, 200, 128, (SaveFlags::Flags)(0)); + canvas.drawRect(100, 100, 200, 200, SkPaint()); + canvas.restore(); + }); // draw with partial screen dirty that doesn't intersect with savelayer - FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 200, 200, - sLightGeometry, Caches::getInstance()); + FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 200, 200, sLightGeometry, + Caches::getInstance()); frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); FailRenderer renderer; @@ -1253,7 +1243,7 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, saveLayerUnclipped_complex) { class SaveLayerUnclippedComplexTestRenderer : public TestRendererBase { public: OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) { - EXPECT_EQ(0, mIndex++); // savelayer first + EXPECT_EQ(0, mIndex++); // savelayer first return (OffscreenBuffer*)0xabcd; } void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override { @@ -1275,9 +1265,7 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, saveLayerUnclipped_complex) { int index = mIndex++; EXPECT_TRUE(index == 4 || index == 10); } - void endLayer() override { - EXPECT_EQ(5, mIndex++); - } + void endLayer() override { EXPECT_EQ(5, mIndex++); } void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override { EXPECT_EQ(6, mIndex++); } @@ -1285,28 +1273,27 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, saveLayerUnclipped_complex) { EXPECT_EQ(9, mIndex++); EXPECT_EQ((OffscreenBuffer*)0xabcd, *op.layerHandle); } - void endFrame(const Rect& repaintRect) override { - EXPECT_EQ(11, mIndex++); - } + void endFrame(const Rect& repaintRect) override { EXPECT_EQ(11, mIndex++); } void recycleTemporaryLayer(OffscreenBuffer* offscreenBuffer) override { EXPECT_EQ(12, mIndex++); EXPECT_EQ((OffscreenBuffer*)0xabcd, offscreenBuffer); } }; - auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 600, 600, // 500x500 triggers clipping + auto node = TestUtils::createNode<RecordingCanvas>( + 0, 0, 600, 600, // 500x500 triggers clipping [](RenderProperties& props, RecordingCanvas& canvas) { - canvas.saveLayerAlpha(0, 0, 500, 500, 128, (SaveFlags::Flags)0); // unclipped - canvas.saveLayerAlpha(100, 100, 400, 400, 128, SaveFlags::ClipToLayer); // clipped - canvas.saveLayerAlpha(200, 200, 300, 300, 128, (SaveFlags::Flags)0); // unclipped - canvas.drawRect(200, 200, 300, 300, SkPaint()); - canvas.restore(); - canvas.restore(); - canvas.restore(); - }); - - FrameBuilder frameBuilder(SkRect::MakeWH(600, 600), 600, 600, - sLightGeometry, Caches::getInstance()); + canvas.saveLayerAlpha(0, 0, 500, 500, 128, (SaveFlags::Flags)0); // unclipped + canvas.saveLayerAlpha(100, 100, 400, 400, 128, SaveFlags::ClipToLayer); // clipped + canvas.saveLayerAlpha(200, 200, 300, 300, 128, (SaveFlags::Flags)0); // unclipped + canvas.drawRect(200, 200, 300, 300, SkPaint()); + canvas.restore(); + canvas.restore(); + canvas.restore(); + }); + + FrameBuilder frameBuilder(SkRect::MakeWH(600, 600), 600, 600, sLightGeometry, + Caches::getInstance()); frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); SaveLayerUnclippedComplexTestRenderer renderer; @@ -1332,27 +1319,23 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, hwLayer_simple) { EXPECT_EQ(Rect(25, 25, 75, 75), state.computedState.clipRect()) << "Damage rect should be used to clip layer content"; } - void endLayer() override { - EXPECT_EQ(2, mIndex++); - } + void endLayer() override { EXPECT_EQ(2, mIndex++); } void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override { EXPECT_EQ(3, mIndex++); } void onLayerOp(const LayerOp& op, const BakedOpState& state) override { EXPECT_EQ(4, mIndex++); } - void endFrame(const Rect& repaintRect) override { - EXPECT_EQ(5, mIndex++); - } + void endFrame(const Rect& repaintRect) override { EXPECT_EQ(5, mIndex++); } }; - auto node = TestUtils::createNode<RecordingCanvas>(10, 10, 110, 110, - [](RenderProperties& props, RecordingCanvas& canvas) { - props.mutateLayerProperties().setType(LayerType::RenderLayer); - SkPaint paint; - paint.setColor(SK_ColorWHITE); - canvas.drawRect(0, 0, 100, 100, paint); - }); + auto node = TestUtils::createNode<RecordingCanvas>( + 10, 10, 110, 110, [](RenderProperties& props, RecordingCanvas& canvas) { + props.mutateLayerProperties().setType(LayerType::RenderLayer); + SkPaint paint; + paint.setColor(SK_ColorWHITE); + canvas.drawRect(0, 0, 100, 100, paint); + }); OffscreenBuffer** layerHandle = node->getLayerHandle(); // create RenderNode's layer here in same way prepareTree would @@ -1362,11 +1345,11 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, hwLayer_simple) { auto syncedNode = TestUtils::getSyncedNode(node); // only enqueue partial damage - LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid + LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid layerUpdateQueue.enqueueLayerWithDamage(node.get(), Rect(25, 25, 75, 75)); - FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, - sLightGeometry, Caches::getInstance()); + FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, sLightGeometry, + Caches::getInstance()); frameBuilder.deferLayers(layerUpdateQueue); frameBuilder.deferRenderNode(*syncedNode); @@ -1388,7 +1371,7 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, hwLayer_complex) { class HwLayerComplexTestRenderer : public TestRendererBase { public: OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) { - EXPECT_EQ(3, mIndex++); // savelayer first + EXPECT_EQ(3, mIndex++); // savelayer first return (OffscreenBuffer*)0xabcd; } void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override { @@ -1401,7 +1384,9 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, hwLayer_complex) { // starting outer layer EXPECT_EQ(200u, offscreenBuffer->viewportWidth); EXPECT_EQ(200u, offscreenBuffer->viewportHeight); - } else { ADD_FAILURE(); } + } else { + ADD_FAILURE(); + } } void onRectOp(const RectOp& op, const BakedOpState& state) override { int index = mIndex++; @@ -1411,7 +1396,9 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, hwLayer_complex) { } else if (index == 7) { // outer layer's rect (grey) EXPECT_EQ(SK_ColorDKGRAY, op.paint->getColor()); - } else { ADD_FAILURE(); } + } else { + ADD_FAILURE(); + } } void endLayer() override { int index = mIndex++; @@ -1431,49 +1418,49 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, hwLayer_complex) { } else if (index == 11) { EXPECT_EQ(200u, layer->viewportWidth); EXPECT_EQ(200u, layer->viewportHeight); - } else { ADD_FAILURE(); } - } - void endFrame(const Rect& repaintRect) override { - EXPECT_EQ(12, mIndex++); + } else { + ADD_FAILURE(); + } } + void endFrame(const Rect& repaintRect) override { EXPECT_EQ(12, mIndex++); } void recycleTemporaryLayer(OffscreenBuffer* offscreenBuffer) override { EXPECT_EQ(13, mIndex++); } }; - auto child = TestUtils::createNode<RecordingCanvas>(50, 50, 150, 150, - [](RenderProperties& props, RecordingCanvas& canvas) { - props.mutateLayerProperties().setType(LayerType::RenderLayer); - SkPaint paint; - paint.setColor(SK_ColorWHITE); - canvas.drawRect(0, 0, 100, 100, paint); - }); + auto child = TestUtils::createNode<RecordingCanvas>( + 50, 50, 150, 150, [](RenderProperties& props, RecordingCanvas& canvas) { + props.mutateLayerProperties().setType(LayerType::RenderLayer); + SkPaint paint; + paint.setColor(SK_ColorWHITE); + canvas.drawRect(0, 0, 100, 100, paint); + }); OffscreenBuffer childLayer(renderThread.renderState(), Caches::getInstance(), 100, 100); *(child->getLayerHandle()) = &childLayer; RenderNode* childPtr = child.get(); - auto parent = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200, - [childPtr](RenderProperties& props, RecordingCanvas& canvas) { - props.mutateLayerProperties().setType(LayerType::RenderLayer); - SkPaint paint; - paint.setColor(SK_ColorDKGRAY); - canvas.drawRect(0, 0, 200, 200, paint); - - canvas.saveLayerAlpha(50, 50, 150, 150, 128, SaveFlags::ClipToLayer); - canvas.drawRenderNode(childPtr); - canvas.restore(); - }); + auto parent = TestUtils::createNode<RecordingCanvas>( + 0, 0, 200, 200, [childPtr](RenderProperties& props, RecordingCanvas& canvas) { + props.mutateLayerProperties().setType(LayerType::RenderLayer); + SkPaint paint; + paint.setColor(SK_ColorDKGRAY); + canvas.drawRect(0, 0, 200, 200, paint); + + canvas.saveLayerAlpha(50, 50, 150, 150, 128, SaveFlags::ClipToLayer); + canvas.drawRenderNode(childPtr); + canvas.restore(); + }); OffscreenBuffer parentLayer(renderThread.renderState(), Caches::getInstance(), 200, 200); *(parent->getLayerHandle()) = &parentLayer; auto syncedNode = TestUtils::getSyncedNode(parent); - LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid + LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid layerUpdateQueue.enqueueLayerWithDamage(child.get(), Rect(100, 100)); layerUpdateQueue.enqueueLayerWithDamage(parent.get(), Rect(200, 200)); - FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, - sLightGeometry, Caches::getInstance()); + FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, sLightGeometry, + Caches::getInstance()); frameBuilder.deferLayers(layerUpdateQueue); frameBuilder.deferRenderNode(*syncedNode); @@ -1486,7 +1473,6 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, hwLayer_complex) { *(parent->getLayerHandle()) = nullptr; } - RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, buildLayer) { class BuildLayerTestRenderer : public TestRendererBase { public: @@ -1505,9 +1491,7 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, buildLayer) { EXPECT_EQ(Rect(25, 25, 75, 75), state.computedState.clipRect()) << "Damage rect should be used to clip layer content"; } - void endLayer() override { - EXPECT_EQ(2, mIndex++); - } + void endLayer() override { EXPECT_EQ(2, mIndex++); } void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override { ADD_FAILURE() << "Primary frame draw not expected in this test"; } @@ -1516,11 +1500,11 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, buildLayer) { } }; - auto node = TestUtils::createNode<RecordingCanvas>(10, 10, 110, 110, - [](RenderProperties& props, RecordingCanvas& canvas) { - props.mutateLayerProperties().setType(LayerType::RenderLayer); - canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver); - }); + auto node = TestUtils::createNode<RecordingCanvas>( + 10, 10, 110, 110, [](RenderProperties& props, RecordingCanvas& canvas) { + props.mutateLayerProperties().setType(LayerType::RenderLayer); + canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver); + }); OffscreenBuffer** layerHandle = node->getLayerHandle(); // create RenderNode's layer here in same way prepareTree would @@ -1530,7 +1514,7 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, buildLayer) { TestUtils::syncHierarchyPropertiesAndDisplayList(node); // only enqueue partial damage - LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid + LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid layerUpdateQueue.enqueueLayerWithDamage(node.get(), Rect(25, 25, 75, 75)); // Draw, but pass empty node list, so no work is done for primary frame @@ -1552,65 +1536,69 @@ static void drawOrderedRect(Canvas* canvas, uint8_t expectedDrawOrder) { canvas->drawRect(0, 0, 100, 100, paint); } static void drawOrderedNode(Canvas* canvas, uint8_t expectedDrawOrder, float z) { - auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100, - [expectedDrawOrder](RenderProperties& props, RecordingCanvas& canvas) { - drawOrderedRect(&canvas, expectedDrawOrder); - }); + auto node = TestUtils::createNode<RecordingCanvas>( + 0, 0, 100, 100, [expectedDrawOrder](RenderProperties& props, RecordingCanvas& canvas) { + drawOrderedRect(&canvas, expectedDrawOrder); + }); node->mutateStagingProperties().setTranslationZ(z); node->setPropertyFieldsDirty(RenderNode::TRANSLATION_Z); - canvas->drawRenderNode(node.get()); // canvas takes reference/sole ownership + canvas->drawRenderNode(node.get()); // canvas takes reference/sole ownership } -static void drawOrderedNode(Canvas* canvas, uint8_t expectedDrawOrder, +static void drawOrderedNode( + Canvas* canvas, uint8_t expectedDrawOrder, std::function<void(RenderProperties& props, RecordingCanvas& canvas)> setup) { - auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100, + auto node = TestUtils::createNode<RecordingCanvas>( + 0, 0, 100, 100, [expectedDrawOrder, setup](RenderProperties& props, RecordingCanvas& canvas) { - drawOrderedRect(&canvas, expectedDrawOrder); - if (setup) { - setup(props, canvas); - } - }); - canvas->drawRenderNode(node.get()); // canvas takes reference/sole ownership + drawOrderedRect(&canvas, expectedDrawOrder); + if (setup) { + setup(props, canvas); + } + }); + canvas->drawRenderNode(node.get()); // canvas takes reference/sole ownership } class ZReorderTestRenderer : public TestRendererBase { public: void onRectOp(const RectOp& op, const BakedOpState& state) override { - int expectedOrder = SkColorGetB(op.paint->getColor()); // extract order from blue channel + int expectedOrder = SkColorGetB(op.paint->getColor()); // extract order from blue channel EXPECT_EQ(expectedOrder, mIndex++) << "An op was drawn out of order"; } }; -} // end anonymous namespace +} // end anonymous namespace RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, zReorder) { - auto parent = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100, - [](RenderProperties& props, RecordingCanvas& canvas) { - canvas.insertReorderBarrier(true); - canvas.insertReorderBarrier(false); - drawOrderedNode(&canvas, 0, 10.0f); // in reorder=false at this point, so played inorder - drawOrderedRect(&canvas, 1); - canvas.insertReorderBarrier(true); - drawOrderedNode(&canvas, 6, 2.0f); - drawOrderedRect(&canvas, 3); - drawOrderedNode(&canvas, 4, 0.0f); - drawOrderedRect(&canvas, 5); - drawOrderedNode(&canvas, 2, -2.0f); - drawOrderedNode(&canvas, 7, 2.0f); - canvas.insertReorderBarrier(false); - drawOrderedRect(&canvas, 8); - drawOrderedNode(&canvas, 9, -10.0f); // in reorder=false at this point, so played inorder - canvas.insertReorderBarrier(true); //reorder a node ahead of drawrect op - drawOrderedRect(&canvas, 11); - drawOrderedNode(&canvas, 10, -1.0f); - canvas.insertReorderBarrier(false); - canvas.insertReorderBarrier(true); //test with two empty reorder sections - canvas.insertReorderBarrier(true); - canvas.insertReorderBarrier(false); - drawOrderedRect(&canvas, 12); - }); - FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, - sLightGeometry, Caches::getInstance()); + auto parent = TestUtils::createNode<RecordingCanvas>( + 0, 0, 100, 100, [](RenderProperties& props, RecordingCanvas& canvas) { + canvas.insertReorderBarrier(true); + canvas.insertReorderBarrier(false); + drawOrderedNode(&canvas, 0, + 10.0f); // in reorder=false at this point, so played inorder + drawOrderedRect(&canvas, 1); + canvas.insertReorderBarrier(true); + drawOrderedNode(&canvas, 6, 2.0f); + drawOrderedRect(&canvas, 3); + drawOrderedNode(&canvas, 4, 0.0f); + drawOrderedRect(&canvas, 5); + drawOrderedNode(&canvas, 2, -2.0f); + drawOrderedNode(&canvas, 7, 2.0f); + canvas.insertReorderBarrier(false); + drawOrderedRect(&canvas, 8); + drawOrderedNode(&canvas, 9, + -10.0f); // in reorder=false at this point, so played inorder + canvas.insertReorderBarrier(true); // reorder a node ahead of drawrect op + drawOrderedRect(&canvas, 11); + drawOrderedNode(&canvas, 10, -1.0f); + canvas.insertReorderBarrier(false); + canvas.insertReorderBarrier(true); // test with two empty reorder sections + canvas.insertReorderBarrier(true); + canvas.insertReorderBarrier(false); + drawOrderedRect(&canvas, 12); + }); + FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, sLightGeometry, + Caches::getInstance()); frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent)); ZReorderTestRenderer renderer; @@ -1628,28 +1616,28 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorder) { Matrix4 expectedMatrix; switch (index) { - case 0: - EXPECT_EQ(Rect(100, 100), op.unmappedBounds); - EXPECT_EQ(SK_ColorWHITE, op.paint->getColor()); - expectedMatrix.loadIdentity(); - EXPECT_EQ(nullptr, state.computedState.localProjectionPathMask); - break; - case 1: - EXPECT_EQ(Rect(-10, -10, 60, 60), op.unmappedBounds); - EXPECT_EQ(SK_ColorDKGRAY, op.paint->getColor()); - expectedMatrix.loadTranslate(50 - scrollX, 50 - scrollY, 0); - ASSERT_NE(nullptr, state.computedState.localProjectionPathMask); - EXPECT_EQ(Rect(-35, -30, 45, 50), - Rect(state.computedState.localProjectionPathMask->getBounds())); - break; - case 2: - EXPECT_EQ(Rect(100, 50), op.unmappedBounds); - EXPECT_EQ(SK_ColorBLUE, op.paint->getColor()); - expectedMatrix.loadTranslate(-scrollX, 50 - scrollY, 0); - EXPECT_EQ(nullptr, state.computedState.localProjectionPathMask); - break; - default: - ADD_FAILURE(); + case 0: + EXPECT_EQ(Rect(100, 100), op.unmappedBounds); + EXPECT_EQ(SK_ColorWHITE, op.paint->getColor()); + expectedMatrix.loadIdentity(); + EXPECT_EQ(nullptr, state.computedState.localProjectionPathMask); + break; + case 1: + EXPECT_EQ(Rect(-10, -10, 60, 60), op.unmappedBounds); + EXPECT_EQ(SK_ColorDKGRAY, op.paint->getColor()); + expectedMatrix.loadTranslate(50 - scrollX, 50 - scrollY, 0); + ASSERT_NE(nullptr, state.computedState.localProjectionPathMask); + EXPECT_EQ(Rect(-35, -30, 45, 50), + Rect(state.computedState.localProjectionPathMask->getBounds())); + break; + case 2: + EXPECT_EQ(Rect(100, 50), op.unmappedBounds); + EXPECT_EQ(SK_ColorBLUE, op.paint->getColor()); + expectedMatrix.loadTranslate(-scrollX, 50 - scrollY, 0); + EXPECT_EQ(nullptr, state.computedState.localProjectionPathMask); + break; + default: + ADD_FAILURE(); } EXPECT_EQ(expectedMatrix, state.computedState.transform); } @@ -1663,47 +1651,51 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorder) { * The parent is scrolled by scrollX/scrollY, but this does not affect the background * (which isn't affected by scroll). */ - auto receiverBackground = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100, - [](RenderProperties& properties, RecordingCanvas& canvas) { - properties.setProjectionReceiver(true); - // scroll doesn't apply to background, so undone via translationX/Y - // NOTE: translationX/Y only! no other transform properties may be set for a proj receiver! - properties.setTranslationX(scrollX); - properties.setTranslationY(scrollY); - - SkPaint paint; - paint.setColor(SK_ColorWHITE); - canvas.drawRect(0, 0, 100, 100, paint); - }); - auto projectingRipple = TestUtils::createNode<RecordingCanvas>(50, 0, 100, 50, - [](RenderProperties& properties, RecordingCanvas& canvas) { - properties.setProjectBackwards(true); - properties.setClipToBounds(false); - SkPaint paint; - paint.setColor(SK_ColorDKGRAY); - canvas.drawRect(-10, -10, 60, 60, paint); - }); - auto child = TestUtils::createNode<RecordingCanvas>(0, 50, 100, 100, + auto receiverBackground = TestUtils::createNode<RecordingCanvas>( + 0, 0, 100, 100, [](RenderProperties& properties, RecordingCanvas& canvas) { + properties.setProjectionReceiver(true); + // scroll doesn't apply to background, so undone via translationX/Y + // NOTE: translationX/Y only! no other transform properties may be set for a proj + // receiver! + properties.setTranslationX(scrollX); + properties.setTranslationY(scrollY); + + SkPaint paint; + paint.setColor(SK_ColorWHITE); + canvas.drawRect(0, 0, 100, 100, paint); + }); + auto projectingRipple = TestUtils::createNode<RecordingCanvas>( + 50, 0, 100, 50, [](RenderProperties& properties, RecordingCanvas& canvas) { + properties.setProjectBackwards(true); + properties.setClipToBounds(false); + SkPaint paint; + paint.setColor(SK_ColorDKGRAY); + canvas.drawRect(-10, -10, 60, 60, paint); + }); + auto child = TestUtils::createNode<RecordingCanvas>( + 0, 50, 100, 100, [&projectingRipple](RenderProperties& properties, RecordingCanvas& canvas) { - SkPaint paint; - paint.setColor(SK_ColorBLUE); - canvas.drawRect(0, 0, 100, 50, paint); - canvas.drawRenderNode(projectingRipple.get()); - }); - auto parent = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100, + SkPaint paint; + paint.setColor(SK_ColorBLUE); + canvas.drawRect(0, 0, 100, 50, paint); + canvas.drawRenderNode(projectingRipple.get()); + }); + auto parent = TestUtils::createNode<RecordingCanvas>( + 0, 0, 100, 100, [&receiverBackground, &child](RenderProperties& properties, RecordingCanvas& canvas) { - // Set a rect outline for the projecting ripple to be masked against. - properties.mutableOutline().setRoundRect(10, 10, 90, 90, 5, 1.0f); - - canvas.save(SaveFlags::MatrixClip); - canvas.translate(-scrollX, -scrollY); // Apply scroll (note: bg undoes this internally) - canvas.drawRenderNode(receiverBackground.get()); - canvas.drawRenderNode(child.get()); - canvas.restore(); - }); - - FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, - sLightGeometry, Caches::getInstance()); + // Set a rect outline for the projecting ripple to be masked against. + properties.mutableOutline().setRoundRect(10, 10, 90, 90, 5, 1.0f); + + canvas.save(SaveFlags::MatrixClip); + canvas.translate(-scrollX, + -scrollY); // Apply scroll (note: bg undoes this internally) + canvas.drawRenderNode(receiverBackground.get()); + canvas.drawRenderNode(child.get()); + canvas.restore(); + }); + + FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, sLightGeometry, + Caches::getInstance()); frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent)); ProjectionReorderTestRenderer renderer; @@ -1723,9 +1715,7 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionHwLayer) { EXPECT_EQ(1, mIndex++); ASSERT_EQ(nullptr, state.computedState.localProjectionPathMask); } - void endLayer() override { - EXPECT_EQ(2, mIndex++); - } + void endLayer() override { EXPECT_EQ(2, mIndex++); } void onRectOp(const RectOp& op, const BakedOpState& state) override { EXPECT_EQ(3, mIndex++); ASSERT_EQ(nullptr, state.computedState.localProjectionPathMask); @@ -1737,60 +1727,64 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionHwLayer) { expected.loadTranslate(100 - scrollX, 100 - scrollY, 0); EXPECT_EQ(expected, state.computedState.transform); EXPECT_EQ(Rect(-85, -80, 295, 300), - Rect(state.computedState.localProjectionPathMask->getBounds())); + Rect(state.computedState.localProjectionPathMask->getBounds())); } void onLayerOp(const LayerOp& op, const BakedOpState& state) override { EXPECT_EQ(5, mIndex++); ASSERT_EQ(nullptr, state.computedState.localProjectionPathMask); } }; - auto receiverBackground = TestUtils::createNode<RecordingCanvas>(0, 0, 400, 400, - [](RenderProperties& properties, RecordingCanvas& canvas) { - properties.setProjectionReceiver(true); - // scroll doesn't apply to background, so undone via translationX/Y - // NOTE: translationX/Y only! no other transform properties may be set for a proj receiver! - properties.setTranslationX(scrollX); - properties.setTranslationY(scrollY); + auto receiverBackground = TestUtils::createNode<RecordingCanvas>( + 0, 0, 400, 400, [](RenderProperties& properties, RecordingCanvas& canvas) { + properties.setProjectionReceiver(true); + // scroll doesn't apply to background, so undone via translationX/Y + // NOTE: translationX/Y only! no other transform properties may be set for a proj + // receiver! + properties.setTranslationX(scrollX); + properties.setTranslationY(scrollY); - canvas.drawRect(0, 0, 400, 400, SkPaint()); - }); - auto projectingRipple = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200, - [](RenderProperties& properties, RecordingCanvas& canvas) { - properties.setProjectBackwards(true); - properties.setClipToBounds(false); - canvas.drawOval(100, 100, 300, 300, SkPaint()); // drawn mostly out of layer bounds - }); - auto child = TestUtils::createNode<RecordingCanvas>(100, 100, 300, 300, + canvas.drawRect(0, 0, 400, 400, SkPaint()); + }); + auto projectingRipple = TestUtils::createNode<RecordingCanvas>( + 0, 0, 200, 200, [](RenderProperties& properties, RecordingCanvas& canvas) { + properties.setProjectBackwards(true); + properties.setClipToBounds(false); + canvas.drawOval(100, 100, 300, 300, SkPaint()); // drawn mostly out of layer bounds + }); + auto child = TestUtils::createNode<RecordingCanvas>( + 100, 100, 300, 300, [&projectingRipple](RenderProperties& properties, RecordingCanvas& canvas) { - properties.mutateLayerProperties().setType(LayerType::RenderLayer); - canvas.drawRenderNode(projectingRipple.get()); - canvas.drawArc(0, 0, 200, 200, 0.0f, 280.0f, true, SkPaint()); - }); - auto parent = TestUtils::createNode<RecordingCanvas>(0, 0, 400, 400, + properties.mutateLayerProperties().setType(LayerType::RenderLayer); + canvas.drawRenderNode(projectingRipple.get()); + canvas.drawArc(0, 0, 200, 200, 0.0f, 280.0f, true, SkPaint()); + }); + auto parent = TestUtils::createNode<RecordingCanvas>( + 0, 0, 400, 400, [&receiverBackground, &child](RenderProperties& properties, RecordingCanvas& canvas) { - // Set a rect outline for the projecting ripple to be masked against. - properties.mutableOutline().setRoundRect(10, 10, 390, 390, 0, 1.0f); - canvas.translate(-scrollX, -scrollY); // Apply scroll (note: bg undoes this internally) - canvas.drawRenderNode(receiverBackground.get()); - canvas.drawRenderNode(child.get()); - }); + // Set a rect outline for the projecting ripple to be masked against. + properties.mutableOutline().setRoundRect(10, 10, 390, 390, 0, 1.0f); + canvas.translate(-scrollX, + -scrollY); // Apply scroll (note: bg undoes this internally) + canvas.drawRenderNode(receiverBackground.get()); + canvas.drawRenderNode(child.get()); + }); OffscreenBuffer** layerHandle = child->getLayerHandle(); // create RenderNode's layer here in same way prepareTree would, setting windowTransform OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 200, 200); Matrix4 windowTransform; - windowTransform.loadTranslate(100, 100, 0); // total transform of layer's origin + windowTransform.loadTranslate(100, 100, 0); // total transform of layer's origin layer.setWindowTransform(windowTransform); *layerHandle = &layer; auto syncedNode = TestUtils::getSyncedNode(parent); - LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid + LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid layerUpdateQueue.enqueueLayerWithDamage(child.get(), Rect(200, 200)); - FrameBuilder frameBuilder(SkRect::MakeWH(400, 400), 400, 400, - sLightGeometry, Caches::getInstance()); + FrameBuilder frameBuilder(SkRect::MakeWH(400, 400), 400, 400, sLightGeometry, + Caches::getInstance()); frameBuilder.deferLayers(layerUpdateQueue); frameBuilder.deferRenderNode(*syncedNode); @@ -1819,37 +1813,41 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionChildScroll) { EXPECT_TRUE(state.computedState.transform.isIdentity()); } }; - auto receiverBackground = TestUtils::createNode<RecordingCanvas>(0, 0, 400, 400, - [](RenderProperties& properties, RecordingCanvas& canvas) { - properties.setProjectionReceiver(true); - canvas.drawRect(0, 0, 400, 400, SkPaint()); - }); - auto projectingRipple = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200, - [](RenderProperties& properties, RecordingCanvas& canvas) { - // scroll doesn't apply to background, so undone via translationX/Y - // NOTE: translationX/Y only! no other transform properties may be set for a proj receiver! - properties.setTranslationX(scrollX); - properties.setTranslationY(scrollY); - properties.setProjectBackwards(true); - properties.setClipToBounds(false); - canvas.drawOval(0, 0, 200, 200, SkPaint()); - }); - auto child = TestUtils::createNode<RecordingCanvas>(0, 0, 400, 400, + auto receiverBackground = TestUtils::createNode<RecordingCanvas>( + 0, 0, 400, 400, [](RenderProperties& properties, RecordingCanvas& canvas) { + properties.setProjectionReceiver(true); + canvas.drawRect(0, 0, 400, 400, SkPaint()); + }); + auto projectingRipple = TestUtils::createNode<RecordingCanvas>( + 0, 0, 200, 200, [](RenderProperties& properties, RecordingCanvas& canvas) { + // scroll doesn't apply to background, so undone via translationX/Y + // NOTE: translationX/Y only! no other transform properties may be set for a proj + // receiver! + properties.setTranslationX(scrollX); + properties.setTranslationY(scrollY); + properties.setProjectBackwards(true); + properties.setClipToBounds(false); + canvas.drawOval(0, 0, 200, 200, SkPaint()); + }); + auto child = TestUtils::createNode<RecordingCanvas>( + 0, 0, 400, 400, [&projectingRipple](RenderProperties& properties, RecordingCanvas& canvas) { - // Record time clip will be ignored by projectee - canvas.clipRect(100, 100, 300, 300, SkClipOp::kIntersect); - - canvas.translate(-scrollX, -scrollY); // Apply scroll (note: bg undoes this internally) - canvas.drawRenderNode(projectingRipple.get()); - }); - auto parent = TestUtils::createNode<RecordingCanvas>(0, 0, 400, 400, + // Record time clip will be ignored by projectee + canvas.clipRect(100, 100, 300, 300, SkClipOp::kIntersect); + + canvas.translate(-scrollX, + -scrollY); // Apply scroll (note: bg undoes this internally) + canvas.drawRenderNode(projectingRipple.get()); + }); + auto parent = TestUtils::createNode<RecordingCanvas>( + 0, 0, 400, 400, [&receiverBackground, &child](RenderProperties& properties, RecordingCanvas& canvas) { - canvas.drawRenderNode(receiverBackground.get()); - canvas.drawRenderNode(child.get()); - }); + canvas.drawRenderNode(receiverBackground.get()); + canvas.drawRenderNode(child.get()); + }); - FrameBuilder frameBuilder(SkRect::MakeWH(400, 400), 400, 400, - sLightGeometry, Caches::getInstance()); + FrameBuilder frameBuilder(SkRect::MakeWH(400, 400), 400, 400, sLightGeometry, + Caches::getInstance()); frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent)); ProjectionChildScrollTestRenderer renderer; @@ -1859,14 +1857,14 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionChildScroll) { // creates a 100x100 shadow casting node with provided translationZ static sp<RenderNode> createWhiteRectShadowCaster(float translationZ) { - return TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100, - [translationZ](RenderProperties& properties, RecordingCanvas& canvas) { - properties.setTranslationZ(translationZ); - properties.mutableOutline().setRoundRect(0, 0, 100, 100, 0.0f, 1.0f); - SkPaint paint; - paint.setColor(SK_ColorWHITE); - canvas.drawRect(0, 0, 100, 100, paint); - }); + return TestUtils::createNode<RecordingCanvas>( + 0, 0, 100, 100, [translationZ](RenderProperties& properties, RecordingCanvas& canvas) { + properties.setTranslationZ(translationZ); + properties.mutableOutline().setRoundRect(0, 0, 100, 100, 0.0f, 1.0f); + SkPaint paint; + paint.setColor(SK_ColorWHITE); + canvas.drawRect(0, 0, 100, 100, paint); + }); } RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, shadow) { @@ -1887,14 +1885,14 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, shadow) { } }; - auto parent = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200, - [](RenderProperties& props, RecordingCanvas& canvas) { - canvas.insertReorderBarrier(true); - canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get()); - }); + auto parent = TestUtils::createNode<RecordingCanvas>( + 0, 0, 200, 200, [](RenderProperties& props, RecordingCanvas& canvas) { + canvas.insertReorderBarrier(true); + canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get()); + }); - FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, - sLightGeometry, Caches::getInstance()); + FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, sLightGeometry, + Caches::getInstance()); frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent)); ShadowTestRenderer renderer; @@ -1917,9 +1915,7 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, shadowSaveLayer) { void onRectOp(const RectOp& op, const BakedOpState& state) override { EXPECT_EQ(2, mIndex++); } - void endLayer() override { - EXPECT_EQ(3, mIndex++); - } + void endLayer() override { EXPECT_EQ(3, mIndex++); } void onLayerOp(const LayerOp& op, const BakedOpState& state) override { EXPECT_EQ(4, mIndex++); } @@ -1928,19 +1924,20 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, shadowSaveLayer) { } }; - auto parent = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200, - [](RenderProperties& props, RecordingCanvas& canvas) { - // save/restore outside of reorderBarrier, so they don't get moved out of place - canvas.translate(20, 10); - int count = canvas.saveLayerAlpha(30, 50, 130, 150, 128, SaveFlags::ClipToLayer); - canvas.insertReorderBarrier(true); - canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get()); - canvas.insertReorderBarrier(false); - canvas.restoreToCount(count); - }); + auto parent = TestUtils::createNode<RecordingCanvas>( + 0, 0, 200, 200, [](RenderProperties& props, RecordingCanvas& canvas) { + // save/restore outside of reorderBarrier, so they don't get moved out of place + canvas.translate(20, 10); + int count = canvas.saveLayerAlpha(30, 50, 130, 150, 128, SaveFlags::ClipToLayer); + canvas.insertReorderBarrier(true); + canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get()); + canvas.insertReorderBarrier(false); + canvas.restoreToCount(count); + }); FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, - (FrameBuilder::LightGeometry) {{ 100, 100, 100 }, 50}, Caches::getInstance()); + (FrameBuilder::LightGeometry){{100, 100, 100}, 50}, + Caches::getInstance()); frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent)); ShadowSaveLayerTestRenderer renderer; @@ -1963,38 +1960,37 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, shadowHwLayer) { void onRectOp(const RectOp& op, const BakedOpState& state) override { EXPECT_EQ(2, mIndex++); } - void endLayer() override { - EXPECT_EQ(3, mIndex++); - } + void endLayer() override { EXPECT_EQ(3, mIndex++); } void onLayerOp(const LayerOp& op, const BakedOpState& state) override { EXPECT_EQ(4, mIndex++); } }; - auto parent = TestUtils::createNode<RecordingCanvas>(50, 60, 150, 160, - [](RenderProperties& props, RecordingCanvas& canvas) { - props.mutateLayerProperties().setType(LayerType::RenderLayer); - canvas.insertReorderBarrier(true); - canvas.save(SaveFlags::MatrixClip); - canvas.translate(20, 10); - canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get()); - canvas.restore(); - }); + auto parent = TestUtils::createNode<RecordingCanvas>( + 50, 60, 150, 160, [](RenderProperties& props, RecordingCanvas& canvas) { + props.mutateLayerProperties().setType(LayerType::RenderLayer); + canvas.insertReorderBarrier(true); + canvas.save(SaveFlags::MatrixClip); + canvas.translate(20, 10); + canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get()); + canvas.restore(); + }); OffscreenBuffer** layerHandle = parent->getLayerHandle(); // create RenderNode's layer here in same way prepareTree would, setting windowTransform OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 100, 100); Matrix4 windowTransform; - windowTransform.loadTranslate(50, 60, 0); // total transform of layer's origin + windowTransform.loadTranslate(50, 60, 0); // total transform of layer's origin layer.setWindowTransform(windowTransform); *layerHandle = &layer; auto syncedNode = TestUtils::getSyncedNode(parent); - LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid + LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid layerUpdateQueue.enqueueLayerWithDamage(parent.get(), Rect(100, 100)); FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, - (FrameBuilder::LightGeometry) {{ 100, 100, 100 }, 30}, Caches::getInstance()); + (FrameBuilder::LightGeometry){{100, 100, 100}, 30}, + Caches::getInstance()); frameBuilder.deferLayers(layerUpdateQueue); frameBuilder.deferRenderNode(*syncedNode); @@ -2018,14 +2014,15 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, shadowLayering) { EXPECT_TRUE(index == 2 || index == 3); } }; - auto parent = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200, - [](RenderProperties& props, RecordingCanvas& canvas) { - canvas.insertReorderBarrier(true); - canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get()); - canvas.drawRenderNode(createWhiteRectShadowCaster(5.0001f).get()); - }); + auto parent = TestUtils::createNode<RecordingCanvas>( + 0, 0, 200, 200, [](RenderProperties& props, RecordingCanvas& canvas) { + canvas.insertReorderBarrier(true); + canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get()); + canvas.drawRenderNode(createWhiteRectShadowCaster(5.0001f).get()); + }); FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, - (FrameBuilder::LightGeometry) {{ 100, 100, 100 }, 50}, Caches::getInstance()); + (FrameBuilder::LightGeometry){{100, 100, 100}, 50}, + Caches::getInstance()); frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent)); ShadowLayeringTestRenderer renderer; @@ -2045,17 +2042,18 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, shadowClipping) { EXPECT_EQ(1, mIndex++); } }; - auto parent = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100, - [](RenderProperties& props, RecordingCanvas& canvas) { - // Apply a clip before the reorder barrier/shadow casting child is drawn. - // This clip must be applied to the shadow cast by the child. - canvas.clipRect(25, 25, 75, 75, SkClipOp::kIntersect); - canvas.insertReorderBarrier(true); - canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get()); - }); + auto parent = TestUtils::createNode<RecordingCanvas>( + 0, 0, 100, 100, [](RenderProperties& props, RecordingCanvas& canvas) { + // Apply a clip before the reorder barrier/shadow casting child is drawn. + // This clip must be applied to the shadow cast by the child. + canvas.clipRect(25, 25, 75, 75, SkClipOp::kIntersect); + canvas.insertReorderBarrier(true); + canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get()); + }); FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, - (FrameBuilder::LightGeometry) {{ 100, 100, 100 }, 50}, Caches::getInstance()); + (FrameBuilder::LightGeometry){{100, 100, 100}, 50}, + Caches::getInstance()); frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent)); ShadowClippingTestRenderer renderer; @@ -2063,11 +2061,13 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, shadowClipping) { EXPECT_EQ(2, renderer.getIndex()); } -static void testProperty(std::function<void(RenderProperties&)> propSetupCallback, +static void testProperty( + std::function<void(RenderProperties&)> propSetupCallback, std::function<void(const RectOp&, const BakedOpState&)> opValidateCallback) { class PropertyTestRenderer : public TestRendererBase { public: - explicit PropertyTestRenderer(std::function<void(const RectOp&, const BakedOpState&)> callback) + explicit PropertyTestRenderer( + std::function<void(const RectOp&, const BakedOpState&)> callback) : mCallback(callback) {} void onRectOp(const RectOp& op, const BakedOpState& state) override { EXPECT_EQ(mIndex++, 0); @@ -2076,16 +2076,16 @@ static void testProperty(std::function<void(RenderProperties&)> propSetupCallbac std::function<void(const RectOp&, const BakedOpState&)> mCallback; }; - auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100, - [propSetupCallback](RenderProperties& props, RecordingCanvas& canvas) { - propSetupCallback(props); - SkPaint paint; - paint.setColor(SK_ColorWHITE); - canvas.drawRect(0, 0, 100, 100, paint); - }); + auto node = TestUtils::createNode<RecordingCanvas>( + 0, 0, 100, 100, [propSetupCallback](RenderProperties& props, RecordingCanvas& canvas) { + propSetupCallback(props); + SkPaint paint; + paint.setColor(SK_ColorWHITE); + canvas.drawRect(0, 0, 100, 100, paint); + }); - FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 200, 200, - sLightGeometry, Caches::getInstance()); + FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 200, 200, sLightGeometry, + Caches::getInstance()); frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); PropertyTestRenderer renderer(opValidateCallback); @@ -2094,78 +2094,89 @@ static void testProperty(std::function<void(RenderProperties&)> propSetupCallbac } RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, renderPropOverlappingRenderingAlpha) { - testProperty([](RenderProperties& properties) { - properties.setAlpha(0.5f); - properties.setHasOverlappingRendering(false); - }, [](const RectOp& op, const BakedOpState& state) { - EXPECT_EQ(0.5f, state.alpha) << "Alpha should be applied directly to op"; - }); + testProperty( + [](RenderProperties& properties) { + properties.setAlpha(0.5f); + properties.setHasOverlappingRendering(false); + }, + [](const RectOp& op, const BakedOpState& state) { + EXPECT_EQ(0.5f, state.alpha) << "Alpha should be applied directly to op"; + }); } RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, renderPropClipping) { - testProperty([](RenderProperties& properties) { - properties.setClipToBounds(true); - properties.setClipBounds(Rect(10, 20, 300, 400)); - }, [](const RectOp& op, const BakedOpState& state) { - EXPECT_EQ(Rect(10, 20, 100, 100), state.computedState.clippedBounds) - << "Clip rect should be intersection of node bounds and clip bounds"; - }); + testProperty( + [](RenderProperties& properties) { + properties.setClipToBounds(true); + properties.setClipBounds(Rect(10, 20, 300, 400)); + }, + [](const RectOp& op, const BakedOpState& state) { + EXPECT_EQ(Rect(10, 20, 100, 100), state.computedState.clippedBounds) + << "Clip rect should be intersection of node bounds and clip bounds"; + }); } RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, renderPropRevealClip) { - testProperty([](RenderProperties& properties) { - properties.mutableRevealClip().set(true, 50, 50, 25); - }, [](const RectOp& op, const BakedOpState& state) { - ASSERT_NE(nullptr, state.roundRectClipState); - EXPECT_TRUE(state.roundRectClipState->highPriority); - EXPECT_EQ(25, state.roundRectClipState->radius); - EXPECT_EQ(Rect(50, 50, 50, 50), state.roundRectClipState->innerRect); - }); + testProperty( + [](RenderProperties& properties) { + properties.mutableRevealClip().set(true, 50, 50, 25); + }, + [](const RectOp& op, const BakedOpState& state) { + ASSERT_NE(nullptr, state.roundRectClipState); + EXPECT_TRUE(state.roundRectClipState->highPriority); + EXPECT_EQ(25, state.roundRectClipState->radius); + EXPECT_EQ(Rect(50, 50, 50, 50), state.roundRectClipState->innerRect); + }); } RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, renderPropOutlineClip) { - testProperty([](RenderProperties& properties) { - properties.mutableOutline().setShouldClip(true); - properties.mutableOutline().setRoundRect(10, 20, 30, 40, 5.0f, 0.5f); - }, [](const RectOp& op, const BakedOpState& state) { - ASSERT_NE(nullptr, state.roundRectClipState); - EXPECT_FALSE(state.roundRectClipState->highPriority); - EXPECT_EQ(5, state.roundRectClipState->radius); - EXPECT_EQ(Rect(15, 25, 25, 35), state.roundRectClipState->innerRect); - }); + testProperty( + [](RenderProperties& properties) { + properties.mutableOutline().setShouldClip(true); + properties.mutableOutline().setRoundRect(10, 20, 30, 40, 5.0f, 0.5f); + }, + [](const RectOp& op, const BakedOpState& state) { + ASSERT_NE(nullptr, state.roundRectClipState); + EXPECT_FALSE(state.roundRectClipState->highPriority); + EXPECT_EQ(5, state.roundRectClipState->radius); + EXPECT_EQ(Rect(15, 25, 25, 35), state.roundRectClipState->innerRect); + }); } RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, renderPropTransform) { - testProperty([](RenderProperties& properties) { - properties.setLeftTopRightBottom(10, 10, 110, 110); - - SkMatrix staticMatrix = SkMatrix::MakeScale(1.2f, 1.2f); - properties.setStaticMatrix(&staticMatrix); - - // ignored, since static overrides animation - SkMatrix animationMatrix = SkMatrix::MakeTrans(15, 15); - properties.setAnimationMatrix(&animationMatrix); - - properties.setTranslationX(10); - properties.setTranslationY(20); - properties.setScaleX(0.5f); - properties.setScaleY(0.7f); - }, [](const RectOp& op, const BakedOpState& state) { - Matrix4 matrix; - matrix.loadTranslate(10, 10, 0); // left, top - matrix.scale(1.2f, 1.2f, 1); // static matrix - // ignore animation matrix, since static overrides it - - // translation xy - matrix.translate(10, 20); - - // scale xy (from default pivot - center) - matrix.translate(50, 50); - matrix.scale(0.5f, 0.7f, 1); - matrix.translate(-50, -50); - EXPECT_MATRIX_APPROX_EQ(matrix, state.computedState.transform) - << "Op draw matrix must match expected combination of transformation properties"; - }); + testProperty( + [](RenderProperties& properties) { + properties.setLeftTopRightBottom(10, 10, 110, 110); + + SkMatrix staticMatrix = SkMatrix::MakeScale(1.2f, 1.2f); + properties.setStaticMatrix(&staticMatrix); + + // ignored, since static overrides animation + SkMatrix animationMatrix = SkMatrix::MakeTrans(15, 15); + properties.setAnimationMatrix(&animationMatrix); + + properties.setTranslationX(10); + properties.setTranslationY(20); + properties.setScaleX(0.5f); + properties.setScaleY(0.7f); + }, + [](const RectOp& op, const BakedOpState& state) { + Matrix4 matrix; + matrix.loadTranslate(10, 10, 0); // left, top + matrix.scale(1.2f, 1.2f, 1); // static matrix + // ignore animation matrix, since static overrides it + + // translation xy + matrix.translate(10, 20); + + // scale xy (from default pivot - center) + matrix.translate(50, 50); + matrix.scale(0.5f, 0.7f, 1); + matrix.translate(-50, -50); + EXPECT_MATRIX_APPROX_EQ(matrix, state.computedState.transform) + << "Op draw matrix must match expected combination of transformation " + "properties"; + }); } struct SaveLayerAlphaData { @@ -2186,11 +2197,10 @@ struct SaveLayerAlphaData { * (for efficiency, and to fit in layer size constraints) based on parent clip. */ void testSaveLayerAlphaClip(SaveLayerAlphaData* outObservedData, - std::function<void(RenderProperties&)> propSetupCallback) { + std::function<void(RenderProperties&)> propSetupCallback) { class SaveLayerAlphaClipTestRenderer : public TestRendererBase { public: - explicit SaveLayerAlphaClipTestRenderer(SaveLayerAlphaData* outData) - : mOutData(outData) {} + explicit SaveLayerAlphaClipTestRenderer(SaveLayerAlphaData* outData) : mOutData(outData) {} OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override { EXPECT_EQ(0, mIndex++); @@ -2204,9 +2214,7 @@ void testSaveLayerAlphaClip(SaveLayerAlphaData* outObservedData, mOutData->rectClippedBounds = state.computedState.clippedBounds; mOutData->rectMatrix = state.computedState.transform; } - void endLayer() override { - EXPECT_EQ(2, mIndex++); - } + void endLayer() override { EXPECT_EQ(2, mIndex++); } void onLayerOp(const LayerOp& op, const BakedOpState& state) override { EXPECT_EQ(3, mIndex++); mOutData->drawLayerMatrix = state.computedState.transform; @@ -2214,27 +2222,29 @@ void testSaveLayerAlphaClip(SaveLayerAlphaData* outObservedData, void recycleTemporaryLayer(OffscreenBuffer* offscreenBuffer) override { EXPECT_EQ(4, mIndex++); } + private: SaveLayerAlphaData* mOutData; }; ASSERT_GT(10000, DeviceInfo::get()->maxTextureSize()) << "Node must be bigger than max texture size to exercise saveLayer codepath"; - auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 10000, 10000, + auto node = TestUtils::createNode<RecordingCanvas>( + 0, 0, 10000, 10000, [&propSetupCallback](RenderProperties& properties, RecordingCanvas& canvas) { - properties.setHasOverlappingRendering(true); - properties.setAlpha(0.5f); // force saveLayer, since too big for HW layer - // apply other properties - propSetupCallback(properties); - - SkPaint paint; - paint.setColor(SK_ColorWHITE); - canvas.drawRect(0, 0, 10000, 10000, paint); - }); - auto syncedNode = TestUtils::getSyncedNode(node); // sync before querying height - - FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, - sLightGeometry, Caches::getInstance()); + properties.setHasOverlappingRendering(true); + properties.setAlpha(0.5f); // force saveLayer, since too big for HW layer + // apply other properties + propSetupCallback(properties); + + SkPaint paint; + paint.setColor(SK_ColorWHITE); + canvas.drawRect(0, 0, 10000, 10000, paint); + }); + auto syncedNode = TestUtils::getSyncedNode(node); // sync before querying height + + FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, sLightGeometry, + Caches::getInstance()); frameBuilder.deferRenderNode(*syncedNode); SaveLayerAlphaClipTestRenderer renderer(outObservedData); @@ -2247,8 +2257,8 @@ void testSaveLayerAlphaClip(SaveLayerAlphaData* outObservedData, RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, renderPropSaveLayerAlphaClipBig) { SaveLayerAlphaData observedData; testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) { - properties.setTranslationX(10); // offset rendering content - properties.setTranslationY(-2000); // offset rendering content + properties.setTranslationX(10); // offset rendering content + properties.setTranslationY(-2000); // offset rendering content }); EXPECT_EQ(190u, observedData.layerWidth); EXPECT_EQ(200u, observedData.layerHeight); @@ -2260,7 +2270,7 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, renderPropSaveLayerAlphaClipBig) << "expect content to be translated as part of being clipped"; expected.loadTranslate(10, 0, 0); EXPECT_MATRIX_APPROX_EQ(expected, observedData.drawLayerMatrix) - << "expect drawLayer to be translated as part of being clipped"; + << "expect drawLayer to be translated as part of being clipped"; } RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, renderPropSaveLayerAlphaRotate) { @@ -2306,14 +2316,14 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, clip_replace) { << "Expect resolved clip to be intersection of viewport clip and clip op"; } }; - auto node = TestUtils::createNode<RecordingCanvas>(20, 20, 30, 30, - [](RenderProperties& props, RecordingCanvas& canvas) { - canvas.clipRect(0, -20, 10, 30, SkClipOp::kReplace_deprecated); - canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver); - }); - - FrameBuilder frameBuilder(SkRect::MakeLTRB(10, 10, 40, 40), 50, 50, - sLightGeometry, Caches::getInstance()); + auto node = TestUtils::createNode<RecordingCanvas>( + 20, 20, 30, 30, [](RenderProperties& props, RecordingCanvas& canvas) { + canvas.clipRect(0, -20, 10, 30, SkClipOp::kReplace_deprecated); + canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver); + }); + + FrameBuilder frameBuilder(SkRect::MakeLTRB(10, 10, 40, 40), 50, 50, sLightGeometry, + Caches::getInstance()); frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); ClipReplaceTestRenderer renderer; @@ -2329,21 +2339,22 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderProjectedInMidd | R */ - auto nodeA = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100, - [](RenderProperties& props, RecordingCanvas& canvas) { - drawOrderedNode(&canvas, 0, [](RenderProperties& props, RecordingCanvas& canvas) { - props.setProjectionReceiver(true); - } ); //nodeB - drawOrderedNode(&canvas, 2, [](RenderProperties& props, RecordingCanvas& canvas) { - drawOrderedNode(&canvas, 1, [](RenderProperties& props, RecordingCanvas& canvas) { - props.setProjectBackwards(true); - props.setClipToBounds(false); - } ); //nodeR - } ); //nodeC - }); //nodeA - - FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, - sLightGeometry, Caches::getInstance()); + auto nodeA = TestUtils::createNode<RecordingCanvas>( + 0, 0, 100, 100, [](RenderProperties& props, RecordingCanvas& canvas) { + drawOrderedNode(&canvas, 0, [](RenderProperties& props, RecordingCanvas& canvas) { + props.setProjectionReceiver(true); + }); // nodeB + drawOrderedNode(&canvas, 2, [](RenderProperties& props, RecordingCanvas& canvas) { + drawOrderedNode(&canvas, 1, + [](RenderProperties& props, RecordingCanvas& canvas) { + props.setProjectBackwards(true); + props.setClipToBounds(false); + }); // nodeR + }); // nodeC + }); // nodeA + + FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, sLightGeometry, + Caches::getInstance()); frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA)); ZReorderTestRenderer renderer; @@ -2360,22 +2371,24 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderProjectLast) { | R */ - auto nodeA = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100, - [](RenderProperties& props, RecordingCanvas& canvas) { - drawOrderedNode(&canvas, 0, nullptr); //nodeB - drawOrderedNode(&canvas, 1, [](RenderProperties& props, RecordingCanvas& canvas) { - drawOrderedNode(&canvas, 3, [](RenderProperties& props, RecordingCanvas& canvas) { //drawn as 2 - props.setProjectBackwards(true); - props.setClipToBounds(false); - } ); //nodeR - } ); //nodeC - drawOrderedNode(&canvas, 2, [](RenderProperties& props, RecordingCanvas& canvas) { //drawn as 3 - props.setProjectionReceiver(true); - } ); //nodeE - }); //nodeA - - FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, - sLightGeometry, Caches::getInstance()); + auto nodeA = TestUtils::createNode<RecordingCanvas>( + 0, 0, 100, 100, [](RenderProperties& props, RecordingCanvas& canvas) { + drawOrderedNode(&canvas, 0, nullptr); // nodeB + drawOrderedNode(&canvas, 1, [](RenderProperties& props, RecordingCanvas& canvas) { + drawOrderedNode(&canvas, 3, [](RenderProperties& props, + RecordingCanvas& canvas) { // drawn as 2 + props.setProjectBackwards(true); + props.setClipToBounds(false); + }); // nodeR + }); // nodeC + drawOrderedNode(&canvas, 2, [](RenderProperties& props, + RecordingCanvas& canvas) { // drawn as 3 + props.setProjectionReceiver(true); + }); // nodeE + }); // nodeA + + FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, sLightGeometry, + Caches::getInstance()); frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA)); ZReorderTestRenderer renderer; @@ -2391,20 +2404,21 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderNoReceivable) { | R */ - auto nodeA = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100, - [](RenderProperties& props, RecordingCanvas& canvas) { - drawOrderedNode(&canvas, 0, nullptr); //nodeB - drawOrderedNode(&canvas, 1, [](RenderProperties& props, RecordingCanvas& canvas) { - drawOrderedNode(&canvas, 255, [](RenderProperties& props, RecordingCanvas& canvas) { - //not having a projection receiver is an undefined behavior - props.setProjectBackwards(true); - props.setClipToBounds(false); - } ); //nodeR - } ); //nodeC - }); //nodeA - - FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, - sLightGeometry, Caches::getInstance()); + auto nodeA = TestUtils::createNode<RecordingCanvas>( + 0, 0, 100, 100, [](RenderProperties& props, RecordingCanvas& canvas) { + drawOrderedNode(&canvas, 0, nullptr); // nodeB + drawOrderedNode(&canvas, 1, [](RenderProperties& props, RecordingCanvas& canvas) { + drawOrderedNode(&canvas, 255, + [](RenderProperties& props, RecordingCanvas& canvas) { + // not having a projection receiver is an undefined behavior + props.setProjectBackwards(true); + props.setClipToBounds(false); + }); // nodeR + }); // nodeC + }); // nodeA + + FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, sLightGeometry, + Caches::getInstance()); frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA)); ZReorderTestRenderer renderer; @@ -2420,20 +2434,21 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderParentReceivabl | R */ - auto nodeA = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100, - [](RenderProperties& props, RecordingCanvas& canvas) { - drawOrderedNode(&canvas, 0, nullptr); //nodeB - drawOrderedNode(&canvas, 1, [](RenderProperties& props, RecordingCanvas& canvas) { - props.setProjectionReceiver(true); - drawOrderedNode(&canvas, 2, [](RenderProperties& props, RecordingCanvas& canvas) { - props.setProjectBackwards(true); - props.setClipToBounds(false); - } ); //nodeR - } ); //nodeC - }); //nodeA - - FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, - sLightGeometry, Caches::getInstance()); + auto nodeA = TestUtils::createNode<RecordingCanvas>( + 0, 0, 100, 100, [](RenderProperties& props, RecordingCanvas& canvas) { + drawOrderedNode(&canvas, 0, nullptr); // nodeB + drawOrderedNode(&canvas, 1, [](RenderProperties& props, RecordingCanvas& canvas) { + props.setProjectionReceiver(true); + drawOrderedNode(&canvas, 2, + [](RenderProperties& props, RecordingCanvas& canvas) { + props.setProjectBackwards(true); + props.setClipToBounds(false); + }); // nodeR + }); // nodeC + }); // nodeA + + FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, sLightGeometry, + Caches::getInstance()); frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA)); ZReorderTestRenderer renderer; @@ -2442,21 +2457,23 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderParentReceivabl } RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderSameNodeReceivable) { - auto nodeA = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100, - [](RenderProperties& props, RecordingCanvas& canvas) { - drawOrderedNode(&canvas, 0, nullptr); //nodeB - drawOrderedNode(&canvas, 1, [](RenderProperties& props, RecordingCanvas& canvas) { - drawOrderedNode(&canvas, 255, [](RenderProperties& props, RecordingCanvas& canvas) { - //having a node that is projected on itself is an undefined/unexpected behavior - props.setProjectionReceiver(true); - props.setProjectBackwards(true); - props.setClipToBounds(false); - } ); //nodeR - } ); //nodeC - }); //nodeA - - FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, - sLightGeometry, Caches::getInstance()); + auto nodeA = TestUtils::createNode<RecordingCanvas>( + 0, 0, 100, 100, [](RenderProperties& props, RecordingCanvas& canvas) { + drawOrderedNode(&canvas, 0, nullptr); // nodeB + drawOrderedNode(&canvas, 1, [](RenderProperties& props, RecordingCanvas& canvas) { + drawOrderedNode(&canvas, 255, + [](RenderProperties& props, RecordingCanvas& canvas) { + // having a node that is projected on itself is an + // undefined/unexpected behavior + props.setProjectionReceiver(true); + props.setProjectBackwards(true); + props.setClipToBounds(false); + }); // nodeR + }); // nodeC + }); // nodeA + + FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, sLightGeometry, + Caches::getInstance()); frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA)); ZReorderTestRenderer renderer; @@ -2465,9 +2482,9 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderSameNodeReceiva } RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderProjectedSibling) { - //TODO: this test together with the next "projectionReorderProjectedSibling2" likely expose a - //bug in HWUI. First test draws R, while the second test does not draw R for a nearly identical - //tree setup. The correct behaviour is to not draw R, because the receiver cannot be a sibling + // TODO: this test together with the next "projectionReorderProjectedSibling2" likely expose a + // bug in HWUI. First test draws R, while the second test does not draw R for a nearly identical + // tree setup. The correct behaviour is to not draw R, because the receiver cannot be a sibling /* R is backward projected on B. R is not expected to be drawn (see Sibling2 outcome below), but for some reason it is drawn. A @@ -2475,21 +2492,21 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderProjectedSiblin / | \ B C R */ - auto nodeA = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100, - [](RenderProperties& props, RecordingCanvas& canvas) { - drawOrderedNode(&canvas, 0, [](RenderProperties& props, RecordingCanvas& canvas) { - props.setProjectionReceiver(true); - } ); //nodeB - drawOrderedNode(&canvas, 2, [](RenderProperties& props, RecordingCanvas& canvas) { - } ); //nodeC - drawOrderedNode(&canvas, 1, [](RenderProperties& props, RecordingCanvas& canvas) { - props.setProjectBackwards(true); - props.setClipToBounds(false); - } ); //nodeR - }); //nodeA + auto nodeA = TestUtils::createNode<RecordingCanvas>( + 0, 0, 100, 100, [](RenderProperties& props, RecordingCanvas& canvas) { + drawOrderedNode(&canvas, 0, [](RenderProperties& props, RecordingCanvas& canvas) { + props.setProjectionReceiver(true); + }); // nodeB + drawOrderedNode(&canvas, 2, + [](RenderProperties& props, RecordingCanvas& canvas) {}); // nodeC + drawOrderedNode(&canvas, 1, [](RenderProperties& props, RecordingCanvas& canvas) { + props.setProjectBackwards(true); + props.setClipToBounds(false); + }); // nodeR + }); // nodeA - FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, - sLightGeometry, Caches::getInstance()); + FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, sLightGeometry, + Caches::getInstance()); frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA)); ZReorderTestRenderer renderer; @@ -2506,23 +2523,27 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderProjectedSiblin / | \ B C R */ - auto nodeA = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100, - [](RenderProperties& props, RecordingCanvas& canvas) { - drawOrderedNode(&canvas, 0, [](RenderProperties& props, RecordingCanvas& canvas) { //G - drawOrderedNode(&canvas, 1, [](RenderProperties& props, RecordingCanvas& canvas) { //B - props.setProjectionReceiver(true); - } ); //nodeB - drawOrderedNode(&canvas, 2, [](RenderProperties& props, RecordingCanvas& canvas) { //C - } ); //nodeC - drawOrderedNode(&canvas, 255, [](RenderProperties& props, RecordingCanvas& canvas) { //R - props.setProjectBackwards(true); - props.setClipToBounds(false); - } ); //nodeR - } ); //nodeG - }); //nodeA - - FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, - sLightGeometry, Caches::getInstance()); + auto nodeA = TestUtils::createNode<RecordingCanvas>( + 0, 0, 100, 100, [](RenderProperties& props, RecordingCanvas& canvas) { + drawOrderedNode(&canvas, 0, [](RenderProperties& props, + RecordingCanvas& canvas) { // G + drawOrderedNode(&canvas, 1, + [](RenderProperties& props, RecordingCanvas& canvas) { // B + props.setProjectionReceiver(true); + }); // nodeB + drawOrderedNode(&canvas, 2, + [](RenderProperties& props, RecordingCanvas& canvas) { // C + }); // nodeC + drawOrderedNode(&canvas, 255, + [](RenderProperties& props, RecordingCanvas& canvas) { // R + props.setProjectBackwards(true); + props.setClipToBounds(false); + }); // nodeR + }); // nodeG + }); // nodeA + + FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, sLightGeometry, + Caches::getInstance()); frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA)); ZReorderTestRenderer renderer; @@ -2540,21 +2561,23 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderGrandparentRece | R */ - auto nodeA = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100, - [](RenderProperties& props, RecordingCanvas& canvas) { - drawOrderedNode(&canvas, 0, [](RenderProperties& props, RecordingCanvas& canvas) { - props.setProjectionReceiver(true); - drawOrderedNode(&canvas, 1, [](RenderProperties& props, RecordingCanvas& canvas) { - drawOrderedNode(&canvas, 2, [](RenderProperties& props, RecordingCanvas& canvas) { - props.setProjectBackwards(true); - props.setClipToBounds(false); - } ); //nodeR - } ); //nodeC - } ); //nodeB - }); //nodeA - - FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, - sLightGeometry, Caches::getInstance()); + auto nodeA = TestUtils::createNode<RecordingCanvas>( + 0, 0, 100, 100, [](RenderProperties& props, RecordingCanvas& canvas) { + drawOrderedNode(&canvas, 0, [](RenderProperties& props, RecordingCanvas& canvas) { + props.setProjectionReceiver(true); + drawOrderedNode(&canvas, 1, + [](RenderProperties& props, RecordingCanvas& canvas) { + drawOrderedNode(&canvas, 2, [](RenderProperties& props, + RecordingCanvas& canvas) { + props.setProjectBackwards(true); + props.setClipToBounds(false); + }); // nodeR + }); // nodeC + }); // nodeB + }); // nodeA + + FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, sLightGeometry, + Caches::getInstance()); frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA)); ZReorderTestRenderer renderer; @@ -2570,24 +2593,28 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderTwoReceivables) / \ G R */ - auto nodeA = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100, - [](RenderProperties& props, RecordingCanvas& canvas) { - drawOrderedNode(&canvas, 0, [](RenderProperties& props, RecordingCanvas& canvas) { //B - props.setProjectionReceiver(true); - } ); //nodeB - drawOrderedNode(&canvas, 2, [](RenderProperties& props, RecordingCanvas& canvas) { //C - drawOrderedNode(&canvas, 3, [](RenderProperties& props, RecordingCanvas& canvas) { //G - props.setProjectionReceiver(true); - } ); //nodeG - drawOrderedNode(&canvas, 1, [](RenderProperties& props, RecordingCanvas& canvas) { //R - props.setProjectBackwards(true); - props.setClipToBounds(false); - } ); //nodeR - } ); //nodeC - }); //nodeA - - FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, - sLightGeometry, Caches::getInstance()); + auto nodeA = TestUtils::createNode<RecordingCanvas>( + 0, 0, 100, 100, [](RenderProperties& props, RecordingCanvas& canvas) { + drawOrderedNode(&canvas, 0, + [](RenderProperties& props, RecordingCanvas& canvas) { // B + props.setProjectionReceiver(true); + }); // nodeB + drawOrderedNode(&canvas, 2, [](RenderProperties& props, + RecordingCanvas& canvas) { // C + drawOrderedNode(&canvas, 3, + [](RenderProperties& props, RecordingCanvas& canvas) { // G + props.setProjectionReceiver(true); + }); // nodeG + drawOrderedNode(&canvas, 1, + [](RenderProperties& props, RecordingCanvas& canvas) { // R + props.setProjectBackwards(true); + props.setClipToBounds(false); + }); // nodeR + }); // nodeC + }); // nodeA + + FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, sLightGeometry, + Caches::getInstance()); frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA)); ZReorderTestRenderer renderer; @@ -2603,24 +2630,28 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderTwoReceivablesL / \ G R */ - auto nodeA = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100, - [](RenderProperties& props, RecordingCanvas& canvas) { - drawOrderedNode(&canvas, 0, [](RenderProperties& props, RecordingCanvas& canvas) { //B - props.setProjectionReceiver(true); - } ); //nodeB - drawOrderedNode(&canvas, 2, [](RenderProperties& props, RecordingCanvas& canvas) { //C - drawOrderedNode(&canvas, 1, [](RenderProperties& props, RecordingCanvas& canvas) { //G - props.setProjectionReceiver(true); - props.setProjectBackwards(true); - props.setClipToBounds(false); - } ); //nodeG - drawOrderedNode(&canvas, 3, [](RenderProperties& props, RecordingCanvas& canvas) { //R - } ); //nodeR - } ); //nodeC - }); //nodeA - - FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, - sLightGeometry, Caches::getInstance()); + auto nodeA = TestUtils::createNode<RecordingCanvas>( + 0, 0, 100, 100, [](RenderProperties& props, RecordingCanvas& canvas) { + drawOrderedNode(&canvas, 0, + [](RenderProperties& props, RecordingCanvas& canvas) { // B + props.setProjectionReceiver(true); + }); // nodeB + drawOrderedNode(&canvas, 2, [](RenderProperties& props, + RecordingCanvas& canvas) { // C + drawOrderedNode(&canvas, 1, + [](RenderProperties& props, RecordingCanvas& canvas) { // G + props.setProjectionReceiver(true); + props.setProjectBackwards(true); + props.setClipToBounds(false); + }); // nodeG + drawOrderedNode(&canvas, 3, + [](RenderProperties& props, RecordingCanvas& canvas) { // R + }); // nodeR + }); // nodeC + }); // nodeA + + FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, sLightGeometry, + Caches::getInstance()); frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA)); ZReorderTestRenderer renderer; @@ -2638,26 +2669,31 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderTwoReceivablesD | R */ - auto nodeA = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100, - [](RenderProperties& props, RecordingCanvas& canvas) { - drawOrderedNode(&canvas, 0, [](RenderProperties& props, RecordingCanvas& canvas) { //B - props.setProjectionReceiver(true); - } ); //nodeB - drawOrderedNode(&canvas, 1, [](RenderProperties& props, RecordingCanvas& canvas) { //C - drawOrderedNode(&canvas, 2, [](RenderProperties& props, RecordingCanvas& canvas) { //G - props.setProjectionReceiver(true); - } ); //nodeG - drawOrderedNode(&canvas, 4, [](RenderProperties& props, RecordingCanvas& canvas) { //D - drawOrderedNode(&canvas, 3, [](RenderProperties& props, RecordingCanvas& canvas) { //R - props.setProjectBackwards(true); - props.setClipToBounds(false); - } ); //nodeR - } ); //nodeD - } ); //nodeC - }); //nodeA - - FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, - sLightGeometry, Caches::getInstance()); + auto nodeA = TestUtils::createNode<RecordingCanvas>( + 0, 0, 100, 100, [](RenderProperties& props, RecordingCanvas& canvas) { + drawOrderedNode(&canvas, 0, + [](RenderProperties& props, RecordingCanvas& canvas) { // B + props.setProjectionReceiver(true); + }); // nodeB + drawOrderedNode(&canvas, 1, [](RenderProperties& props, + RecordingCanvas& canvas) { // C + drawOrderedNode(&canvas, 2, + [](RenderProperties& props, RecordingCanvas& canvas) { // G + props.setProjectionReceiver(true); + }); // nodeG + drawOrderedNode( + &canvas, 4, [](RenderProperties& props, RecordingCanvas& canvas) { // D + drawOrderedNode(&canvas, 3, [](RenderProperties& props, + RecordingCanvas& canvas) { // R + props.setProjectBackwards(true); + props.setClipToBounds(false); + }); // nodeR + }); // nodeD + }); // nodeC + }); // nodeA + + FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, sLightGeometry, + Caches::getInstance()); frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA)); ZReorderTestRenderer renderer; @@ -2665,5 +2701,5 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderTwoReceivablesD EXPECT_EQ(5, renderer.getIndex()); } -} // namespace uirenderer -} // namespace android +} // namespace uirenderer +} // namespace android diff --git a/libs/hwui/tests/unit/GlopBuilderTests.cpp b/libs/hwui/tests/unit/GlopBuilderTests.cpp index caeb6bf0081b..c8bfc99fac92 100644 --- a/libs/hwui/tests/unit/GlopBuilderTests.cpp +++ b/libs/hwui/tests/unit/GlopBuilderTests.cpp @@ -28,8 +28,7 @@ using namespace android::uirenderer; static void expectFillEq(Glop::Fill& expectedFill, Glop::Fill& builtFill) { EXPECT_EQ(expectedFill.colorEnabled, builtFill.colorEnabled); - if (expectedFill.colorEnabled) - EXPECT_EQ(expectedFill.color, builtFill.color); + if (expectedFill.colorEnabled) EXPECT_EQ(expectedFill.color, builtFill.color); EXPECT_EQ(expectedFill.filterMode, builtFill.filterMode); if (expectedFill.filterMode == ProgramDescription::ColorFilterMode::Blend) { @@ -38,15 +37,15 @@ static void expectFillEq(Glop::Fill& expectedFill, Glop::Fill& builtFill) { Glop::Fill::Filter::Matrix& expectedMatrix = expectedFill.filter.matrix; Glop::Fill::Filter::Matrix& builtMatrix = expectedFill.filter.matrix; EXPECT_TRUE(std::memcmp(expectedMatrix.matrix, builtMatrix.matrix, - sizeof(Glop::Fill::Filter::Matrix::matrix))); + sizeof(Glop::Fill::Filter::Matrix::matrix))); EXPECT_TRUE(std::memcmp(expectedMatrix.vector, builtMatrix.vector, - sizeof(Glop::Fill::Filter::Matrix::vector))); + sizeof(Glop::Fill::Filter::Matrix::vector))); } EXPECT_EQ(expectedFill.skiaShaderData.skiaShaderType, builtFill.skiaShaderData.skiaShaderType); EXPECT_EQ(expectedFill.texture.clamp, builtFill.texture.clamp); EXPECT_EQ(expectedFill.texture.filter, builtFill.texture.filter); - EXPECT_TRUE((expectedFill.texture.texture && builtFill.texture.texture) - || (!expectedFill.texture.texture && !builtFill.texture.texture)); + EXPECT_TRUE((expectedFill.texture.texture && builtFill.texture.texture) || + (!expectedFill.texture.texture && !builtFill.texture.texture)); if (expectedFill.texture.texture) { EXPECT_EQ(expectedFill.texture.texture->target(), builtFill.texture.texture->target()); } @@ -97,22 +96,23 @@ static void expectGlopEq(Glop& expectedGlop, Glop& builtGlop) { static std::unique_ptr<Glop> blackUnitQuadGlop(RenderState& renderState) { std::unique_ptr<Glop> glop(new Glop()); - glop->blend = { GL_ZERO, GL_ZERO }; + glop->blend = {GL_ZERO, GL_ZERO}; glop->mesh.elementCount = 4; glop->mesh.primitiveMode = GL_TRIANGLE_STRIP; glop->mesh.indices.indices = nullptr; glop->mesh.indices.bufferObject = GL_ZERO; - glop->mesh.vertices = { - renderState.meshState().getUnitQuadVBO(), - VertexAttribFlags::None, - nullptr, nullptr, nullptr, - kTextureVertexStride }; + glop->mesh.vertices = {renderState.meshState().getUnitQuadVBO(), + VertexAttribFlags::None, + nullptr, + nullptr, + nullptr, + kTextureVertexStride}; glop->transform.modelView.loadIdentity(); glop->fill.colorEnabled = true; glop->fill.color.set(Color::Black); glop->fill.skiaShaderData.skiaShaderType = kNone_SkiaShaderType; glop->fill.filterMode = ProgramDescription::ColorFilterMode::None; - glop->fill.texture = { nullptr, GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr }; + glop->fill.texture = {nullptr, GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr}; return glop; } diff --git a/libs/hwui/tests/unit/GpuMemoryTrackerTests.cpp b/libs/hwui/tests/unit/GpuMemoryTrackerTests.cpp index 8cbd24edbde2..9bfb08292be2 100644 --- a/libs/hwui/tests/unit/GpuMemoryTrackerTests.cpp +++ b/libs/hwui/tests/unit/GpuMemoryTrackerTests.cpp @@ -14,9 +14,8 @@ * limitations under the License. */ - -#include <gtest/gtest.h> #include <GpuMemoryTracker.h> +#include <gtest/gtest.h> #include "renderthread/EglManager.h" #include "renderthread/RenderThread.h" @@ -32,9 +31,7 @@ class TestGPUObject : public GpuMemoryTracker { public: TestGPUObject() : GpuMemoryTracker(GpuObjectType::Texture) {} - void changeSize(int newSize) { - notifySizeChanged(newSize); - } + void changeSize(int newSize) { notifySizeChanged(newSize); } }; // Other tests may have created a renderthread and EGL context. @@ -42,9 +39,7 @@ public: // current thread can spoof being a GPU thread static void destroyEglContext() { if (TestUtils::isRenderThreadRunning()) { - TestUtils::runOnRenderThread([](RenderThread& thread) { - thread.eglManager().destroy(); - }); + TestUtils::runOnRenderThread([](RenderThread& thread) { thread.eglManager().destroy(); }); } } diff --git a/libs/hwui/tests/unit/GradientCacheTests.cpp b/libs/hwui/tests/unit/GradientCacheTests.cpp index a3b346f11a87..6710c71c386f 100644 --- a/libs/hwui/tests/unit/GradientCacheTests.cpp +++ b/libs/hwui/tests/unit/GradientCacheTests.cpp @@ -28,12 +28,12 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(GradientCache, addRemove) { GradientCache cache(extensions); ASSERT_LT(1000u, cache.getMaxSize()) << "Expect non-trivial size"; - SkColor colors[] = { 0xFF00FF00, 0xFFFF0000, 0xFF0000FF }; - float positions[] = { 1, 2, 3 }; + SkColor colors[] = {0xFF00FF00, 0xFFFF0000, 0xFF0000FF}; + float positions[] = {1, 2, 3}; Texture* texture = cache.get(colors, positions, 3); ASSERT_TRUE(texture); ASSERT_FALSE(texture->cleanup); - ASSERT_EQ((uint32_t) texture->objectSize(), cache.getSize()); + ASSERT_EQ((uint32_t)texture->objectSize(), cache.getSize()); ASSERT_TRUE(cache.getSize()); cache.clear(); ASSERT_EQ(cache.getSize(), 0u); diff --git a/libs/hwui/tests/unit/GraphicsStatsServiceTests.cpp b/libs/hwui/tests/unit/GraphicsStatsServiceTests.cpp index fda3a79a69da..30c3b4ea5ab3 100644 --- a/libs/hwui/tests/unit/GraphicsStatsServiceTests.cpp +++ b/libs/hwui/tests/unit/GraphicsStatsServiceTests.cpp @@ -20,10 +20,10 @@ #include <frameworks/base/core/proto/android/service/graphicsstats.pb.h> -#include <sys/types.h> -#include <sys/stat.h> #include <stdio.h> #include <stdlib.h> +#include <sys/stat.h> +#include <sys/types.h> #include <unistd.h> using namespace android; @@ -35,8 +35,8 @@ std::string findRootPath() { // < 1023 because we need room for the null terminator if (r <= 0 || r > 1023) { int err = errno; - fprintf(stderr, "Failed to read from /proc/self/exe; r=%zd, err=%d (%s)\n", - r, err, strerror(err)); + fprintf(stderr, "Failed to read from /proc/self/exe; r=%zd, err=%d (%s)\n", r, err, + strerror(err)); exit(EXIT_FAILURE); } while (--r > 0) { @@ -88,8 +88,8 @@ TEST(GraphicsStats, saveLoad) { EXPECT_EQ(20, loadedProto.summary().janky_frames()); EXPECT_EQ(100, loadedProto.summary().total_frames()); EXPECT_EQ(mockData.editFrameCounts().size() + mockData.editSlowFrameCounts().size(), - (size_t) loadedProto.histogram_size()); - for (size_t i = 0; i < (size_t) loadedProto.histogram_size(); i++) { + (size_t)loadedProto.histogram_size()); + for (size_t i = 0; i < (size_t)loadedProto.histogram_size(); i++) { int expectedCount, expectedBucket; if (i < mockData.editFrameCounts().size()) { expectedCount = ((i % 10) + 1) * 2; @@ -144,8 +144,8 @@ TEST(GraphicsStats, merge) { EXPECT_EQ(20 + 50, loadedProto.summary().janky_frames()); EXPECT_EQ(100 + 500, loadedProto.summary().total_frames()); EXPECT_EQ(mockData.editFrameCounts().size() + mockData.editSlowFrameCounts().size(), - (size_t) loadedProto.histogram_size()); - for (size_t i = 0; i < (size_t) loadedProto.histogram_size(); i++) { + (size_t)loadedProto.histogram_size()); + for (size_t i = 0; i < (size_t)loadedProto.histogram_size(); i++) { int expectedCount, expectedBucket; if (i < mockData.editFrameCounts().size()) { expectedCount = ((i % 10) + 1) * 2; diff --git a/libs/hwui/tests/unit/LayerUpdateQueueTests.cpp b/libs/hwui/tests/unit/LayerUpdateQueueTests.cpp index 91c7514e271e..217d63f9c2e1 100644 --- a/libs/hwui/tests/unit/LayerUpdateQueueTests.cpp +++ b/libs/hwui/tests/unit/LayerUpdateQueueTests.cpp @@ -51,9 +51,9 @@ TEST(LayerUpdateQueue, enqueueSimple) { EXPECT_EQ(a.get(), queue.entries()[0].renderNode.get()); EXPECT_EQ(Rect(25, 25, 75, 75), queue.entries()[0].damage); EXPECT_EQ(b.get(), queue.entries()[1].renderNode.get()); - EXPECT_EQ(Rect(100, 100, 200, 200), queue.entries()[1].damage); // clipped to bounds + EXPECT_EQ(Rect(100, 100, 200, 200), queue.entries()[1].damage); // clipped to bounds EXPECT_EQ(c.get(), queue.entries()[2].renderNode.get()); - EXPECT_EQ(Rect(0, 0, 1, 1), queue.entries()[2].damage); // rounded out + EXPECT_EQ(Rect(0, 0, 1, 1), queue.entries()[2].damage); // rounded out } TEST(LayerUpdateQueue, enqueueUnion) { @@ -81,6 +81,5 @@ TEST(LayerUpdateQueue, clear) { EXPECT_TRUE(queue.entries().empty()); } - }; }; diff --git a/libs/hwui/tests/unit/LeakCheckTests.cpp b/libs/hwui/tests/unit/LeakCheckTests.cpp index 19d7ef59e397..20ec0848212f 100644 --- a/libs/hwui/tests/unit/LeakCheckTests.cpp +++ b/libs/hwui/tests/unit/LeakCheckTests.cpp @@ -14,8 +14,8 @@ * limitations under the License. */ -#include "BakedOpRenderer.h" #include "BakedOpDispatcher.h" +#include "BakedOpRenderer.h" #include "FrameBuilder.h" #include "LayerUpdateQueue.h" #include "RecordingCanvas.h" @@ -26,12 +26,11 @@ using namespace android; using namespace android::uirenderer; -const FrameBuilder::LightGeometry sLightGeometery = { {100, 100, 100}, 50}; -const BakedOpRenderer::LightInfo sLightInfo = { 128, 128 }; +const FrameBuilder::LightGeometry sLightGeometery = {{100, 100, 100}, 50}; +const BakedOpRenderer::LightInfo sLightInfo = {128, 128}; RENDERTHREAD_OPENGL_PIPELINE_TEST(LeakCheck, saveLayer_overdrawRejection) { - auto node = TestUtils::createNode(0, 0, 100, 100, - [](RenderProperties& props, Canvas& canvas) { + auto node = TestUtils::createNode(0, 0, 100, 100, [](RenderProperties& props, Canvas& canvas) { canvas.saveLayerAlpha(0, 0, 100, 100, 128, SaveFlags::ClipToLayer); canvas.drawRect(0, 0, 100, 100, SkPaint()); canvas.restore(); @@ -42,16 +41,15 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(LeakCheck, saveLayer_overdrawRejection) { RenderState& renderState = renderThread.renderState(); Caches& caches = Caches::getInstance(); - FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, - sLightGeometery, Caches::getInstance()); + FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, sLightGeometery, + Caches::getInstance()); frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); BakedOpRenderer renderer(caches, renderState, true, false, sLightInfo); frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer); } RENDERTHREAD_OPENGL_PIPELINE_TEST(LeakCheck, saveLayerUnclipped_simple) { - auto node = TestUtils::createNode(0, 0, 200, 200, - [](RenderProperties& props, Canvas& canvas) { + auto node = TestUtils::createNode(0, 0, 200, 200, [](RenderProperties& props, Canvas& canvas) { canvas.saveLayerAlpha(10, 10, 190, 190, 128, (SaveFlags::Flags)(0)); canvas.drawRect(0, 0, 200, 200, SkPaint()); canvas.restore(); @@ -59,8 +57,8 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(LeakCheck, saveLayerUnclipped_simple) { RenderState& renderState = renderThread.renderState(); Caches& caches = Caches::getInstance(); - FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, - sLightGeometery, Caches::getInstance()); + FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, sLightGeometery, + Caches::getInstance()); frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); BakedOpRenderer renderer(caches, renderState, true, false, sLightInfo); frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer); diff --git a/libs/hwui/tests/unit/LinearAllocatorTests.cpp b/libs/hwui/tests/unit/LinearAllocatorTests.cpp index ffcbf128fc3a..1038da3c4cd0 100644 --- a/libs/hwui/tests/unit/LinearAllocatorTests.cpp +++ b/libs/hwui/tests/unit/LinearAllocatorTests.cpp @@ -43,7 +43,7 @@ TEST(LinearAllocator, create) { } TEST(LinearAllocator, dtor) { - int destroyed[10] = { 0 }; + int destroyed[10] = {0}; { LinearAllocator la; for (int i = 0; i < 5; i++) { @@ -111,7 +111,6 @@ TEST(LinearStdAllocator, simpleAllocate) { EXPECT_LT(lastLocation, &v[0]); EXPECT_GT(lastLocation + 20, &v[0]); - } TEST(LsaVector, dtorCheck) { @@ -126,7 +125,7 @@ TEST(LsaVector, dtorCheck) { vector.emplace_back(new TestUtils::SignalingDtor(&destroyed)); } EXPECT_EQ(0, destroyed); - EXPECT_EQ(size, (int) vector.size()); + EXPECT_EQ(size, (int)vector.size()); } EXPECT_EQ(size, destroyed); } diff --git a/libs/hwui/tests/unit/MatrixTests.cpp b/libs/hwui/tests/unit/MatrixTests.cpp index eddab878a49d..16a396e00790 100644 --- a/libs/hwui/tests/unit/MatrixTests.cpp +++ b/libs/hwui/tests/unit/MatrixTests.cpp @@ -32,8 +32,7 @@ TEST(Matrix, mapRect_emptyScaleSkew) { ASSERT_TRUE(empty.isEmpty()); scaleMatrix.mapRect(empty); EXPECT_EQ(Rect(170, 215, 250, 1015), empty); - EXPECT_FALSE(empty.isEmpty()) - << "Empty 'line' rect doesn't remain empty when skewed."; + EXPECT_FALSE(empty.isEmpty()) << "Empty 'line' rect doesn't remain empty when skewed."; } TEST(Matrix, mapRect_emptyRotate) { @@ -45,6 +44,5 @@ TEST(Matrix, mapRect_emptyRotate) { Rect lineRect(0, 100); ASSERT_TRUE(lineRect.isEmpty()); skewMatrix.mapRect(lineRect); - EXPECT_FALSE(lineRect.isEmpty()) - << "Empty 'line' rect doesn't remain empty when rotated."; + EXPECT_FALSE(lineRect.isEmpty()) << "Empty 'line' rect doesn't remain empty when rotated."; } diff --git a/libs/hwui/tests/unit/MeshStateTests.cpp b/libs/hwui/tests/unit/MeshStateTests.cpp index 511d6d25fbaf..1573fd30d5cb 100644 --- a/libs/hwui/tests/unit/MeshStateTests.cpp +++ b/libs/hwui/tests/unit/MeshStateTests.cpp @@ -16,8 +16,8 @@ #include <debug/MockGlesDriver.h> #include <debug/ScopedReplaceDriver.h> -#include <gtest/gtest.h> #include <gmock/gmock.h> +#include <gtest/gtest.h> #include <renderstate/MeshState.h> #include <tests/common/TestUtils.h> @@ -32,5 +32,6 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(MeshState, genOrUpdate) { EXPECT_CALL(mockGlDriver, glBufferData_(_, _, _, _)); GLuint buffer = 0; - renderThread.renderState().meshState().genOrUpdateMeshBuffer(&buffer, 10, nullptr, GL_DYNAMIC_DRAW); + renderThread.renderState().meshState().genOrUpdateMeshBuffer(&buffer, 10, nullptr, + GL_DYNAMIC_DRAW); } diff --git a/libs/hwui/tests/unit/OffscreenBufferPoolTests.cpp b/libs/hwui/tests/unit/OffscreenBufferPoolTests.cpp index 308fef303740..0d4736757629 100644 --- a/libs/hwui/tests/unit/OffscreenBufferPoolTests.cpp +++ b/libs/hwui/tests/unit/OffscreenBufferPoolTests.cpp @@ -14,8 +14,8 @@ * limitations under the License. */ -#include <gtest/gtest.h> #include <Rect.h> +#include <gtest/gtest.h> #include <renderstate/OffscreenBufferPool.h> #include <tests/common/TestUtils.h> @@ -56,12 +56,10 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(OffscreenBuffer, constructWideColorGamut) { RENDERTHREAD_OPENGL_PIPELINE_TEST(OffscreenBuffer, getTextureCoordinates) { OffscreenBuffer layerAligned(renderThread.renderState(), Caches::getInstance(), 256u, 256u); - EXPECT_EQ(Rect(0, 1, 1, 0), - layerAligned.getTextureCoordinates()); + EXPECT_EQ(Rect(0, 1, 1, 0), layerAligned.getTextureCoordinates()); OffscreenBuffer layerUnaligned(renderThread.renderState(), Caches::getInstance(), 200u, 225u); - EXPECT_EQ(Rect(0, 225.0f / 256.0f, 200.0f / 256.0f, 0), - layerUnaligned.getTextureCoordinates()); + EXPECT_EQ(Rect(0, 225.0f / 256.0f, 200.0f / 256.0f, 0), layerUnaligned.getTextureCoordinates()); } RENDERTHREAD_OPENGL_PIPELINE_TEST(OffscreenBuffer, dirty) { @@ -221,7 +219,7 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(OffscreenBufferPool, putAndDestroy) { auto hugeLayer = pool.get(renderThread.renderState(), pool.getMaxSize() / 64, 64); EXPECT_GT(hugeLayer->getSizeInBytes(), pool.getMaxSize()); pool.putOrDelete(hugeLayer); - EXPECT_EQ(0u, pool.getCount()); // failed to put (so was destroyed instead) + EXPECT_EQ(0u, pool.getCount()); // failed to put (so was destroyed instead) } RENDERTHREAD_OPENGL_PIPELINE_TEST(OffscreenBufferPool, clear) { @@ -244,4 +242,3 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(OffscreenBufferPool, clear) { EXPECT_EQ(0, GpuMemoryTracker::getInstanceCount(GpuObjectType::OffscreenBuffer)); } - diff --git a/libs/hwui/tests/unit/OpDumperTests.cpp b/libs/hwui/tests/unit/OpDumperTests.cpp index 01840d72b766..ef30e872a7bd 100644 --- a/libs/hwui/tests/unit/OpDumperTests.cpp +++ b/libs/hwui/tests/unit/OpDumperTests.cpp @@ -16,8 +16,8 @@ #include <gtest/gtest.h> -#include "tests/common/TestUtils.h" #include "OpDumper.h" +#include "tests/common/TestUtils.h" using namespace android; using namespace android::uirenderer; diff --git a/libs/hwui/tests/unit/PathInterpolatorTests.cpp b/libs/hwui/tests/unit/PathInterpolatorTests.cpp index d7cb23a4b793..d888012a80ce 100644 --- a/libs/hwui/tests/unit/PathInterpolatorTests.cpp +++ b/libs/hwui/tests/unit/PathInterpolatorTests.cpp @@ -37,55 +37,36 @@ const static TestData sTestDataSet[] = { {0.0f, 0.2f, 0.4f, 0.6f, 0.8f, 1.0f}, }, - { - { - 0.0f, 0.5f, 0.5178955f, 0.5341797f, 0.5489991f, 0.5625f, 0.5748291f, - 0.5861328f, 0.60625005f, 0.62402344f, 0.640625f, 0.675f, 0.6951172f, - 0.71875f, 0.7470703f, 0.78125f, 0.82246095f, 0.84606934f, 0.871875f, - 0.9000244f, 0.93066406f, 0.96394044f, 1.0f - }, - { - 0.0f, 0.0f, 0.0028686523f, 0.011230469f, 0.024719238f, 0.04296875f, - 0.06561279f, 0.092285156f, 0.15625f, 0.2319336f, 0.31640625f, 0.5f, - 0.5932617f, 0.68359375f, 0.7680664f, 0.84375f, 0.90771484f, 0.9343872f, - 0.95703125f, 0.97528076f, 0.98876953f, 0.99713135f, 1.0f - }, - { - 0.0f, 0.03375840187072754f, 0.13503384590148926f, 0.23630905151367188f, - 0.336834192276001f, 0.4508626461029053f, 0.564141035079956f, - 0.6781694889068604f, 0.7921979427337646f, 0.9054763317108154f, 1.0f - }, - { - 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0459827296435833f, - 0.5146934390068054f, 0.8607426285743713f, 0.9776809215545654f, 1.0f + {{0.0f, 0.5f, 0.5178955f, 0.5341797f, 0.5489991f, 0.5625f, + 0.5748291f, 0.5861328f, 0.60625005f, 0.62402344f, 0.640625f, 0.675f, + 0.6951172f, 0.71875f, 0.7470703f, 0.78125f, 0.82246095f, 0.84606934f, + 0.871875f, 0.9000244f, 0.93066406f, 0.96394044f, 1.0f}, + {0.0f, 0.0f, 0.0028686523f, 0.011230469f, 0.024719238f, 0.04296875f, + 0.06561279f, 0.092285156f, 0.15625f, 0.2319336f, 0.31640625f, 0.5f, + 0.5932617f, 0.68359375f, 0.7680664f, 0.84375f, 0.90771484f, 0.9343872f, + 0.95703125f, 0.97528076f, 0.98876953f, 0.99713135f, 1.0f}, + {0.0f, 0.03375840187072754f, 0.13503384590148926f, 0.23630905151367188f, + 0.336834192276001f, 0.4508626461029053f, 0.564141035079956f, 0.6781694889068604f, + 0.7921979427337646f, 0.9054763317108154f, 1.0f}, + {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0459827296435833f, 0.5146934390068054f, + 0.8607426285743713f, 0.9776809215545654f, 1.0f - } + } }, - { - { - 0.0f, 0.017895509f, 0.034179688f, 0.048999026f, 0.0625f, 0.0748291f, - 0.08613282f, 0.10625f, 0.12402344f, 0.140625f, 0.17500001f, 0.19511719f, - 0.21875f, 0.24707031f, 0.28125f, 0.32246095f, 0.34606934f, 0.371875f, - 0.4000244f, 0.43066406f, 0.46394044f, 0.5f, 1.0f - }, - { - 0.0f, 0.0028686523f, 0.011230469f, 0.024719238f, 0.04296875f, 0.06561279f, - 0.092285156f, 0.15625f, 0.2319336f, 0.31640625f, 0.5f, 0.5932617f, - 0.68359375f, 0.7680664f, 0.84375f, 0.90771484f, 0.9343872f, 0.95703125f, - 0.97528076f, 0.98876953f, 0.99713135f, 1.0f, 1.0f - }, - { - 0.0f, 0.102020263671875f, 0.20330810546875f, 0.3165740966796875f, - 0.43060302734375f, 0.5318756103515625f, 0.6331634521484375f, - 0.746429443359375f, 0.84771728515625f, 0.9617462158203125f, 1.0f - }, - { - 0.0f, 0.14280107617378235f, 0.6245699524879456f, 0.8985776901245117f, - 0.9887426495552063f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f - } - }, - + {{0.0f, 0.017895509f, 0.034179688f, 0.048999026f, 0.0625f, 0.0748291f, + 0.08613282f, 0.10625f, 0.12402344f, 0.140625f, 0.17500001f, 0.19511719f, + 0.21875f, 0.24707031f, 0.28125f, 0.32246095f, 0.34606934f, 0.371875f, + 0.4000244f, 0.43066406f, 0.46394044f, 0.5f, 1.0f}, + {0.0f, 0.0028686523f, 0.011230469f, 0.024719238f, 0.04296875f, 0.06561279f, + 0.092285156f, 0.15625f, 0.2319336f, 0.31640625f, 0.5f, 0.5932617f, + 0.68359375f, 0.7680664f, 0.84375f, 0.90771484f, 0.9343872f, 0.95703125f, + 0.97528076f, 0.98876953f, 0.99713135f, 1.0f, 1.0f}, + {0.0f, 0.102020263671875f, 0.20330810546875f, 0.3165740966796875f, 0.43060302734375f, + 0.5318756103515625f, 0.6331634521484375f, 0.746429443359375f, 0.84771728515625f, + 0.9617462158203125f, 1.0f}, + {0.0f, 0.14280107617378235f, 0.6245699524879456f, 0.8985776901245117f, 0.9887426495552063f, + 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f}}, }; @@ -98,13 +79,12 @@ static std::vector<float> getY(const TestData& data) { } TEST(Interpolator, pathInterpolation) { - for (const TestData& data: sTestDataSet) { + for (const TestData& data : sTestDataSet) { PathInterpolator interpolator(getX(data), getY(data)); for (size_t i = 0; i < data.inFraction.size(); i++) { EXPECT_FLOAT_EQ(data.outFraction[i], interpolator.interpolate(data.inFraction[i])); } } } - } } diff --git a/libs/hwui/tests/unit/RecordingCanvasTests.cpp b/libs/hwui/tests/unit/RecordingCanvasTests.cpp index d36bca031a87..8a9e34f81c6d 100644 --- a/libs/hwui/tests/unit/RecordingCanvasTests.cpp +++ b/libs/hwui/tests/unit/RecordingCanvasTests.cpp @@ -32,7 +32,7 @@ namespace android { namespace uirenderer { static void playbackOps(const DisplayList& displayList, - std::function<void(const RecordedOp&)> opReceiver) { + std::function<void(const RecordedOp&)> opReceiver) { for (auto& chunk : displayList.getChunks()) { for (size_t opIndex = chunk.beginOpIndex; opIndex < chunk.endOpIndex; opIndex++) { RecordedOp* op = displayList.getOps()[opIndex]; @@ -42,7 +42,7 @@ static void playbackOps(const DisplayList& displayList, } static void validateSingleOp(std::unique_ptr<DisplayList>& dl, - std::function<void(const RecordedOp& op)> opValidator) { + std::function<void(const RecordedOp& op)> opValidator) { ASSERT_EQ(1u, dl->getOps().size()) << "Must be exactly one op"; opValidator(*(dl->getOps()[0])); } @@ -82,7 +82,7 @@ OPENGL_PIPELINE_TEST(RecordingCanvas, emptyClipRect) { canvas.save(SaveFlags::MatrixClip); canvas.clipRect(0, 0, 100, 100, SkClipOp::kIntersect); canvas.clipRect(100, 100, 200, 200, SkClipOp::kIntersect); - canvas.drawRect(0, 0, 50, 50, SkPaint()); // rejected at record time + canvas.drawRect(0, 0, 50, 50, SkPaint()); // rejected at record time canvas.restore(); }); ASSERT_EQ(0u, dl->getOps().size()) << "Must be zero ops. Rect should be rejected."; @@ -120,16 +120,16 @@ OPENGL_PIPELINE_TEST(RecordingCanvas, drawArc) { EXPECT_EQ(RecordedOpId::ArcOp, ops[0]->opId); EXPECT_EQ(Rect(200, 200), ops[0]->unmappedBounds); - EXPECT_EQ(RecordedOpId::OvalOp, ops[1]->opId) - << "Circular arcs should be converted to ovals"; + EXPECT_EQ(RecordedOpId::OvalOp, ops[1]->opId) << "Circular arcs should be converted to ovals"; EXPECT_EQ(Rect(100, 100), ops[1]->unmappedBounds); } OPENGL_PIPELINE_TEST(RecordingCanvas, drawLines) { auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) { SkPaint paint; - paint.setStrokeWidth(20); // doesn't affect recorded bounds - would be resolved at bake time - float points[] = { 0, 0, 20, 10, 30, 40, 90 }; // NB: only 1 valid line + paint.setStrokeWidth( + 20); // doesn't affect recorded bounds - would be resolved at bake time + float points[] = {0, 0, 20, 10, 30, 40, 90}; // NB: only 1 valid line canvas.drawLines(&points[0], 7, paint); }); @@ -143,9 +143,8 @@ OPENGL_PIPELINE_TEST(RecordingCanvas, drawLines) { } OPENGL_PIPELINE_TEST(RecordingCanvas, drawRect) { - auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) { - canvas.drawRect(10, 20, 90, 180, SkPaint()); - }); + auto dl = TestUtils::createDisplayList<RecordingCanvas>( + 100, 200, [](RecordingCanvas& canvas) { canvas.drawRect(10, 20, 90, 180, SkPaint()); }); ASSERT_EQ(1u, dl->getOps().size()) << "Must be exactly one op"; auto op = *(dl->getOps()[0]); @@ -168,7 +167,7 @@ OPENGL_PIPELINE_TEST(RecordingCanvas, drawRoundRect) { }); ASSERT_EQ(1u, dl->getOps().size()) << "Must be exactly one op"; ASSERT_EQ(RecordedOpId::RectOp, dl->getOps()[0]->opId) - << "Non-rounded rects should be converted"; + << "Non-rounded rects should be converted"; } OPENGL_PIPELINE_TEST(RecordingCanvas, drawGlyphs) { @@ -176,7 +175,6 @@ OPENGL_PIPELINE_TEST(RecordingCanvas, drawGlyphs) { SkPaint paint; paint.setAntiAlias(true); paint.setTextSize(20); - paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); TestUtils::drawUtf8ToCanvas(&canvas, "test text", paint, 25, 25); }); @@ -197,7 +195,6 @@ OPENGL_PIPELINE_TEST(RecordingCanvas, drawGlyphs_strikeThruAndUnderline) { SkPaint paint; paint.setAntiAlias(true); paint.setTextSize(20); - paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); for (int i = 0; i < 2; i++) { for (int j = 0; j < 2; j++) { uint32_t flags = paint.getFlags(); @@ -221,17 +218,17 @@ OPENGL_PIPELINE_TEST(RecordingCanvas, drawGlyphs_strikeThruAndUnderline) { ASSERT_EQ(8u, ops.size()); int index = 0; - EXPECT_EQ(RecordedOpId::TextOp, ops[index++]->opId); // no underline or strikethrough + EXPECT_EQ(RecordedOpId::TextOp, ops[index++]->opId); // no underline or strikethrough EXPECT_EQ(RecordedOpId::TextOp, ops[index++]->opId); - EXPECT_EQ(RecordedOpId::RectOp, ops[index++]->opId); // strikethrough only + EXPECT_EQ(RecordedOpId::RectOp, ops[index++]->opId); // strikethrough only EXPECT_EQ(RecordedOpId::TextOp, ops[index++]->opId); - EXPECT_EQ(RecordedOpId::RectOp, ops[index++]->opId); // underline only + EXPECT_EQ(RecordedOpId::RectOp, ops[index++]->opId); // underline only EXPECT_EQ(RecordedOpId::TextOp, ops[index++]->opId); - EXPECT_EQ(RecordedOpId::RectOp, ops[index++]->opId); // underline - EXPECT_EQ(RecordedOpId::RectOp, ops[index++]->opId); // strikethrough + EXPECT_EQ(RecordedOpId::RectOp, ops[index++]->opId); // underline + EXPECT_EQ(RecordedOpId::RectOp, ops[index++]->opId); // strikethrough } OPENGL_PIPELINE_TEST(RecordingCanvas, drawGlyphs_forceAlignLeft) { @@ -239,7 +236,6 @@ OPENGL_PIPELINE_TEST(RecordingCanvas, drawGlyphs_forceAlignLeft) { SkPaint paint; paint.setAntiAlias(true); paint.setTextSize(20); - paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); paint.setTextAlign(SkPaint::kLeft_Align); TestUtils::drawUtf8ToCanvas(&canvas, "test text", paint, 25, 25); paint.setTextAlign(SkPaint::kCenter_Align); @@ -329,15 +325,14 @@ OPENGL_PIPELINE_TEST(RecordingCanvas, backgroundAndImage) { } RENDERTHREAD_OPENGL_PIPELINE_TEST(RecordingCanvas, textureLayer) { - auto layerUpdater = TestUtils::createTextureLayerUpdater(renderThread, 100, 100, - SkMatrix::MakeTrans(5, 5)); + auto layerUpdater = + TestUtils::createTextureLayerUpdater(renderThread, 100, 100, SkMatrix::MakeTrans(5, 5)); - auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, - [&layerUpdater](RecordingCanvas& canvas) { - canvas.drawLayer(layerUpdater.get()); - }); + auto dl = TestUtils::createDisplayList<RecordingCanvas>( + 200, 200, + [&layerUpdater](RecordingCanvas& canvas) { canvas.drawLayer(layerUpdater.get()); }); - validateSingleOp(dl, [] (const RecordedOp& op) { + validateSingleOp(dl, [](const RecordedOp& op) { ASSERT_EQ(RecordedOpId::TextureLayerOp, op.opId); ASSERT_TRUE(op.localMatrix.isIdentity()) << "Op must not apply matrix at record time."; }); @@ -352,26 +347,26 @@ OPENGL_PIPELINE_TEST(RecordingCanvas, saveLayer_simple) { int count = 0; playbackOps(*dl, [&count](const RecordedOp& op) { Matrix4 expectedMatrix; - switch(count++) { - case 0: - EXPECT_EQ(RecordedOpId::BeginLayerOp, op.opId); - EXPECT_EQ(Rect(10, 20, 190, 180), op.unmappedBounds); - EXPECT_EQ(nullptr, op.localClip); - EXPECT_TRUE(op.localMatrix.isIdentity()); - break; - case 1: - EXPECT_EQ(RecordedOpId::RectOp, op.opId); - EXPECT_CLIP_RECT(Rect(180, 160), op.localClip); - EXPECT_EQ(Rect(10, 20, 190, 180), op.unmappedBounds); - expectedMatrix.loadTranslate(-10, -20, 0); - EXPECT_MATRIX_APPROX_EQ(expectedMatrix, op.localMatrix); - break; - case 2: - EXPECT_EQ(RecordedOpId::EndLayerOp, op.opId); - // Don't bother asserting recording state data - it's not used - break; - default: - ADD_FAILURE(); + switch (count++) { + case 0: + EXPECT_EQ(RecordedOpId::BeginLayerOp, op.opId); + EXPECT_EQ(Rect(10, 20, 190, 180), op.unmappedBounds); + EXPECT_EQ(nullptr, op.localClip); + EXPECT_TRUE(op.localMatrix.isIdentity()); + break; + case 1: + EXPECT_EQ(RecordedOpId::RectOp, op.opId); + EXPECT_CLIP_RECT(Rect(180, 160), op.localClip); + EXPECT_EQ(Rect(10, 20, 190, 180), op.unmappedBounds); + expectedMatrix.loadTranslate(-10, -20, 0); + EXPECT_MATRIX_APPROX_EQ(expectedMatrix, op.localMatrix); + break; + case 2: + EXPECT_EQ(RecordedOpId::EndLayerOp, op.opId); + // Don't bother asserting recording state data - it's not used + break; + default: + ADD_FAILURE(); } }); EXPECT_EQ(3, count); @@ -379,14 +374,14 @@ OPENGL_PIPELINE_TEST(RecordingCanvas, saveLayer_simple) { OPENGL_PIPELINE_TEST(RecordingCanvas, saveLayer_rounding) { auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 100, [](RecordingCanvas& canvas) { - canvas.saveLayerAlpha(10.25f, 10.75f, 89.25f, 89.75f, 128, SaveFlags::ClipToLayer); - canvas.drawRect(20, 20, 80, 80, SkPaint()); - canvas.restore(); - }); - int count = 0; - playbackOps(*dl, [&count](const RecordedOp& op) { - Matrix4 expectedMatrix; - switch(count++) { + canvas.saveLayerAlpha(10.25f, 10.75f, 89.25f, 89.75f, 128, SaveFlags::ClipToLayer); + canvas.drawRect(20, 20, 80, 80, SkPaint()); + canvas.restore(); + }); + int count = 0; + playbackOps(*dl, [&count](const RecordedOp& op) { + Matrix4 expectedMatrix; + switch (count++) { case 0: EXPECT_EQ(RecordedOpId::BeginLayerOp, op.opId); EXPECT_EQ(Rect(10, 10, 90, 90), op.unmappedBounds) << "Expect bounds rounded out"; @@ -402,9 +397,9 @@ OPENGL_PIPELINE_TEST(RecordingCanvas, saveLayer_rounding) { break; default: ADD_FAILURE(); - } - }); - EXPECT_EQ(3, count); + } + }); + EXPECT_EQ(3, count); } OPENGL_PIPELINE_TEST(RecordingCanvas, saveLayer_missingRestore) { @@ -424,31 +419,31 @@ OPENGL_PIPELINE_TEST(RecordingCanvas, saveLayer_missingRestore) { OPENGL_PIPELINE_TEST(RecordingCanvas, saveLayer_simpleUnclipped) { auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) { - canvas.saveLayerAlpha(10, 20, 190, 180, 128, (SaveFlags::Flags)0); // unclipped + canvas.saveLayerAlpha(10, 20, 190, 180, 128, (SaveFlags::Flags)0); // unclipped canvas.drawRect(10, 20, 190, 180, SkPaint()); canvas.restore(); }); int count = 0; playbackOps(*dl, [&count](const RecordedOp& op) { - switch(count++) { - case 0: - EXPECT_EQ(RecordedOpId::BeginUnclippedLayerOp, op.opId); - EXPECT_EQ(Rect(10, 20, 190, 180), op.unmappedBounds); - EXPECT_EQ(nullptr, op.localClip); - EXPECT_TRUE(op.localMatrix.isIdentity()); - break; - case 1: - EXPECT_EQ(RecordedOpId::RectOp, op.opId); - EXPECT_EQ(nullptr, op.localClip); - EXPECT_EQ(Rect(10, 20, 190, 180), op.unmappedBounds); - EXPECT_TRUE(op.localMatrix.isIdentity()); - break; - case 2: - EXPECT_EQ(RecordedOpId::EndUnclippedLayerOp, op.opId); - // Don't bother asserting recording state data - it's not used - break; - default: - ADD_FAILURE(); + switch (count++) { + case 0: + EXPECT_EQ(RecordedOpId::BeginUnclippedLayerOp, op.opId); + EXPECT_EQ(Rect(10, 20, 190, 180), op.unmappedBounds); + EXPECT_EQ(nullptr, op.localClip); + EXPECT_TRUE(op.localMatrix.isIdentity()); + break; + case 1: + EXPECT_EQ(RecordedOpId::RectOp, op.opId); + EXPECT_EQ(nullptr, op.localClip); + EXPECT_EQ(Rect(10, 20, 190, 180), op.unmappedBounds); + EXPECT_TRUE(op.localMatrix.isIdentity()); + break; + case 2: + EXPECT_EQ(RecordedOpId::EndUnclippedLayerOp, op.opId); + // Don't bother asserting recording state data - it's not used + break; + default: + ADD_FAILURE(); } }); EXPECT_EQ(3, count); @@ -458,7 +453,7 @@ OPENGL_PIPELINE_TEST(RecordingCanvas, saveLayer_addClipFlag) { auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) { canvas.save(SaveFlags::MatrixClip); canvas.clipRect(10, 20, 190, 180, SkClipOp::kIntersect); - canvas.saveLayerAlpha(10, 20, 190, 180, 128, (SaveFlags::Flags)0); // unclipped + canvas.saveLayerAlpha(10, 20, 190, 180, 128, (SaveFlags::Flags)0); // unclipped canvas.drawRect(10, 20, 190, 180, SkPaint()); canvas.restore(); canvas.restore(); @@ -487,7 +482,7 @@ OPENGL_PIPELINE_TEST(RecordingCanvas, saveLayer_viewportCrop) { if (count++ == 1) { Matrix4 expectedMatrix; EXPECT_EQ(RecordedOpId::RectOp, op.opId); - EXPECT_CLIP_RECT(Rect(100, 100), op.localClip) // Recorded clip rect should be + EXPECT_CLIP_RECT(Rect(100, 100), op.localClip) // Recorded clip rect should be // intersection of viewport and saveLayer bounds, in layer space; EXPECT_EQ(Rect(400, 400), op.unmappedBounds); expectedMatrix.loadTranslate(-100, -100, 0); @@ -564,7 +559,7 @@ OPENGL_PIPELINE_TEST(RecordingCanvas, saveLayer_rotateClipped) { OPENGL_PIPELINE_TEST(RecordingCanvas, saveLayer_rejectBegin) { auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) { canvas.save(SaveFlags::MatrixClip); - canvas.translate(0, -20); // avoid identity case + canvas.translate(0, -20); // avoid identity case // empty clip rect should force layer + contents to be rejected canvas.clipRect(0, -20, 200, -20, SkClipOp::kIntersect); canvas.saveLayerAlpha(0, 0, 200, 200, 128, SaveFlags::ClipToLayer); @@ -577,37 +572,38 @@ OPENGL_PIPELINE_TEST(RecordingCanvas, saveLayer_rejectBegin) { } OPENGL_PIPELINE_TEST(RecordingCanvas, drawRenderNode_rejection) { - auto child = TestUtils::createNode(50, 50, 150, 150, - [](RenderProperties& props, Canvas& canvas) { - SkPaint paint; - paint.setColor(SK_ColorWHITE); - canvas.drawRect(0, 0, 100, 100, paint); - }); - - auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [&child](RecordingCanvas& canvas) { - canvas.clipRect(0, 0, 0, 0, SkClipOp::kIntersect); // empty clip, reject node - canvas.drawRenderNode(child.get()); // shouldn't crash when rejecting node... - }); + auto child = + TestUtils::createNode(50, 50, 150, 150, [](RenderProperties& props, Canvas& canvas) { + SkPaint paint; + paint.setColor(SK_ColorWHITE); + canvas.drawRect(0, 0, 100, 100, paint); + }); + + auto dl = TestUtils::createDisplayList<RecordingCanvas>( + 200, 200, [&child](RecordingCanvas& canvas) { + canvas.clipRect(0, 0, 0, 0, SkClipOp::kIntersect); // empty clip, reject node + canvas.drawRenderNode(child.get()); // shouldn't crash when rejecting node... + }); ASSERT_TRUE(dl->isEmpty()); } OPENGL_PIPELINE_TEST(RecordingCanvas, drawRenderNode_projection) { - sp<RenderNode> background = TestUtils::createNode(50, 50, 150, 150, - [](RenderProperties& props, Canvas& canvas) { - SkPaint paint; - paint.setColor(SK_ColorWHITE); - canvas.drawRect(0, 0, 100, 100, paint); - }); + sp<RenderNode> background = + TestUtils::createNode(50, 50, 150, 150, [](RenderProperties& props, Canvas& canvas) { + SkPaint paint; + paint.setColor(SK_ColorWHITE); + canvas.drawRect(0, 0, 100, 100, paint); + }); { background->mutateStagingProperties().setProjectionReceiver(false); // NO RECEIVER PRESENT - auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, - [&background](RecordingCanvas& canvas) { - canvas.drawRect(0, 0, 100, 100, SkPaint()); - canvas.drawRenderNode(background.get()); - canvas.drawRect(0, 0, 100, 100, SkPaint()); - }); + auto dl = TestUtils::createDisplayList<RecordingCanvas>( + 200, 200, [&background](RecordingCanvas& canvas) { + canvas.drawRect(0, 0, 100, 100, SkPaint()); + canvas.drawRenderNode(background.get()); + canvas.drawRect(0, 0, 100, 100, SkPaint()); + }); EXPECT_EQ(-1, dl->projectionReceiveIndex) << "no projection receiver should have been observed"; } @@ -615,18 +611,17 @@ OPENGL_PIPELINE_TEST(RecordingCanvas, drawRenderNode_projection) { background->mutateStagingProperties().setProjectionReceiver(true); // RECEIVER PRESENT - auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, - [&background](RecordingCanvas& canvas) { - canvas.drawRect(0, 0, 100, 100, SkPaint()); - canvas.drawRenderNode(background.get()); - canvas.drawRect(0, 0, 100, 100, SkPaint()); - }); + auto dl = TestUtils::createDisplayList<RecordingCanvas>( + 200, 200, [&background](RecordingCanvas& canvas) { + canvas.drawRect(0, 0, 100, 100, SkPaint()); + canvas.drawRenderNode(background.get()); + canvas.drawRect(0, 0, 100, 100, SkPaint()); + }); ASSERT_EQ(3u, dl->getOps().size()) << "Must be three ops"; auto op = dl->getOps()[1]; EXPECT_EQ(RecordedOpId::RenderNodeOp, op->opId); - EXPECT_EQ(1, dl->projectionReceiveIndex) - << "correct projection receiver not identified"; + EXPECT_EQ(1, dl->projectionReceiveIndex) << "correct projection receiver not identified"; // verify the behavior works even though projection receiver hasn't been sync'd yet EXPECT_TRUE(background->stagingProperties().isProjectionReceiver()); @@ -718,17 +713,18 @@ OPENGL_PIPELINE_TEST(RecordingCanvas, insertReorderBarrier_clip) { OPENGL_PIPELINE_TEST(RecordingCanvas, refPaint) { SkPaint paint; - auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [&paint](RecordingCanvas& canvas) { - paint.setColor(SK_ColorBLUE); - // first two should use same paint - canvas.drawRect(0, 0, 200, 10, paint); - SkPaint paintCopy(paint); - canvas.drawRect(0, 10, 200, 20, paintCopy); - - // only here do we use different paint ptr - paint.setColor(SK_ColorRED); - canvas.drawRect(0, 20, 200, 30, paint); - }); + auto dl = TestUtils::createDisplayList<RecordingCanvas>( + 200, 200, [&paint](RecordingCanvas& canvas) { + paint.setColor(SK_ColorBLUE); + // first two should use same paint + canvas.drawRect(0, 0, 200, 10, paint); + SkPaint paintCopy(paint); + canvas.drawRect(0, 10, 200, 20, paintCopy); + + // only here do we use different paint ptr + paint.setColor(SK_ColorRED); + canvas.drawRect(0, 20, 200, 30, paint); + }); auto ops = dl->getOps(); ASSERT_EQ(3u, ops.size()); @@ -745,56 +741,58 @@ OPENGL_PIPELINE_TEST(RecordingCanvas, refPaint) { OPENGL_PIPELINE_TEST(RecordingCanvas, refBitmap) { sk_sp<Bitmap> bitmap(TestUtils::createBitmap(100, 100)); - auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 100, [&bitmap](RecordingCanvas& canvas) { - canvas.drawBitmap(*bitmap, 0, 0, nullptr); - }); + auto dl = TestUtils::createDisplayList<RecordingCanvas>( + 100, 100, + [&bitmap](RecordingCanvas& canvas) { canvas.drawBitmap(*bitmap, 0, 0, nullptr); }); auto& bitmaps = dl->getBitmapResources(); EXPECT_EQ(1u, bitmaps.size()); } OPENGL_PIPELINE_TEST(RecordingCanvas, refBitmapInShader_bitmapShader) { sk_sp<Bitmap> bitmap = TestUtils::createBitmap(100, 100); - auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 100, [&bitmap](RecordingCanvas& canvas) { - SkPaint paint; - SkBitmap skBitmap; - bitmap->getSkBitmap(&skBitmap); - sk_sp<SkImage> image = SkMakeImageFromRasterBitmap(skBitmap, kNever_SkCopyPixelsMode); - sk_sp<SkShader> shader = image->makeShader( - SkShader::TileMode::kClamp_TileMode, - SkShader::TileMode::kClamp_TileMode, - nullptr); - paint.setShader(std::move(shader)); - canvas.drawRoundRect(0, 0, 100, 100, 20.0f, 20.0f, paint); - }); + auto dl = TestUtils::createDisplayList<RecordingCanvas>( + 100, 100, [&bitmap](RecordingCanvas& canvas) { + SkPaint paint; + SkBitmap skBitmap; + bitmap->getSkBitmap(&skBitmap); + sk_sp<SkImage> image = + SkMakeImageFromRasterBitmap(skBitmap, kNever_SkCopyPixelsMode); + sk_sp<SkShader> shader = + image->makeShader(SkShader::TileMode::kClamp_TileMode, + SkShader::TileMode::kClamp_TileMode, nullptr); + paint.setShader(std::move(shader)); + canvas.drawRoundRect(0, 0, 100, 100, 20.0f, 20.0f, paint); + }); auto& bitmaps = dl->getBitmapResources(); EXPECT_EQ(1u, bitmaps.size()); } OPENGL_PIPELINE_TEST(RecordingCanvas, refBitmapInShader_composeShader) { sk_sp<Bitmap> bitmap = TestUtils::createBitmap(100, 100); - auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 100, [&bitmap](RecordingCanvas& canvas) { - SkPaint paint; - SkBitmap skBitmap; - bitmap->getSkBitmap(&skBitmap); - sk_sp<SkImage> image = SkMakeImageFromRasterBitmap(skBitmap, kNever_SkCopyPixelsMode); - sk_sp<SkShader> shader1 = image->makeShader( - SkShader::TileMode::kClamp_TileMode, - SkShader::TileMode::kClamp_TileMode, - nullptr); - - SkPoint center; - center.set(50, 50); - SkColor colors[2]; - colors[0] = Color::Black; - colors[1] = Color::White; - sk_sp<SkShader> shader2 = SkGradientShader::MakeRadial(center, 50, colors, nullptr, 2, - SkShader::TileMode::kRepeat_TileMode); - - sk_sp<SkShader> composeShader = SkShader::MakeComposeShader(std::move(shader1), std::move(shader2), - SkBlendMode::kMultiply); - paint.setShader(std::move(composeShader)); - canvas.drawRoundRect(0, 0, 100, 100, 20.0f, 20.0f, paint); - }); + auto dl = TestUtils::createDisplayList<RecordingCanvas>( + 100, 100, [&bitmap](RecordingCanvas& canvas) { + SkPaint paint; + SkBitmap skBitmap; + bitmap->getSkBitmap(&skBitmap); + sk_sp<SkImage> image = + SkMakeImageFromRasterBitmap(skBitmap, kNever_SkCopyPixelsMode); + sk_sp<SkShader> shader1 = + image->makeShader(SkShader::TileMode::kClamp_TileMode, + SkShader::TileMode::kClamp_TileMode, nullptr); + + SkPoint center; + center.set(50, 50); + SkColor colors[2]; + colors[0] = Color::Black; + colors[1] = Color::White; + sk_sp<SkShader> shader2 = SkGradientShader::MakeRadial( + center, 50, colors, nullptr, 2, SkShader::TileMode::kRepeat_TileMode); + + sk_sp<SkShader> composeShader = SkShader::MakeComposeShader( + std::move(shader1), std::move(shader2), SkBlendMode::kMultiply); + paint.setShader(std::move(composeShader)); + canvas.drawRoundRect(0, 0, 100, 100, 20.0f, 20.0f, paint); + }); auto& bitmaps = dl->getBitmapResources(); EXPECT_EQ(1u, bitmaps.size()); } @@ -804,9 +802,7 @@ OPENGL_PIPELINE_TEST(RecordingCanvas, drawText) { Paint paint; paint.setAntiAlias(true); paint.setTextSize(20); - paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); - std::unique_ptr<uint16_t[]> dst = TestUtils::asciiToUtf16("HELLO"); - canvas.drawText(dst.get(), 0, 5, 5, 25, 25, minikin::kBidi_Force_LTR, paint, NULL); + TestUtils::drawUtf8ToCanvas(&canvas, "HELLO", paint, 25, 25); }); int count = 0; @@ -822,16 +818,15 @@ OPENGL_PIPELINE_TEST(RecordingCanvas, drawText) { } OPENGL_PIPELINE_TEST(RecordingCanvas, drawTextInHighContrast) { + Properties::enableHighContrastText = true; auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) { - canvas.setHighContrastText(true); Paint paint; paint.setColor(SK_ColorWHITE); paint.setAntiAlias(true); paint.setTextSize(20); - paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); - std::unique_ptr<uint16_t[]> dst = TestUtils::asciiToUtf16("HELLO"); - canvas.drawText(dst.get(), 0, 5, 5, 25, 25, minikin::kBidi_Force_LTR, paint, NULL); + TestUtils::drawUtf8ToCanvas(&canvas, "HELLO", paint, 25, 25); }); + Properties::enableHighContrastText = false; int count = 0; playbackOps(*dl, [&count](const RecordedOp& op) { @@ -848,5 +843,5 @@ OPENGL_PIPELINE_TEST(RecordingCanvas, drawTextInHighContrast) { ASSERT_EQ(2, count); } -} // namespace uirenderer -} // namespace android +} // namespace uirenderer +} // namespace android diff --git a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp index 4c3e182ced2f..2953ea8b21e9 100644 --- a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp +++ b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp @@ -14,24 +14,24 @@ * limitations under the License. */ -#include <gtest/gtest.h> #include <VectorDrawable.h> +#include <gtest/gtest.h> +#include <SkClipStack.h> +#include <SkLiteRecorder.h> +#include <SkSurface_Base.h> +#include <string.h> #include "AnimationContext.h" #include "DamageAccumulator.h" +#include "FatalTestCanvas.h" #include "IContextFactory.h" +#include "SkiaCanvas.h" #include "pipeline/skia/SkiaDisplayList.h" +#include "pipeline/skia/SkiaOpenGLPipeline.h" #include "pipeline/skia/SkiaPipeline.h" #include "pipeline/skia/SkiaRecordingCanvas.h" #include "renderthread/CanvasContext.h" #include "tests/common/TestUtils.h" -#include "SkiaCanvas.h" -#include <SkSurface_Base.h> -#include <SkLiteRecorder.h> -#include <SkClipStack.h> -#include "FatalTestCanvas.h" -#include <string.h> - using namespace android; using namespace android::uirenderer; @@ -39,8 +39,8 @@ using namespace android::uirenderer::renderthread; using namespace android::uirenderer::skiapipeline; TEST(RenderNodeDrawable, create) { - auto rootNode = TestUtils::createNode(0, 0, 200, 400, - [](RenderProperties& props, Canvas& canvas) { + auto rootNode = + TestUtils::createNode(0, 0, 200, 400, [](RenderProperties& props, Canvas& canvas) { canvas.drawColor(Color::Red_500, SkBlendMode::kSrcOver); }); @@ -65,47 +65,50 @@ static void drawOrderedRect(Canvas* canvas, uint8_t expectedDrawOrder) { } static void drawOrderedNode(Canvas* canvas, uint8_t expectedDrawOrder, float z) { - auto node = TestUtils::createSkiaNode(0, 0, 100, 100, + auto node = TestUtils::createSkiaNode( + 0, 0, 100, 100, [expectedDrawOrder, z](RenderProperties& props, SkiaRecordingCanvas& canvas) { - drawOrderedRect(&canvas, expectedDrawOrder); - props.setTranslationZ(z); - }); - canvas->drawRenderNode(node.get()); // canvas takes reference/sole ownership + drawOrderedRect(&canvas, expectedDrawOrder); + props.setTranslationZ(z); + }); + canvas->drawRenderNode(node.get()); // canvas takes reference/sole ownership } -static void drawOrderedNode(Canvas* canvas, uint8_t expectedDrawOrder, +static void drawOrderedNode( + Canvas* canvas, uint8_t expectedDrawOrder, std::function<void(RenderProperties& props, SkiaRecordingCanvas& canvas)> setup) { - auto node = TestUtils::createSkiaNode(0, 0, 100, 100, + auto node = TestUtils::createSkiaNode( + 0, 0, 100, 100, [expectedDrawOrder, setup](RenderProperties& props, SkiaRecordingCanvas& canvas) { - drawOrderedRect(&canvas, expectedDrawOrder); - if (setup) { - setup(props, canvas); - } - }); - canvas->drawRenderNode(node.get()); // canvas takes reference/sole ownership + drawOrderedRect(&canvas, expectedDrawOrder); + if (setup) { + setup(props, canvas); + } + }); + canvas->drawRenderNode(node.get()); // canvas takes reference/sole ownership } class ZReorderCanvas : public SkCanvas { public: ZReorderCanvas(int width, int height) : SkCanvas(width, height) {} void onDrawRect(const SkRect& rect, const SkPaint& paint) override { - int expectedOrder = SkColorGetB(paint.getColor()); // extract order from blue channel + int expectedOrder = SkColorGetB(paint.getColor()); // extract order from blue channel EXPECT_EQ(expectedOrder, mDrawCounter++) << "An op was drawn out of order"; } int getIndex() { return mDrawCounter; } + protected: int mDrawCounter = 0; }; -} // end anonymous namespace +} // end anonymous namespace TEST(RenderNodeDrawable, zReorder) { - - auto parent = TestUtils::createSkiaNode(0, 0, 100, 100, - [](RenderProperties& props, SkiaRecordingCanvas& canvas) { + auto parent = TestUtils::createSkiaNode(0, 0, 100, 100, [](RenderProperties& props, + SkiaRecordingCanvas& canvas) { canvas.insertReorderBarrier(true); canvas.insertReorderBarrier(false); - drawOrderedNode(&canvas, 0, 10.0f); // in reorder=false at this point, so played inorder + drawOrderedNode(&canvas, 0, 10.0f); // in reorder=false at this point, so played inorder drawOrderedRect(&canvas, 1); canvas.insertReorderBarrier(true); drawOrderedNode(&canvas, 6, 2.0f); @@ -116,37 +119,36 @@ TEST(RenderNodeDrawable, zReorder) { drawOrderedNode(&canvas, 7, 2.0f); canvas.insertReorderBarrier(false); drawOrderedRect(&canvas, 8); - drawOrderedNode(&canvas, 9, -10.0f); // in reorder=false at this point, so played inorder - canvas.insertReorderBarrier(true); //reorder a node ahead of drawrect op + drawOrderedNode(&canvas, 9, -10.0f); // in reorder=false at this point, so played inorder + canvas.insertReorderBarrier(true); // reorder a node ahead of drawrect op drawOrderedRect(&canvas, 11); drawOrderedNode(&canvas, 10, -1.0f); canvas.insertReorderBarrier(false); - canvas.insertReorderBarrier(true); //test with two empty reorder sections + canvas.insertReorderBarrier(true); // test with two empty reorder sections canvas.insertReorderBarrier(true); canvas.insertReorderBarrier(false); drawOrderedRect(&canvas, 12); }); - //create a canvas not backed by any device/pixels, but with dimensions to avoid quick rejection + // create a canvas not backed by any device/pixels, but with dimensions to avoid quick rejection ZReorderCanvas canvas(100, 100); RenderNodeDrawable drawable(parent.get(), &canvas, false); canvas.drawDrawable(&drawable); EXPECT_EQ(13, canvas.getIndex()); } -TEST(RenderNodeDrawable, composeOnLayer) -{ +TEST(RenderNodeDrawable, composeOnLayer) { auto surface = SkSurface::MakeRasterN32Premul(1, 1); SkCanvas& canvas = *surface->getCanvas(); canvas.drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver); ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE); - auto rootNode = TestUtils::createSkiaNode(0, 0, 1, 1, - [](RenderProperties& props, SkiaRecordingCanvas& recorder) { - recorder.drawColor(SK_ColorRED, SkBlendMode::kSrcOver); - }); + auto rootNode = TestUtils::createSkiaNode( + 0, 0, 1, 1, [](RenderProperties& props, SkiaRecordingCanvas& recorder) { + recorder.drawColor(SK_ColorRED, SkBlendMode::kSrcOver); + }); - //attach a layer to the render node + // attach a layer to the render node auto surfaceLayer = SkSurface::MakeRasterN32Premul(1, 1); auto canvas2 = surfaceLayer->getCanvas(); canvas2->drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver); @@ -181,39 +183,38 @@ static SkMatrix getRecorderMatrix(const SkiaRecordingCanvas& recorder) { } } -TEST(RenderNodeDrawable, saveLayerClipAndMatrixRestore) -{ +TEST(RenderNodeDrawable, saveLayerClipAndMatrixRestore) { auto surface = SkSurface::MakeRasterN32Premul(400, 800); SkCanvas& canvas = *surface->getCanvas(); canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver); ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorWHITE); - auto rootNode = TestUtils::createSkiaNode(0, 0, 400, 800, - [](RenderProperties& props, SkiaRecordingCanvas& recorder) { - SkPaint layerPaint; - ASSERT_EQ(SkRect::MakeLTRB(0, 0, 400, 800), getRecorderClipBounds(recorder)); - EXPECT_TRUE(getRecorderMatrix(recorder).isIdentity()); + auto rootNode = TestUtils::createSkiaNode( + 0, 0, 400, 800, [](RenderProperties& props, SkiaRecordingCanvas& recorder) { + SkPaint layerPaint; + ASSERT_EQ(SkRect::MakeLTRB(0, 0, 400, 800), getRecorderClipBounds(recorder)); + EXPECT_TRUE(getRecorderMatrix(recorder).isIdentity()); - //note we don't pass SaveFlags::MatrixClip, but matrix and clip will be saved - recorder.saveLayer(0, 0, 400, 400, &layerPaint, SaveFlags::ClipToLayer); - ASSERT_EQ(SkRect::MakeLTRB(0, 0, 400, 400), getRecorderClipBounds(recorder)); - EXPECT_TRUE(getRecorderMatrix(recorder).isIdentity()); + // note we don't pass SaveFlags::MatrixClip, but matrix and clip will be saved + recorder.saveLayer(0, 0, 400, 400, &layerPaint, SaveFlags::ClipToLayer); + ASSERT_EQ(SkRect::MakeLTRB(0, 0, 400, 400), getRecorderClipBounds(recorder)); + EXPECT_TRUE(getRecorderMatrix(recorder).isIdentity()); - recorder.clipRect(50, 50, 350, 350, SkClipOp::kIntersect); - ASSERT_EQ(SkRect::MakeLTRB(50, 50, 350, 350), getRecorderClipBounds(recorder)); + recorder.clipRect(50, 50, 350, 350, SkClipOp::kIntersect); + ASSERT_EQ(SkRect::MakeLTRB(50, 50, 350, 350), getRecorderClipBounds(recorder)); - recorder.translate(300.0f, 400.0f); - EXPECT_EQ(SkMatrix::MakeTrans(300.0f, 400.0f), getRecorderMatrix(recorder)); + recorder.translate(300.0f, 400.0f); + EXPECT_EQ(SkMatrix::MakeTrans(300.0f, 400.0f), getRecorderMatrix(recorder)); - recorder.restore(); - ASSERT_EQ(SkRect::MakeLTRB(0, 0, 400, 800), getRecorderClipBounds(recorder)); - EXPECT_TRUE(getRecorderMatrix(recorder).isIdentity()); + recorder.restore(); + ASSERT_EQ(SkRect::MakeLTRB(0, 0, 400, 800), getRecorderClipBounds(recorder)); + EXPECT_TRUE(getRecorderMatrix(recorder).isIdentity()); - SkPaint paint; - paint.setAntiAlias(true); - paint.setColor(SK_ColorGREEN); - recorder.drawRect(0.0f, 400.0f, 400.0f, 800.0f, paint); - }); + SkPaint paint; + paint.setAntiAlias(true); + paint.setColor(SK_ColorGREEN); + recorder.drawRect(0.0f, 400.0f, 400.0f, 800.0f, paint); + }); RenderNodeDrawable drawable(rootNode.get(), &canvas, true); canvas.drawDrawable(&drawable); @@ -227,7 +228,7 @@ public: return new AnimationContext(clock); } }; -} // end anonymous namespace +} // end anonymous namespace RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorder) { static const int SCROLL_X = 5; @@ -237,33 +238,36 @@ RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorder) { ProjectionTestCanvas(int width, int height) : SkCanvas(width, height) {} void onDrawRect(const SkRect& rect, const SkPaint& paint) override { const int index = mDrawCounter++; - SkMatrix expectedMatrix;; + SkMatrix expectedMatrix; + ; switch (index) { - case 0: //this is node "B" - EXPECT_EQ(SkRect::MakeWH(100, 100), rect); - EXPECT_EQ(SK_ColorWHITE, paint.getColor()); - expectedMatrix.reset(); - EXPECT_EQ(SkRect::MakeLTRB(0, 0, 100, 100), TestUtils::getClipBounds(this)); - break; - case 1: //this is node "P" - EXPECT_EQ(SkRect::MakeLTRB(-10, -10, 60, 60), rect); - EXPECT_EQ(SK_ColorDKGRAY, paint.getColor()); - expectedMatrix.setTranslate(50 - SCROLL_X, 50 - SCROLL_Y); - EXPECT_EQ(SkRect::MakeLTRB(-35, -30, 45, 50), TestUtils::getLocalClipBounds(this)); - break; - case 2: //this is node "C" - EXPECT_EQ(SkRect::MakeWH(100, 50), rect); - EXPECT_EQ(SK_ColorBLUE, paint.getColor()); - expectedMatrix.setTranslate(-SCROLL_X, 50 - SCROLL_Y); - EXPECT_EQ(SkRect::MakeLTRB(0, 40, 95, 90), TestUtils::getClipBounds(this)); - break; - default: - ADD_FAILURE(); + case 0: // this is node "B" + EXPECT_EQ(SkRect::MakeWH(100, 100), rect); + EXPECT_EQ(SK_ColorWHITE, paint.getColor()); + expectedMatrix.reset(); + EXPECT_EQ(SkRect::MakeLTRB(0, 0, 100, 100), TestUtils::getClipBounds(this)); + break; + case 1: // this is node "P" + EXPECT_EQ(SkRect::MakeLTRB(-10, -10, 60, 60), rect); + EXPECT_EQ(SK_ColorDKGRAY, paint.getColor()); + expectedMatrix.setTranslate(50 - SCROLL_X, 50 - SCROLL_Y); + EXPECT_EQ(SkRect::MakeLTRB(-35, -30, 45, 50), + TestUtils::getLocalClipBounds(this)); + break; + case 2: // this is node "C" + EXPECT_EQ(SkRect::MakeWH(100, 50), rect); + EXPECT_EQ(SK_ColorBLUE, paint.getColor()); + expectedMatrix.setTranslate(-SCROLL_X, 50 - SCROLL_Y); + EXPECT_EQ(SkRect::MakeLTRB(0, 40, 95, 90), TestUtils::getClipBounds(this)); + break; + default: + ADD_FAILURE(); } EXPECT_EQ(expectedMatrix, getTotalMatrix()); } int getIndex() { return mDrawCounter; } + protected: int mDrawCounter = 0; }; @@ -276,66 +280,143 @@ RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorder) { * The parent is scrolled by SCROLL_X/SCROLL_Y, but this does not affect the background * (which isn't affected by scroll). */ - auto receiverBackground = TestUtils::createSkiaNode(0, 0, 100, 100, + auto receiverBackground = TestUtils::createSkiaNode( + 0, 0, 100, 100, [](RenderProperties& properties, SkiaRecordingCanvas& canvas) { - properties.setProjectionReceiver(true); - // scroll doesn't apply to background, so undone via translationX/Y - // NOTE: translationX/Y only! no other transform properties may be set for a proj receiver! - properties.setTranslationX(SCROLL_X); - properties.setTranslationY(SCROLL_Y); - - SkPaint paint; - paint.setColor(SK_ColorWHITE); - canvas.drawRect(0, 0, 100, 100, paint); - }, "B"); - - auto projectingRipple = TestUtils::createSkiaNode(50, 0, 100, 50, + properties.setProjectionReceiver(true); + // scroll doesn't apply to background, so undone via translationX/Y + // NOTE: translationX/Y only! no other transform properties may be set for a proj + // receiver! + properties.setTranslationX(SCROLL_X); + properties.setTranslationY(SCROLL_Y); + + SkPaint paint; + paint.setColor(SK_ColorWHITE); + canvas.drawRect(0, 0, 100, 100, paint); + }, + "B"); + + auto projectingRipple = TestUtils::createSkiaNode( + 50, 0, 100, 50, [](RenderProperties& properties, SkiaRecordingCanvas& canvas) { - properties.setProjectBackwards(true); - properties.setClipToBounds(false); - SkPaint paint; - paint.setColor(SK_ColorDKGRAY); - canvas.drawRect(-10, -10, 60, 60, paint); - }, "P"); - auto child = TestUtils::createSkiaNode(0, 50, 100, 100, + properties.setProjectBackwards(true); + properties.setClipToBounds(false); + SkPaint paint; + paint.setColor(SK_ColorDKGRAY); + canvas.drawRect(-10, -10, 60, 60, paint); + }, + "P"); + auto child = TestUtils::createSkiaNode( + 0, 50, 100, 100, [&projectingRipple](RenderProperties& properties, SkiaRecordingCanvas& canvas) { - SkPaint paint; - paint.setColor(SK_ColorBLUE); - canvas.drawRect(0, 0, 100, 50, paint); - canvas.drawRenderNode(projectingRipple.get()); - }, "C"); - auto parent = TestUtils::createSkiaNode(0, 0, 100, 100, - [&receiverBackground, &child](RenderProperties& properties, SkiaRecordingCanvas& canvas) { - // Set a rect outline for the projecting ripple to be masked against. - properties.mutableOutline().setRoundRect(10, 10, 90, 90, 5, 1.0f); - - canvas.save(SaveFlags::MatrixClip); - canvas.translate(-SCROLL_X, -SCROLL_Y); // Apply scroll (note: bg undoes this internally) - canvas.drawRenderNode(receiverBackground.get()); - canvas.drawRenderNode(child.get()); - canvas.restore(); - }, "A"); + SkPaint paint; + paint.setColor(SK_ColorBLUE); + canvas.drawRect(0, 0, 100, 50, paint); + canvas.drawRenderNode(projectingRipple.get()); + }, + "C"); + auto parent = TestUtils::createSkiaNode( + 0, 0, 100, 100, + [&receiverBackground, &child](RenderProperties& properties, + SkiaRecordingCanvas& canvas) { + // Set a rect outline for the projecting ripple to be masked against. + properties.mutableOutline().setRoundRect(10, 10, 90, 90, 5, 1.0f); + + canvas.save(SaveFlags::MatrixClip); + canvas.translate(-SCROLL_X, + -SCROLL_Y); // Apply scroll (note: bg undoes this internally) + canvas.drawRenderNode(receiverBackground.get()); + canvas.drawRenderNode(child.get()); + canvas.restore(); + }, + "A"); ContextFactory contextFactory; - std::unique_ptr<CanvasContext> canvasContext(CanvasContext::create( - renderThread, false, parent.get(), &contextFactory)); + std::unique_ptr<CanvasContext> canvasContext( + CanvasContext::create(renderThread, false, parent.get(), &contextFactory)); TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get()); DamageAccumulator damageAccumulator; info.damageAccumulator = &damageAccumulator; parent->prepareTree(info); - //parent(A) -> (receiverBackground, child) - //child(C) -> (rect[0, 0, 100, 50], projectingRipple) - //projectingRipple(P) -> (rect[-10, -10, 60, 60]) -> projects backwards - //receiverBackground(B) -> (rect[0, 0, 100, 100]) -> projection receiver + // parent(A) -> (receiverBackground, child) + // child(C) -> (rect[0, 0, 100, 50], projectingRipple) + // projectingRipple(P) -> (rect[-10, -10, 60, 60]) -> projects backwards + // receiverBackground(B) -> (rect[0, 0, 100, 100]) -> projection receiver - //create a canvas not backed by any device/pixels, but with dimensions to avoid quick rejection + // create a canvas not backed by any device/pixels, but with dimensions to avoid quick rejection ProjectionTestCanvas canvas(100, 100); RenderNodeDrawable drawable(parent.get(), &canvas, true); canvas.drawDrawable(&drawable); EXPECT_EQ(3, canvas.getIndex()); } -RENDERTHREAD_TEST(RenderNodeDrawable, projectionHwLayer) { +RENDERTHREAD_SKIA_PIPELINE_TEST(RenderNodeDrawable, emptyReceiver) { + class ProjectionTestCanvas : public SkCanvas { + public: + ProjectionTestCanvas(int width, int height) : SkCanvas(width, height) {} + void onDrawRect(const SkRect& rect, const SkPaint& paint) override { + mDrawCounter++; + } + + int getDrawCounter() { return mDrawCounter; } + + private: + int mDrawCounter = 0; + }; + + auto receiverBackground = TestUtils::createSkiaNode( + 0, 0, 100, 100, + [](RenderProperties& properties, SkiaRecordingCanvas& canvas) { + properties.setProjectionReceiver(true); + }, + "B"); // a receiver with an empty display list + + auto projectingRipple = TestUtils::createSkiaNode( + 0, 0, 100, 100, + [](RenderProperties& properties, SkiaRecordingCanvas& canvas) { + properties.setProjectBackwards(true); + properties.setClipToBounds(false); + SkPaint paint; + canvas.drawRect(0, 0, 100, 100, paint); + }, + "P"); + auto child = TestUtils::createSkiaNode( + 0, 0, 100, 100, + [&projectingRipple](RenderProperties& properties, SkiaRecordingCanvas& canvas) { + SkPaint paint; + canvas.drawRect(0, 0, 100, 100, paint); + canvas.drawRenderNode(projectingRipple.get()); + }, + "C"); + auto parent = TestUtils::createSkiaNode( + 0, 0, 100, 100, + [&receiverBackground, &child](RenderProperties& properties, + SkiaRecordingCanvas& canvas) { + canvas.drawRenderNode(receiverBackground.get()); + canvas.drawRenderNode(child.get()); + }, + "A"); + ContextFactory contextFactory; + std::unique_ptr<CanvasContext> canvasContext( + CanvasContext::create(renderThread, false, parent.get(), &contextFactory)); + TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get()); + DamageAccumulator damageAccumulator; + info.damageAccumulator = &damageAccumulator; + parent->prepareTree(info); + + // parent(A) -> (receiverBackground, child) + // child(C) -> (rect[0, 0, 100, 100], projectingRipple) + // projectingRipple(P) -> (rect[0, 0, 100, 100]) -> projects backwards + // receiverBackground(B) -> (empty) -> projection receiver + + // create a canvas not backed by any device/pixels, but with dimensions to avoid quick rejection + ProjectionTestCanvas canvas(100, 100); + RenderNodeDrawable drawable(parent.get(), &canvas, true); + canvas.drawDrawable(&drawable); + EXPECT_EQ(2, canvas.getDrawCounter()); +} + +RENDERTHREAD_SKIA_PIPELINE_TEST(RenderNodeDrawable, projectionHwLayer) { /* R is backward projected on B and C is a layer. A / \ @@ -352,17 +433,17 @@ RENDERTHREAD_TEST(RenderNodeDrawable, projectionHwLayer) { class ProjectionTestCanvas : public SkCanvas { public: ProjectionTestCanvas(int* drawCounter) - : SkCanvas(CANVAS_WIDTH, CANVAS_HEIGHT) - , mDrawCounter(drawCounter) - {} + : SkCanvas(CANVAS_WIDTH, CANVAS_HEIGHT), mDrawCounter(drawCounter) {} void onDrawArc(const SkRect&, SkScalar startAngle, SkScalar sweepAngle, bool useCenter, - const SkPaint&) override { - EXPECT_EQ(0, (*mDrawCounter)++); //part of painting the layer - EXPECT_EQ(SkRect::MakeLTRB(0, 0, LAYER_WIDTH, LAYER_HEIGHT), TestUtils::getClipBounds(this)); + const SkPaint&) override { + EXPECT_EQ(0, (*mDrawCounter)++); // part of painting the layer + EXPECT_EQ(SkRect::MakeLTRB(0, 0, LAYER_WIDTH, LAYER_HEIGHT), + TestUtils::getClipBounds(this)); } void onDrawRect(const SkRect& rect, const SkPaint& paint) override { EXPECT_EQ(1, (*mDrawCounter)++); - EXPECT_EQ(SkRect::MakeLTRB(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT), TestUtils::getClipBounds(this)); + EXPECT_EQ(SkRect::MakeLTRB(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT), + TestUtils::getClipBounds(this)); } void onDrawOval(const SkRect&, const SkPaint&) override { EXPECT_EQ(2, (*mDrawCounter)++); @@ -377,69 +458,73 @@ RENDERTHREAD_TEST(RenderNodeDrawable, projectionHwLayer) { class ProjectionLayer : public SkSurface_Base { public: ProjectionLayer(int* drawCounter) - : SkSurface_Base(SkImageInfo::MakeN32Premul(LAYER_WIDTH, LAYER_HEIGHT), nullptr) - , mDrawCounter(drawCounter) { - } - void onDraw(SkCanvas*, SkScalar x, SkScalar y, const SkPaint*) override { + : SkSurface_Base(SkImageInfo::MakeN32Premul(LAYER_WIDTH, LAYER_HEIGHT), nullptr) + , mDrawCounter(drawCounter) {} + virtual sk_sp<SkImage> onNewImageSnapshot() override { EXPECT_EQ(3, (*mDrawCounter)++); EXPECT_EQ(SkRect::MakeLTRB(100 - SCROLL_X, 100 - SCROLL_Y, 300 - SCROLL_X, - 300 - SCROLL_Y), TestUtils::getClipBounds(this->getCanvas())); - } - SkCanvas* onNewCanvas() override { - return new ProjectionTestCanvas(mDrawCounter); - } - sk_sp<SkSurface> onNewSurface(const SkImageInfo&) override { - return nullptr; - } - sk_sp<SkImage> onNewImageSnapshot() override { + 300 - SCROLL_Y), + TestUtils::getClipBounds(this->getCanvas())); return nullptr; } + SkCanvas* onNewCanvas() override { return new ProjectionTestCanvas(mDrawCounter); } + sk_sp<SkSurface> onNewSurface(const SkImageInfo&) override { return nullptr; } void onCopyOnWrite(ContentChangeMode) override {} int* mDrawCounter; }; - auto receiverBackground = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, + auto receiverBackground = TestUtils::createSkiaNode( + 0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, [](RenderProperties& properties, SkiaRecordingCanvas& canvas) { - properties.setProjectionReceiver(true); - // scroll doesn't apply to background, so undone via translationX/Y - // NOTE: translationX/Y only! no other transform properties may be set for a proj receiver! - properties.setTranslationX(SCROLL_X); - properties.setTranslationY(SCROLL_Y); - - canvas.drawRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, SkPaint()); - }, "B"); //B - auto projectingRipple = TestUtils::createSkiaNode(0, 0, LAYER_WIDTH, LAYER_HEIGHT, + properties.setProjectionReceiver(true); + // scroll doesn't apply to background, so undone via translationX/Y + // NOTE: translationX/Y only! no other transform properties may be set for a proj + // receiver! + properties.setTranslationX(SCROLL_X); + properties.setTranslationY(SCROLL_Y); + + canvas.drawRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, SkPaint()); + }, + "B"); // B + auto projectingRipple = TestUtils::createSkiaNode( + 0, 0, LAYER_WIDTH, LAYER_HEIGHT, [](RenderProperties& properties, SkiaRecordingCanvas& canvas) { - properties.setProjectBackwards(true); - properties.setClipToBounds(false); - canvas.drawOval(100, 100, 300, 300, SkPaint()); // drawn mostly out of layer bounds - }, "R"); //R - auto child = TestUtils::createSkiaNode(100, 100, 300, 300, + properties.setProjectBackwards(true); + properties.setClipToBounds(false); + canvas.drawOval(100, 100, 300, 300, SkPaint()); // drawn mostly out of layer bounds + }, + "R"); // R + auto child = TestUtils::createSkiaNode( + 100, 100, 300, 300, [&projectingRipple](RenderProperties& properties, SkiaRecordingCanvas& canvas) { - canvas.drawRenderNode(projectingRipple.get()); - canvas.drawArc(0, 0, LAYER_WIDTH, LAYER_HEIGHT, 0.0f, 280.0f, true, SkPaint()); - }, "C"); //C - auto parent = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, + canvas.drawRenderNode(projectingRipple.get()); + canvas.drawArc(0, 0, LAYER_WIDTH, LAYER_HEIGHT, 0.0f, 280.0f, true, SkPaint()); + }, + "C"); // C + auto parent = TestUtils::createSkiaNode( + 0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, [&receiverBackground, &child](RenderProperties& properties, - SkiaRecordingCanvas& canvas) { - // Set a rect outline for the projecting ripple to be masked against. - properties.mutableOutline().setRoundRect(10, 10, 390, 390, 0, 1.0f); - canvas.translate(-SCROLL_X, -SCROLL_Y); // Apply scroll (note: bg undoes this internally) - canvas.drawRenderNode(receiverBackground.get()); - canvas.drawRenderNode(child.get()); - }, "A"); //A - - //prepareTree is required to find, which receivers have backward projected nodes + SkiaRecordingCanvas& canvas) { + // Set a rect outline for the projecting ripple to be masked against. + properties.mutableOutline().setRoundRect(10, 10, 390, 390, 0, 1.0f); + canvas.translate(-SCROLL_X, + -SCROLL_Y); // Apply scroll (note: bg undoes this internally) + canvas.drawRenderNode(receiverBackground.get()); + canvas.drawRenderNode(child.get()); + }, + "A"); // A + + // prepareTree is required to find, which receivers have backward projected nodes ContextFactory contextFactory; - std::unique_ptr<CanvasContext> canvasContext(CanvasContext::create( - renderThread, false, parent.get(), &contextFactory)); + std::unique_ptr<CanvasContext> canvasContext( + CanvasContext::create(renderThread, false, parent.get(), &contextFactory)); TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get()); DamageAccumulator damageAccumulator; info.damageAccumulator = &damageAccumulator; parent->prepareTree(info); int drawCounter = 0; - //set a layer after prepareTree to avoid layer logic there + // set a layer after prepareTree to avoid layer logic there child->animatorProperties().mutateLayerProperties().setType(LayerType::RenderLayer); sk_sp<SkSurface> surfaceLayer1(new ProjectionLayer(&drawCounter)); child->setLayerSurface(surfaceLayer1); @@ -449,9 +534,10 @@ RENDERTHREAD_TEST(RenderNodeDrawable, projectionHwLayer) { LayerUpdateQueue layerUpdateQueue; layerUpdateQueue.enqueueLayerWithDamage(child.get(), - android::uirenderer::Rect(LAYER_WIDTH, LAYER_HEIGHT)); - SkiaPipeline::renderLayersImpl(layerUpdateQueue, true, false); - EXPECT_EQ(1, drawCounter); //assert index 0 is drawn on the layer + android::uirenderer::Rect(LAYER_WIDTH, LAYER_HEIGHT)); + auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread); + pipeline->renderLayersImpl(layerUpdateQueue, true, false); + EXPECT_EQ(1, drawCounter); // assert index 0 is drawn on the layer RenderNodeDrawable drawable(parent.get(), surfaceLayer1->getCanvas(), true); surfaceLayer1->getCanvas()->drawDrawable(&drawable); @@ -488,40 +574,50 @@ RENDERTHREAD_TEST(RenderNodeDrawable, projectionChildScroll) { int mDrawCounter = 0; }; - auto receiverBackground = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, + auto receiverBackground = TestUtils::createSkiaNode( + 0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, [](RenderProperties& properties, SkiaRecordingCanvas& canvas) { - properties.setProjectionReceiver(true); - canvas.drawRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, SkPaint()); - }, "B"); //B - auto projectingRipple = TestUtils::createSkiaNode(0, 0, 200, 200, + properties.setProjectionReceiver(true); + canvas.drawRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, SkPaint()); + }, + "B"); // B + auto projectingRipple = TestUtils::createSkiaNode( + 0, 0, 200, 200, [](RenderProperties& properties, SkiaRecordingCanvas& canvas) { - // scroll doesn't apply to background, so undone via translationX/Y - // NOTE: translationX/Y only! no other transform properties may be set for a proj receiver! - properties.setTranslationX(SCROLL_X); - properties.setTranslationY(SCROLL_Y); - properties.setProjectBackwards(true); - properties.setClipToBounds(false); - canvas.drawOval(0, 0, 200, 200, SkPaint()); - }, "R"); //R - auto child = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, + // scroll doesn't apply to background, so undone via translationX/Y + // NOTE: translationX/Y only! no other transform properties may be set for a proj + // receiver! + properties.setTranslationX(SCROLL_X); + properties.setTranslationY(SCROLL_Y); + properties.setProjectBackwards(true); + properties.setClipToBounds(false); + canvas.drawOval(0, 0, 200, 200, SkPaint()); + }, + "R"); // R + auto child = TestUtils::createSkiaNode( + 0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, [&projectingRipple](RenderProperties& properties, SkiaRecordingCanvas& canvas) { - // Record time clip will be ignored by projectee - canvas.clipRect(100, 100, 300, 300, SkClipOp::kIntersect); - - canvas.translate(-SCROLL_X, -SCROLL_Y); // Apply scroll (note: bg undoes this internally) - canvas.drawRenderNode(projectingRipple.get()); - }, "C"); //C - auto parent = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, - [&receiverBackground, &child](RenderProperties& properties, - SkiaRecordingCanvas& canvas) { - canvas.drawRenderNode(receiverBackground.get()); - canvas.drawRenderNode(child.get()); - }, "A"); //A - - //prepareTree is required to find, which receivers have backward projected nodes + // Record time clip will be ignored by projectee + canvas.clipRect(100, 100, 300, 300, SkClipOp::kIntersect); + + canvas.translate(-SCROLL_X, + -SCROLL_Y); // Apply scroll (note: bg undoes this internally) + canvas.drawRenderNode(projectingRipple.get()); + }, + "C"); // C + auto parent = + TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, + [&receiverBackground, &child](RenderProperties& properties, + SkiaRecordingCanvas& canvas) { + canvas.drawRenderNode(receiverBackground.get()); + canvas.drawRenderNode(child.get()); + }, + "A"); // A + + // prepareTree is required to find, which receivers have backward projected nodes ContextFactory contextFactory; - std::unique_ptr<CanvasContext> canvasContext(CanvasContext::create( - renderThread, false, parent.get(), &contextFactory)); + std::unique_ptr<CanvasContext> canvasContext( + CanvasContext::create(renderThread, false, parent.get(), &contextFactory)); TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get()); DamageAccumulator damageAccumulator; info.damageAccumulator = &damageAccumulator; @@ -534,17 +630,16 @@ RENDERTHREAD_TEST(RenderNodeDrawable, projectionChildScroll) { } namespace { -static int drawNode(RenderThread& renderThread, const sp<RenderNode>& renderNode) -{ +static int drawNode(RenderThread& renderThread, const sp<RenderNode>& renderNode) { ContextFactory contextFactory; - std::unique_ptr<CanvasContext> canvasContext(CanvasContext::create( - renderThread, false, renderNode.get(), &contextFactory)); + std::unique_ptr<CanvasContext> canvasContext( + CanvasContext::create(renderThread, false, renderNode.get(), &contextFactory)); TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get()); DamageAccumulator damageAccumulator; info.damageAccumulator = &damageAccumulator; renderNode->prepareTree(info); - //create a canvas not backed by any device/pixels, but with dimensions to avoid quick rejection + // create a canvas not backed by any device/pixels, but with dimensions to avoid quick rejection ZReorderCanvas canvas(100, 100); RenderNodeDrawable drawable(renderNode.get(), &canvas, false); canvas.drawDrawable(&drawable); @@ -560,18 +655,18 @@ RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderProjectedInMiddle) { | R */ - auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100, - [](RenderProperties& props, SkiaRecordingCanvas& canvas) { + auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100, [](RenderProperties& props, + SkiaRecordingCanvas& canvas) { drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { props.setProjectionReceiver(true); - } ); //nodeB + }); // nodeB drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { props.setProjectBackwards(true); props.setClipToBounds(false); - } ); //nodeR - } ); //nodeC - }); //nodeA + }); // nodeR + }); // nodeC + }); // nodeA EXPECT_EQ(3, drawNode(renderThread, nodeA)); } @@ -584,19 +679,21 @@ RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderProjectLast) { | R */ - auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100, - [](RenderProperties& props, SkiaRecordingCanvas& canvas) { - drawOrderedNode(&canvas, 0, nullptr); //nodeB + auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100, [](RenderProperties& props, + SkiaRecordingCanvas& canvas) { + drawOrderedNode(&canvas, 0, nullptr); // nodeB drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { - drawOrderedNode(&canvas, 3, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //drawn as 2 + drawOrderedNode(&canvas, 3, [](RenderProperties& props, + SkiaRecordingCanvas& canvas) { // drawn as 2 props.setProjectBackwards(true); props.setClipToBounds(false); - } ); //nodeR - } ); //nodeC - drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //drawn as 3 - props.setProjectionReceiver(true); - } ); //nodeE - }); //nodeA + }); // nodeR + }); // nodeC + drawOrderedNode(&canvas, 2, + [](RenderProperties& props, SkiaRecordingCanvas& canvas) { // drawn as 3 + props.setProjectionReceiver(true); + }); // nodeE + }); // nodeA EXPECT_EQ(4, drawNode(renderThread, nodeA)); } @@ -608,17 +705,17 @@ RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderNoReceivable) { | R */ - auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100, - [](RenderProperties& props, SkiaRecordingCanvas& canvas) { - drawOrderedNode(&canvas, 0, nullptr); //nodeB + auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100, [](RenderProperties& props, + SkiaRecordingCanvas& canvas) { + drawOrderedNode(&canvas, 0, nullptr); // nodeB drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { drawOrderedNode(&canvas, 255, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { - //not having a projection receiver is an undefined behavior + // not having a projection receiver is an undefined behavior props.setProjectBackwards(true); props.setClipToBounds(false); - } ); //nodeR - } ); //nodeC - }); //nodeA + }); // nodeR + }); // nodeC + }); // nodeA EXPECT_EQ(2, drawNode(renderThread, nodeA)); } @@ -630,17 +727,17 @@ RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderParentReceivable) { | R */ - auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100, - [](RenderProperties& props, SkiaRecordingCanvas& canvas) { - drawOrderedNode(&canvas, 0, nullptr); //nodeB + auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100, [](RenderProperties& props, + SkiaRecordingCanvas& canvas) { + drawOrderedNode(&canvas, 0, nullptr); // nodeB drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { props.setProjectionReceiver(true); drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { props.setProjectBackwards(true); props.setClipToBounds(false); - } ); //nodeR - } ); //nodeC - }); //nodeA + }); // nodeR + }); // nodeC + }); // nodeA EXPECT_EQ(3, drawNode(renderThread, nodeA)); } @@ -652,22 +749,22 @@ RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderSameNodeReceivable) { | R */ - auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100, - [](RenderProperties& props, SkiaRecordingCanvas& canvas) { - drawOrderedNode(&canvas, 0, nullptr); //nodeB + auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100, [](RenderProperties& props, + SkiaRecordingCanvas& canvas) { + drawOrderedNode(&canvas, 0, nullptr); // nodeB drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { drawOrderedNode(&canvas, 255, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { - //having a node that is projected on itself is an undefined/unexpected behavior + // having a node that is projected on itself is an undefined/unexpected behavior props.setProjectionReceiver(true); props.setProjectBackwards(true); props.setClipToBounds(false); - } ); //nodeR - } ); //nodeC - }); //nodeA + }); // nodeR + }); // nodeC + }); // nodeA EXPECT_EQ(2, drawNode(renderThread, nodeA)); } -//Note: the outcome for this test is different in HWUI +// Note: the outcome for this test is different in HWUI RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderProjectedSibling) { /* R is set to project on B, but R is not drawn because projecting on a sibling is not allowed. A @@ -675,18 +772,18 @@ RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderProjectedSibling) { / | \ B C R */ - auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100, - [](RenderProperties& props, SkiaRecordingCanvas& canvas) { + auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100, [](RenderProperties& props, + SkiaRecordingCanvas& canvas) { drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { props.setProjectionReceiver(true); - } ); //nodeB - drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { - } ); //nodeC + }); // nodeB + drawOrderedNode(&canvas, 1, + [](RenderProperties& props, SkiaRecordingCanvas& canvas) {}); // nodeC drawOrderedNode(&canvas, 255, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { props.setProjectBackwards(true); props.setClipToBounds(false); - } ); //nodeR - }); //nodeA + }); // nodeR + }); // nodeA EXPECT_EQ(2, drawNode(renderThread, nodeA)); } @@ -699,20 +796,20 @@ RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderProjectedSibling2) { / | \ B C R */ - auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100, - [](RenderProperties& props, SkiaRecordingCanvas& canvas) { + auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100, [](RenderProperties& props, + SkiaRecordingCanvas& canvas) { drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { props.setProjectionReceiver(true); - } ); //nodeB - drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { - } ); //nodeC + }); // nodeB + drawOrderedNode(&canvas, 2, + [](RenderProperties& props, SkiaRecordingCanvas& canvas) {}); // nodeC drawOrderedNode(&canvas, 255, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { props.setProjectBackwards(true); props.setClipToBounds(false); - } ); //nodeR - } ); //nodeG - }); //nodeA + }); // nodeR + }); // nodeG + }); // nodeA EXPECT_EQ(3, drawNode(renderThread, nodeA)); } @@ -726,18 +823,19 @@ RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderGrandparentReceivable) { | R */ - auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100, - [](RenderProperties& props, SkiaRecordingCanvas& canvas) { + auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100, [](RenderProperties& props, + SkiaRecordingCanvas& canvas) { drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { props.setProjectionReceiver(true); drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { - drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { - props.setProjectBackwards(true); - props.setClipToBounds(false); - } ); //nodeR - } ); //nodeC - } ); //nodeB - }); //nodeA + drawOrderedNode(&canvas, 2, + [](RenderProperties& props, SkiaRecordingCanvas& canvas) { + props.setProjectBackwards(true); + props.setClipToBounds(false); + }); // nodeR + }); // nodeC + }); // nodeB + }); // nodeA EXPECT_EQ(3, drawNode(renderThread, nodeA)); } @@ -749,21 +847,23 @@ RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderTwoReceivables) { / \ G R */ - auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100, - [](RenderProperties& props, SkiaRecordingCanvas& canvas) { - drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //B + auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100, [](RenderProperties& props, + SkiaRecordingCanvas& canvas) { + drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { // B props.setProjectionReceiver(true); - } ); //nodeB - drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //C - drawOrderedNode(&canvas, 3, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //G - props.setProjectionReceiver(true); - } ); //nodeG - drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //R - props.setProjectBackwards(true); - props.setClipToBounds(false); - } ); //nodeR - } ); //nodeC - }); //nodeA + }); // nodeB + drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { // C + drawOrderedNode(&canvas, 3, + [](RenderProperties& props, SkiaRecordingCanvas& canvas) { // G + props.setProjectionReceiver(true); + }); // nodeG + drawOrderedNode(&canvas, 1, + [](RenderProperties& props, SkiaRecordingCanvas& canvas) { // R + props.setProjectBackwards(true); + props.setClipToBounds(false); + }); // nodeR + }); // nodeC + }); // nodeA EXPECT_EQ(4, drawNode(renderThread, nodeA)); } @@ -775,21 +875,23 @@ RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderTwoReceivablesLikelyScena / \ G R */ - auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100, - [](RenderProperties& props, SkiaRecordingCanvas& canvas) { - drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //B + auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100, [](RenderProperties& props, + SkiaRecordingCanvas& canvas) { + drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { // B props.setProjectionReceiver(true); - } ); //nodeB - drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //C - drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //G - props.setProjectionReceiver(true); - props.setProjectBackwards(true); - props.setClipToBounds(false); - } ); //nodeG - drawOrderedNode(&canvas, 3, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //R - } ); //nodeR - } ); //nodeC - }); //nodeA + }); // nodeB + drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { // C + drawOrderedNode(&canvas, 1, + [](RenderProperties& props, SkiaRecordingCanvas& canvas) { // G + props.setProjectionReceiver(true); + props.setProjectBackwards(true); + props.setClipToBounds(false); + }); // nodeG + drawOrderedNode(&canvas, 3, + [](RenderProperties& props, SkiaRecordingCanvas& canvas) { // R + }); // nodeR + }); // nodeC + }); // nodeA EXPECT_EQ(4, drawNode(renderThread, nodeA)); } @@ -803,23 +905,26 @@ RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderTwoReceivablesDeeper) { | R */ - auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100, - [](RenderProperties& props, SkiaRecordingCanvas& canvas) { - drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //B + auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100, [](RenderProperties& props, + SkiaRecordingCanvas& canvas) { + drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { // B props.setProjectionReceiver(true); - } ); //nodeB - drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //C - drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //G - props.setProjectionReceiver(true); - } ); //nodeG - drawOrderedNode(&canvas, 4, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //D - drawOrderedNode(&canvas, 3, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //R - props.setProjectBackwards(true); - props.setClipToBounds(false); - } ); //nodeR - } ); //nodeD - } ); //nodeC - }); //nodeA + }); // nodeB + drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { // C + drawOrderedNode(&canvas, 2, + [](RenderProperties& props, SkiaRecordingCanvas& canvas) { // G + props.setProjectionReceiver(true); + }); // nodeG + drawOrderedNode(&canvas, 4, + [](RenderProperties& props, SkiaRecordingCanvas& canvas) { // D + drawOrderedNode(&canvas, 3, [](RenderProperties& props, + SkiaRecordingCanvas& canvas) { // R + props.setProjectBackwards(true); + props.setClipToBounds(false); + }); // nodeR + }); // nodeD + }); // nodeC + }); // nodeA EXPECT_EQ(5, drawNode(renderThread, nodeA)); } @@ -828,8 +933,7 @@ RENDERTHREAD_TEST(RenderNodeDrawable, simple) { static const int CANVAS_HEIGHT = 200; class SimpleTestCanvas : public TestCanvasBase { public: - SimpleTestCanvas() : TestCanvasBase(CANVAS_WIDTH, CANVAS_HEIGHT) { - } + SimpleTestCanvas() : TestCanvasBase(CANVAS_WIDTH, CANVAS_HEIGHT) {} void onDrawRect(const SkRect& rect, const SkPaint& paint) override { EXPECT_EQ(0, mDrawCounter++); } @@ -839,11 +943,12 @@ RENDERTHREAD_TEST(RenderNodeDrawable, simple) { }; auto node = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, - [](RenderProperties& props, SkiaRecordingCanvas& canvas) { - sk_sp<Bitmap> bitmap(TestUtils::createBitmap(25, 25)); - canvas.drawRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, SkPaint()); - canvas.drawBitmap(*bitmap, 10, 10, nullptr); - }); + [](RenderProperties& props, SkiaRecordingCanvas& canvas) { + sk_sp<Bitmap> bitmap(TestUtils::createBitmap(25, 25)); + canvas.drawRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, + SkPaint()); + canvas.drawBitmap(*bitmap, 10, 10, nullptr); + }); SimpleTestCanvas canvas; RenderNodeDrawable drawable(node.get(), &canvas, true); @@ -856,33 +961,32 @@ RENDERTHREAD_TEST(RenderNodeDrawable, colorOp_unbounded) { static const int CANVAS_HEIGHT = 200; class ColorTestCanvas : public TestCanvasBase { public: - ColorTestCanvas() : TestCanvasBase(CANVAS_WIDTH, CANVAS_HEIGHT) { - } + ColorTestCanvas() : TestCanvasBase(CANVAS_WIDTH, CANVAS_HEIGHT) {} void onDrawPaint(const SkPaint&) { switch (mDrawCounter++) { - case 0: - EXPECT_EQ(SkRect::MakeWH(CANVAS_WIDTH, CANVAS_HEIGHT), - TestUtils::getClipBounds(this)); - break; - case 1: - EXPECT_EQ(SkRect::MakeWH(10, 10), TestUtils::getClipBounds(this)); - break; - default: - ADD_FAILURE(); + case 0: + EXPECT_EQ(SkRect::MakeWH(CANVAS_WIDTH, CANVAS_HEIGHT), + TestUtils::getClipBounds(this)); + break; + case 1: + EXPECT_EQ(SkRect::MakeWH(10, 10), TestUtils::getClipBounds(this)); + break; + default: + ADD_FAILURE(); } } }; - auto unclippedColorView = TestUtils::createSkiaNode(0, 0, 10, 10, - [](RenderProperties& props, SkiaRecordingCanvas& canvas) { - props.setClipToBounds(false); - canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver); - }); + auto unclippedColorView = TestUtils::createSkiaNode( + 0, 0, 10, 10, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { + props.setClipToBounds(false); + canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver); + }); - auto clippedColorView = TestUtils::createSkiaNode(0, 0, 10, 10, - [](RenderProperties& props, SkiaRecordingCanvas& canvas) { - canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver); - }); + auto clippedColorView = TestUtils::createSkiaNode( + 0, 0, 10, 10, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { + canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver); + }); ColorTestCanvas canvas; RenderNodeDrawable drawable(unclippedColorView.get(), &canvas, true); @@ -898,42 +1002,43 @@ TEST(RenderNodeDrawable, renderNode) { static const int CANVAS_HEIGHT = 200; class RenderNodeTestCanvas : public TestCanvasBase { public: - RenderNodeTestCanvas() : TestCanvasBase(CANVAS_WIDTH, CANVAS_HEIGHT) { - } + RenderNodeTestCanvas() : TestCanvasBase(CANVAS_WIDTH, CANVAS_HEIGHT) {} void onDrawRect(const SkRect& rect, const SkPaint& paint) override { - switch(mDrawCounter++) { - case 0: - EXPECT_EQ(SkRect::MakeWH(CANVAS_WIDTH, CANVAS_HEIGHT), TestUtils::getClipBounds(this)); - EXPECT_EQ(SK_ColorDKGRAY, paint.getColor()); - break; - case 1: - EXPECT_EQ(SkRect::MakeLTRB(50, 50, 150, 150), TestUtils::getClipBounds(this)); - EXPECT_EQ(SK_ColorWHITE, paint.getColor()); - break; - default: - ADD_FAILURE(); + switch (mDrawCounter++) { + case 0: + EXPECT_EQ(SkRect::MakeWH(CANVAS_WIDTH, CANVAS_HEIGHT), + TestUtils::getClipBounds(this)); + EXPECT_EQ(SK_ColorDKGRAY, paint.getColor()); + break; + case 1: + EXPECT_EQ(SkRect::MakeLTRB(50, 50, 150, 150), TestUtils::getClipBounds(this)); + EXPECT_EQ(SK_ColorWHITE, paint.getColor()); + break; + default: + ADD_FAILURE(); } } }; - auto child = TestUtils::createSkiaNode(10, 10, 110, 110, - [](RenderProperties& props, SkiaRecordingCanvas& canvas) { - SkPaint paint; - paint.setColor(SK_ColorWHITE); - canvas.drawRect(0, 0, 100, 100, paint); - }); + auto child = TestUtils::createSkiaNode( + 10, 10, 110, 110, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { + SkPaint paint; + paint.setColor(SK_ColorWHITE); + canvas.drawRect(0, 0, 100, 100, paint); + }); - auto parent = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, + auto parent = TestUtils::createSkiaNode( + 0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, [&child](RenderProperties& props, SkiaRecordingCanvas& canvas) { - SkPaint paint; - paint.setColor(SK_ColorDKGRAY); - canvas.drawRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, paint); - - canvas.save(SaveFlags::MatrixClip); - canvas.translate(40, 40); - canvas.drawRenderNode(child.get()); - canvas.restore(); - }); + SkPaint paint; + paint.setColor(SK_ColorDKGRAY); + canvas.drawRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, paint); + + canvas.save(SaveFlags::MatrixClip); + canvas.translate(40, 40); + canvas.drawRenderNode(child.get()); + canvas.restore(); + }); RenderNodeTestCanvas canvas; RenderNodeDrawable drawable(parent.get(), &canvas, true); @@ -941,6 +1046,39 @@ TEST(RenderNodeDrawable, renderNode) { EXPECT_EQ(2, canvas.mDrawCounter); } +// Verify that layers are composed with kLow_SkFilterQuality filter quality. +RENDERTHREAD_SKIA_PIPELINE_TEST(RenderNodeDrawable, layerComposeQuality) { + static const int CANVAS_WIDTH = 1; + static const int CANVAS_HEIGHT = 1; + static const int LAYER_WIDTH = 1; + static const int LAYER_HEIGHT = 1; + class FrameTestCanvas : public TestCanvasBase { + public: + FrameTestCanvas() : TestCanvasBase(CANVAS_WIDTH, CANVAS_HEIGHT) {} + void onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst, + const SkPaint* paint, SrcRectConstraint constraint) override { + mDrawCounter++; + EXPECT_EQ(kLow_SkFilterQuality, paint->getFilterQuality()); + } + }; + + auto layerNode = TestUtils::createSkiaNode( + 0, 0, LAYER_WIDTH, LAYER_HEIGHT, + [](RenderProperties& properties, SkiaRecordingCanvas& canvas) { + canvas.drawPaint(SkPaint()); + }); + + layerNode->animatorProperties().mutateLayerProperties().setType(LayerType::RenderLayer); + layerNode->setLayerSurface(SkSurface::MakeRasterN32Premul(LAYER_WIDTH, LAYER_HEIGHT)); + + FrameTestCanvas canvas; + RenderNodeDrawable drawable(layerNode.get(), &canvas, true); + canvas.drawDrawable(&drawable); + EXPECT_EQ(1, canvas.mDrawCounter); //make sure the layer was composed + + // clean up layer pointer, so we can safely destruct RenderNode + layerNode->setLayerSurface(nullptr); +} TEST(ReorderBarrierDrawable, testShadowMatrix) { static const int CANVAS_WIDTH = 100; @@ -952,7 +1090,6 @@ TEST(ReorderBarrierDrawable, testShadowMatrix) { static const float CASTER_WIDTH = 20.0f; static const float CASTER_HEIGHT = 20.0f; - class ShadowTestCanvas : public SkCanvas { public: ShadowTestCanvas(int width, int height) : SkCanvas(width, height) {} @@ -976,30 +1113,32 @@ TEST(ReorderBarrierDrawable, testShadowMatrix) { // matrix. mDrawCounter++; EXPECT_EQ(SkMatrix::MakeTrans(CASTER_X, CASTER_Y), matrix); - EXPECT_EQ(SkMatrix::MakeTrans(CASTER_X+TRANSLATE_X, CASTER_Y+TRANSLATE_Y), - getTotalMatrix()); + EXPECT_EQ(SkMatrix::MakeTrans(CASTER_X + TRANSLATE_X, CASTER_Y + TRANSLATE_Y), + getTotalMatrix()); } + protected: int mDrawCounter = 0; }; - auto parent = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, + auto parent = TestUtils::createSkiaNode( + 0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { - canvas.translate(TRANSLATE_X, TRANSLATE_Y); - canvas.insertReorderBarrier(true); - - auto node = TestUtils::createSkiaNode(CASTER_X, CASTER_Y, CASTER_X + CASTER_WIDTH, - CASTER_Y + CASTER_HEIGHT, - [](RenderProperties& props, SkiaRecordingCanvas& canvas) { - props.setElevation(42); - props.mutableOutline().setRoundRect(0, 0, 20, 20, 5, 1); - props.mutableOutline().setShouldClip(true); - }); - canvas.drawRenderNode(node.get()); - canvas.insertReorderBarrier(false); - }); + canvas.translate(TRANSLATE_X, TRANSLATE_Y); + canvas.insertReorderBarrier(true); + + auto node = TestUtils::createSkiaNode( + CASTER_X, CASTER_Y, CASTER_X + CASTER_WIDTH, CASTER_Y + CASTER_HEIGHT, + [](RenderProperties& props, SkiaRecordingCanvas& canvas) { + props.setElevation(42); + props.mutableOutline().setRoundRect(0, 0, 20, 20, 5, 1); + props.mutableOutline().setShouldClip(true); + }); + canvas.drawRenderNode(node.get()); + canvas.insertReorderBarrier(false); + }); - //create a canvas not backed by any device/pixels, but with dimensions to avoid quick rejection + // create a canvas not backed by any device/pixels, but with dimensions to avoid quick rejection ShadowTestCanvas canvas(CANVAS_WIDTH, CANVAS_HEIGHT); RenderNodeDrawable drawable(parent.get(), &canvas, false); canvas.drawDrawable(&drawable); diff --git a/libs/hwui/tests/unit/RenderNodeTests.cpp b/libs/hwui/tests/unit/RenderNodeTests.cpp index eda4a9de49ae..079520836bb5 100644 --- a/libs/hwui/tests/unit/RenderNodeTests.cpp +++ b/libs/hwui/tests/unit/RenderNodeTests.cpp @@ -14,8 +14,8 @@ * limitations under the License. */ -#include <gtest/gtest.h> #include <VectorDrawable.h> +#include <gtest/gtest.h> #include "AnimationContext.h" #include "DamageAccumulator.h" @@ -32,21 +32,20 @@ using namespace android::uirenderer::renderthread; class ContextFactory : public android::uirenderer::IContextFactory { public: - android::uirenderer::AnimationContext* createAnimationContext - (android::uirenderer::renderthread::TimeLord& clock) override { + android::uirenderer::AnimationContext* createAnimationContext( + android::uirenderer::renderthread::TimeLord& clock) override { return new android::uirenderer::AnimationContext(clock); } }; TEST(RenderNode, hasParents) { - auto child = TestUtils::createNode(0, 0, 200, 400, - [](RenderProperties& props, Canvas& canvas) { + auto child = TestUtils::createNode(0, 0, 200, 400, [](RenderProperties& props, Canvas& canvas) { canvas.drawColor(Color::Red_500, SkBlendMode::kSrcOver); }); auto parent = TestUtils::createNode(0, 0, 200, 400, - [&child](RenderProperties& props, Canvas& canvas) { - canvas.drawRenderNode(child.get()); - }); + [&child](RenderProperties& props, Canvas& canvas) { + canvas.drawRenderNode(child.get()); + }); TestUtils::syncHierarchyPropertiesAndDisplayList(parent); @@ -67,14 +66,13 @@ TEST(RenderNode, hasParents) { } TEST(RenderNode, validity) { - auto child = TestUtils::createNode(0, 0, 200, 400, - [](RenderProperties& props, Canvas& canvas) { + auto child = TestUtils::createNode(0, 0, 200, 400, [](RenderProperties& props, Canvas& canvas) { canvas.drawColor(Color::Red_500, SkBlendMode::kSrcOver); }); auto parent = TestUtils::createNode(0, 0, 200, 400, - [&child](RenderProperties& props, Canvas& canvas) { - canvas.drawRenderNode(child.get()); - }); + [&child](RenderProperties& props, Canvas& canvas) { + canvas.drawRenderNode(child.get()); + }); EXPECT_TRUE(child->isValid()); EXPECT_TRUE(parent->isValid()); @@ -111,9 +109,8 @@ TEST(RenderNode, validity) { EXPECT_TRUE(child->isValid()); EXPECT_TRUE(child->nothingToDraw()); - TestUtils::recordNode(*parent, [&child](Canvas& canvas) { - canvas.drawRenderNode(child.get()); - }); + TestUtils::recordNode(*parent, + [&child](Canvas& canvas) { canvas.drawRenderNode(child.get()); }); TestUtils::syncHierarchyPropertiesAndDisplayList(parent); @@ -131,18 +128,17 @@ TEST(RenderNode, validity) { } TEST(RenderNode, multiTreeValidity) { - auto child = TestUtils::createNode(0, 0, 200, 400, - [](RenderProperties& props, Canvas& canvas) { + auto child = TestUtils::createNode(0, 0, 200, 400, [](RenderProperties& props, Canvas& canvas) { canvas.drawColor(Color::Red_500, SkBlendMode::kSrcOver); }); auto parent1 = TestUtils::createNode(0, 0, 200, 400, - [&child](RenderProperties& props, Canvas& canvas) { - canvas.drawRenderNode(child.get()); - }); + [&child](RenderProperties& props, Canvas& canvas) { + canvas.drawRenderNode(child.get()); + }); auto parent2 = TestUtils::createNode(0, 0, 200, 400, - [&child](RenderProperties& props, Canvas& canvas) { - canvas.drawRenderNode(child.get()); - }); + [&child](RenderProperties& props, Canvas& canvas) { + canvas.drawRenderNode(child.get()); + }); EXPECT_TRUE(child->isValid()); EXPECT_TRUE(parent1->isValid()); @@ -200,14 +196,12 @@ TEST(RenderNode, multiTreeValidity) { }); TestUtils::syncHierarchyPropertiesAndDisplayList(child); - TestUtils::recordNode(*parent1, [&child](Canvas& canvas) { - canvas.drawRenderNode(child.get()); - }); + TestUtils::recordNode(*parent1, + [&child](Canvas& canvas) { canvas.drawRenderNode(child.get()); }); TestUtils::syncHierarchyPropertiesAndDisplayList(parent1); - TestUtils::recordNode(*parent2, [&child](Canvas& canvas) { - canvas.drawRenderNode(child.get()); - }); + TestUtils::recordNode(*parent2, + [&child](Canvas& canvas) { canvas.drawRenderNode(child.get()); }); TestUtils::syncHierarchyPropertiesAndDisplayList(parent2); EXPECT_TRUE(child->isValid()); @@ -240,9 +234,8 @@ TEST(RenderNode, releasedCallback) { class DecRefOnReleased : public GlFunctorLifecycleListener { public: explicit DecRefOnReleased(int* refcnt) : mRefCnt(refcnt) {} - void onGlFunctorReleased(Functor* functor) override { - *mRefCnt -= 1; - } + void onGlFunctorReleased(Functor* functor) override { *mRefCnt -= 1; } + private: int* mRefCnt; }; @@ -251,8 +244,7 @@ TEST(RenderNode, releasedCallback) { sp<DecRefOnReleased> listener(new DecRefOnReleased(&refcnt)); Functor noopFunctor; - auto node = TestUtils::createNode(0, 0, 200, 400, - [&](RenderProperties& props, Canvas& canvas) { + auto node = TestUtils::createNode(0, 0, 200, 400, [&](RenderProperties& props, Canvas& canvas) { refcnt++; canvas.callDrawGLFunction(&noopFunctor, listener.get()); }); @@ -277,17 +269,17 @@ TEST(RenderNode, releasedCallback) { RENDERTHREAD_TEST(RenderNode, prepareTree_nullableDisplayList) { auto rootNode = TestUtils::createNode(0, 0, 200, 400, nullptr); ContextFactory contextFactory; - std::unique_ptr<CanvasContext> canvasContext(CanvasContext::create( - renderThread, false, rootNode.get(), &contextFactory)); + std::unique_ptr<CanvasContext> canvasContext( + CanvasContext::create(renderThread, false, rootNode.get(), &contextFactory)); TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get()); DamageAccumulator damageAccumulator; info.damageAccumulator = &damageAccumulator; { - auto nonNullDLNode = TestUtils::createNode(0, 0, 200, 400, - [](RenderProperties& props, Canvas& canvas) { - canvas.drawColor(Color::Red_500, SkBlendMode::kSrcOver); - }); + auto nonNullDLNode = + TestUtils::createNode(0, 0, 200, 400, [](RenderProperties& props, Canvas& canvas) { + canvas.drawColor(Color::Red_500, SkBlendMode::kSrcOver); + }); TestUtils::syncHierarchyPropertiesAndDisplayList(nonNullDLNode); EXPECT_TRUE(nonNullDLNode->getDisplayList()); nonNullDLNode->prepareTree(info); @@ -304,17 +296,16 @@ RENDERTHREAD_TEST(RenderNode, prepareTree_nullableDisplayList) { } RENDERTHREAD_TEST(RenderNode, prepareTree_HwLayer_AVD_enqueueDamage) { - VectorDrawable::Group* group = new VectorDrawable::Group(); sp<VectorDrawableRoot> vectorDrawable(new VectorDrawableRoot(group)); - auto rootNode = TestUtils::createNode(0, 0, 200, 400, - [&](RenderProperties& props, Canvas& canvas) { - canvas.drawVectorDrawable(vectorDrawable.get()); - }); + auto rootNode = + TestUtils::createNode(0, 0, 200, 400, [&](RenderProperties& props, Canvas& canvas) { + canvas.drawVectorDrawable(vectorDrawable.get()); + }); ContextFactory contextFactory; - std::unique_ptr<CanvasContext> canvasContext(CanvasContext::create( - renderThread, false, rootNode.get(), &contextFactory)); + std::unique_ptr<CanvasContext> canvasContext( + CanvasContext::create(renderThread, false, rootNode.get(), &contextFactory)); TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get()); DamageAccumulator damageAccumulator; LayerUpdateQueue layerUpdateQueue; diff --git a/libs/hwui/tests/unit/ShaderCacheTests.cpp b/libs/hwui/tests/unit/ShaderCacheTests.cpp new file mode 100644 index 000000000000..43080a9460b3 --- /dev/null +++ b/libs/hwui/tests/unit/ShaderCacheTests.cpp @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gtest/gtest.h> +#include <dirent.h> +#include <cutils/properties.h> +#include <cstdint> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/types.h> +#include <utils/Log.h> +#include "pipeline/skia/ShaderCache.h" +#include "FileBlobCache.h" + +using namespace android::uirenderer::skiapipeline; + +namespace android { +namespace uirenderer { +namespace skiapipeline { + +class ShaderCacheTestUtils { +public: + /** + * "setSaveDelay" sets the time in seconds to wait before saving newly inserted cache entries. + * If set to 0, then deferred save is disabled. + */ + static void setSaveDelay(ShaderCache& cache, unsigned int saveDelay) { + cache.mDeferredSaveDelay = saveDelay; + } + + /** + * "terminate" optionally stores the BlobCache on disk and release all in-memory cache. + * Next call to "initShaderDiskCache" will load again the in-memory cache from disk. + */ + static void terminate(ShaderCache& cache, bool saveContent) { + std::lock_guard<std::mutex> lock(cache.mMutex); + if (cache.mInitialized && cache.mBlobCache && saveContent) { + cache.mBlobCache->writeToFile(); + } + cache.mBlobCache = NULL; + } +}; + +} /* namespace skiapipeline */ +} /* namespace uirenderer */ +} /* namespace android */ + + +namespace { + +std::string getExternalStorageFolder() { + return getenv("EXTERNAL_STORAGE"); +} + +bool folderExist(const std::string& folderName) { + DIR* dir = opendir(folderName.c_str()); + if (dir) { + closedir(dir); + return true; + } + return false; +} + +bool checkShader(const sk_sp<SkData>& shader, const char* program) { + sk_sp<SkData> shader2 = SkData::MakeWithCString(program); + return shader->size() == shader2->size() + && 0 == memcmp(shader->data(), shader2->data(), shader->size()); +} + +bool checkShader(const sk_sp<SkData>& shader, std::vector<char>& program) { + sk_sp<SkData> shader2 = SkData::MakeWithCopy(program.data(), program.size()); + return shader->size() == shader2->size() + && 0 == memcmp(shader->data(), shader2->data(), shader->size()); +} + +void setShader(sk_sp<SkData>& shader, const char* program) { + shader = SkData::MakeWithCString(program); +} + +void setShader(sk_sp<SkData>& shader, std::vector<char>& program) { + shader = SkData::MakeWithCopy(program.data(), program.size()); +} + + + +#define GrProgramDescTest(a) (*SkData::MakeWithCString(#a).get()) + +TEST(ShaderCacheTest, testWriteAndRead) { + if (!folderExist(getExternalStorageFolder())) { + //don't run the test if external storage folder is not available + return; + } + std::string cacheFile1 = getExternalStorageFolder() + "/shaderCacheTest1"; + std::string cacheFile2 = getExternalStorageFolder() + "/shaderCacheTest2"; + + //remove any test files from previous test run + int deleteFile = remove(cacheFile1.c_str()); + ASSERT_TRUE(0 == deleteFile || ENOENT == errno); + + //read the cache from a file that does not exist + ShaderCache::get().setFilename(cacheFile1.c_str()); + ShaderCacheTestUtils::setSaveDelay(ShaderCache::get(), 0); //disable deferred save + ShaderCache::get().initShaderDiskCache(); + + //read a key - should not be found since the cache is empty + sk_sp<SkData> outVS; + ASSERT_EQ(ShaderCache::get().load(GrProgramDescTest(432)), sk_sp<SkData>()); + + //write to the in-memory cache without storing on disk and verify we read the same values + sk_sp<SkData> inVS; + setShader(inVS, "sassas"); + ShaderCache::get().store(GrProgramDescTest(100), *inVS.get()); + setShader(inVS, "someVS"); + ShaderCache::get().store(GrProgramDescTest(432), *inVS.get()); + ASSERT_NE((outVS = ShaderCache::get().load(GrProgramDescTest(100))), sk_sp<SkData>()); + ASSERT_TRUE(checkShader(outVS, "sassas")); + ASSERT_NE((outVS = ShaderCache::get().load(GrProgramDescTest(432))), sk_sp<SkData>()); + ASSERT_TRUE(checkShader(outVS, "someVS")); + + //store content to disk and release in-memory cache + ShaderCacheTestUtils::terminate(ShaderCache::get(), true); + + //change to a file that does not exist and verify load fails + ShaderCache::get().setFilename(cacheFile2.c_str()); + ShaderCache::get().initShaderDiskCache(); + ASSERT_EQ(ShaderCache::get().load(GrProgramDescTest(432)), sk_sp<SkData>()); + ShaderCacheTestUtils::terminate(ShaderCache::get(), false); + + //load again content from disk from an existing file and check the data is read correctly + ShaderCache::get().setFilename(cacheFile1.c_str()); + ShaderCache::get().initShaderDiskCache(); + sk_sp<SkData> outVS2; + ASSERT_NE((outVS2 = ShaderCache::get().load(GrProgramDescTest(432))), sk_sp<SkData>()); + ASSERT_TRUE(checkShader(outVS2, "someVS")); + + //change data, store to disk, read back again and verify data has been changed + setShader(inVS, "ewData1"); + ShaderCache::get().store(GrProgramDescTest(432), *inVS.get()); + ShaderCacheTestUtils::terminate(ShaderCache::get(), true); + ShaderCache::get().initShaderDiskCache(); + ASSERT_NE((outVS2 = ShaderCache::get().load(GrProgramDescTest(432))), sk_sp<SkData>()); + ASSERT_TRUE(checkShader(outVS2, "ewData1")); + + + //write and read big data chunk (50K) + size_t dataSize = 50*1024; + std::vector<char> dataBuffer(dataSize); + for (size_t i = 0; i < dataSize; i++) { + dataBuffer[0] = dataSize % 256; + } + setShader(inVS, dataBuffer); + ShaderCache::get().store(GrProgramDescTest(432), *inVS.get()); + ShaderCacheTestUtils::terminate(ShaderCache::get(), true); + ShaderCache::get().initShaderDiskCache(); + ASSERT_NE((outVS2 = ShaderCache::get().load(GrProgramDescTest(432))), sk_sp<SkData>()); + ASSERT_TRUE(checkShader(outVS2, dataBuffer)); + + ShaderCacheTestUtils::terminate(ShaderCache::get(), false); + remove(cacheFile1.c_str()); +} + +} // namespace diff --git a/libs/hwui/tests/unit/SkiaBehaviorTests.cpp b/libs/hwui/tests/unit/SkiaBehaviorTests.cpp index 85b12ba79a8c..bc742b0c5a63 100644 --- a/libs/hwui/tests/unit/SkiaBehaviorTests.cpp +++ b/libs/hwui/tests/unit/SkiaBehaviorTests.cpp @@ -16,13 +16,13 @@ #include "tests/common/TestUtils.h" -#include <gtest/gtest.h> #include <SkBlurDrawLooper.h> #include <SkColorMatrixFilter.h> #include <SkColorSpace.h> #include <SkImagePriv.h> #include <SkPathOps.h> #include <SkShader.h> +#include <gtest/gtest.h> using namespace android; using namespace android::uirenderer; @@ -42,15 +42,13 @@ SkBitmap createSkBitmap(int width, int height) { TEST(SkiaBehavior, CreateBitmapShader1x1) { SkBitmap origBitmap = createSkBitmap(1, 1); sk_sp<SkImage> image = SkMakeImageFromRasterBitmap(origBitmap, kNever_SkCopyPixelsMode); - sk_sp<SkShader> s = image->makeShader( - SkShader::kClamp_TileMode, - SkShader::kRepeat_TileMode, - nullptr); + sk_sp<SkShader> s = + image->makeShader(SkShader::kClamp_TileMode, SkShader::kRepeat_TileMode, nullptr); SkBitmap bitmap; SkShader::TileMode xy[2]; ASSERT_TRUE(s->isABitmap(&bitmap, nullptr, xy)) - << "1x1 bitmap shader must query as bitmap shader"; + << "1x1 bitmap shader must query as bitmap shader"; EXPECT_EQ(origBitmap.pixelRef(), bitmap.pixelRef()); } @@ -63,8 +61,7 @@ TEST(SkiaBehavior, genIds) { TEST(SkiaBehavior, lightingColorFilter_simplify) { { - sk_sp<SkColorFilter> filter( - SkColorMatrixFilter::MakeLightingFilter(0x11223344, 0)); + sk_sp<SkColorFilter> filter(SkColorMatrixFilter::MakeLightingFilter(0x11223344, 0)); SkColor observedColor; SkBlendMode observedMode; @@ -74,8 +71,7 @@ TEST(SkiaBehavior, lightingColorFilter_simplify) { } { - sk_sp<SkColorFilter> failFilter( - SkColorMatrixFilter::MakeLightingFilter(0x11223344, 0x1)); + sk_sp<SkColorFilter> failFilter(SkColorMatrixFilter::MakeLightingFilter(0x11223344, 0x1)); EXPECT_FALSE(failFilter->asColorMode(nullptr, nullptr)); } } diff --git a/libs/hwui/tests/unit/SkiaCanvasTests.cpp b/libs/hwui/tests/unit/SkiaCanvasTests.cpp index c048dda4a2e9..1d7dc3d06ee4 100644 --- a/libs/hwui/tests/unit/SkiaCanvasTests.cpp +++ b/libs/hwui/tests/unit/SkiaCanvasTests.cpp @@ -16,12 +16,12 @@ #include "tests/common/TestUtils.h" -#include <gtest/gtest.h> #include <RecordingCanvas.h> #include <SkBlurDrawLooper.h> #include <SkCanvasStateUtils.h> #include <SkPicture.h> #include <SkPictureRecorder.h> +#include <gtest/gtest.h> using namespace android; using namespace android::uirenderer; @@ -36,7 +36,6 @@ OPENGL_PIPELINE_TEST(SkiaCanvasProxy, drawGlyphsViaPicture) { SkPaint paint; paint.setAntiAlias(true); paint.setTextSize(20); - paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); static const char* text = "testing text bounds"; // draw text directly into Recording canvas @@ -45,8 +44,7 @@ OPENGL_PIPELINE_TEST(SkiaCanvasProxy, drawGlyphsViaPicture) { // record the same text draw into a SkPicture and replay it into a Recording canvas SkPictureRecorder recorder; SkCanvas* skCanvas = recorder.beginRecording(200, 200, NULL, 0); - std::unique_ptr<Canvas> pictCanvas(Canvas::create_canvas(skCanvas, - Canvas::XformToSRGB::kDefer)); + std::unique_ptr<Canvas> pictCanvas(Canvas::create_canvas(skCanvas)); TestUtils::drawUtf8ToCanvas(pictCanvas.get(), text, paint, 25, 25); sk_sp<SkPicture> picture = recorder.finishRecordingAsPicture(); @@ -65,7 +63,7 @@ OPENGL_PIPELINE_TEST(SkiaCanvasProxy, drawGlyphsViaPicture) { TEST(SkiaCanvas, drawShadowLayer) { auto surface = SkSurface::MakeRasterN32Premul(10, 10); - SkiaCanvas canvas(surface->getCanvas(), Canvas::XformToSRGB::kDefer); + SkiaCanvas canvas(surface->getCanvas()); // clear to white canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrc); @@ -89,7 +87,7 @@ TEST(SkiaCanvas, colorSpaceXform) { sk_sp<Bitmap> adobeBitmap = Bitmap::allocateHeapBitmap(adobeInfo); SkBitmap adobeSkBitmap; adobeBitmap->getSkBitmap(&adobeSkBitmap); - *adobeSkBitmap.getAddr32(0, 0) = 0xFF0000F0; // Opaque, almost fully-red + *adobeSkBitmap.getAddr32(0, 0) = 0xFF0000F0; // Opaque, almost fully-red SkImageInfo info = adobeInfo.makeColorSpace(nullptr); sk_sp<Bitmap> bitmap = Bitmap::allocateHeapBitmap(info); @@ -108,27 +106,14 @@ TEST(SkiaCanvas, colorSpaceXform) { // The result should be less than fully red, since we convert to Adobe RGB at draw time. ASSERT_EQ(0xFF0000DC, *adobeSkBitmap.getAddr32(0, 0)); - // Now try in kDefer mode. This is a little strange given that, in practice, all software - // canvases are kImmediate. - SkCanvas skCanvas(skBitmap); - SkiaCanvas deferCanvas(&skCanvas, Canvas::XformToSRGB::kDefer); - deferCanvas.drawBitmap(*adobeBitmap, 0, 0, nullptr); - // The result should be as before, since we deferred the conversion to sRGB. - ASSERT_EQ(0xFF0000DC, *skBitmap.getAddr32(0, 0)); - - // Test picture recording. We will kDefer the xform at recording time, but handle it when - // we playback to the software canvas. + // Test picture recording. SkPictureRecorder recorder; SkCanvas* skPicCanvas = recorder.beginRecording(1, 1, NULL, 0); - SkiaCanvas picCanvas(skPicCanvas, Canvas::XformToSRGB::kDefer); + SkiaCanvas picCanvas(skPicCanvas); picCanvas.drawBitmap(*adobeBitmap, 0, 0, nullptr); sk_sp<SkPicture> picture = recorder.finishRecordingAsPicture(); - // Playback to a deferred canvas. The result should be as before. - deferCanvas.asSkCanvas()->drawPicture(picture); - ASSERT_EQ(0xFF0000DC, *skBitmap.getAddr32(0, 0)); - - // Playback to an immediate canvas. The result should be fully red. + // Playback to an software canvas. The result should be fully red. canvas.asSkCanvas()->drawPicture(picture); ASSERT_EQ(0xFF0000FF, *skBitmap.getAddr32(0, 0)); } @@ -155,7 +140,7 @@ TEST(SkiaCanvas, captureCanvasState) { // Create a picture canvas. SkPictureRecorder recorder; SkCanvas* skPicCanvas = recorder.beginRecording(1, 1, NULL, 0); - SkiaCanvas picCanvas(skPicCanvas, Canvas::XformToSRGB::kDefer); + SkiaCanvas picCanvas(skPicCanvas); state = picCanvas.captureCanvasState(); // Verify that we cannot get the CanvasState. diff --git a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp index dd8f4b4ec8cc..88d6dcf990a5 100644 --- a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp +++ b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp @@ -14,8 +14,8 @@ * limitations under the License. */ -#include <gtest/gtest.h> #include <VectorDrawable.h> +#include <gtest/gtest.h> #include "AnimationContext.h" #include "DamageAccumulator.h" @@ -79,7 +79,7 @@ TEST(SkiaDisplayList, reuseDisplayList) { // detach the list that you just attempted to reuse availableList = renderNode->detachAvailableList(); ASSERT_EQ(availableList.get(), &skiaDL); - availableList.release(); // prevents an invalid free since our DL is stack allocated + availableList.release(); // prevents an invalid free since our DL is stack allocated // after detaching there should return no available list availableList = renderNode->detachAvailableList(); @@ -115,8 +115,8 @@ public: RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaDisplayList, prepareListAndChildren) { auto rootNode = TestUtils::createNode(0, 0, 200, 400, nullptr); ContextFactory contextFactory; - std::unique_ptr<CanvasContext> canvasContext(CanvasContext::create( - renderThread, false, rootNode.get(), &contextFactory)); + std::unique_ptr<CanvasContext> canvasContext( + CanvasContext::create(renderThread, false, rootNode.get(), &contextFactory)); TreeInfo info(TreeInfo::MODE_FULL, *canvasContext.get()); DamageAccumulator damageAccumulator; info.damageAccumulator = &damageAccumulator; @@ -126,13 +126,13 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaDisplayList, prepareListAndChildren) { // prepare with a clean VD VectorDrawableRoot cleanVD(new VectorDrawable::Group()); skiaDL.mVectorDrawables.push_back(&cleanVD); - cleanVD.getBitmapUpdateIfDirty(); // this clears the dirty bit + cleanVD.getBitmapUpdateIfDirty(); // this clears the dirty bit ASSERT_FALSE(cleanVD.isDirty()); ASSERT_FALSE(cleanVD.getPropertyChangeWillBeConsumed()); TestUtils::MockTreeObserver observer; ASSERT_FALSE(skiaDL.prepareListAndChildren(observer, info, false, - [](RenderNode*, TreeObserver&, TreeInfo&, bool) {})); + [](RenderNode*, TreeObserver&, TreeInfo&, bool) {})); ASSERT_TRUE(cleanVD.getPropertyChangeWillBeConsumed()); // prepare again this time adding a dirty VD @@ -142,7 +142,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaDisplayList, prepareListAndChildren) { ASSERT_TRUE(dirtyVD.isDirty()); ASSERT_FALSE(dirtyVD.getPropertyChangeWillBeConsumed()); ASSERT_TRUE(skiaDL.prepareListAndChildren(observer, info, false, - [](RenderNode*, TreeObserver&, TreeInfo&, bool) {})); + [](RenderNode*, TreeObserver&, TreeInfo&, bool) {})); ASSERT_TRUE(dirtyVD.getPropertyChangeWillBeConsumed()); // prepare again this time adding a RenderNode and a callback @@ -151,13 +151,15 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaDisplayList, prepareListAndChildren) { SkCanvas dummyCanvas; skiaDL.mChildNodes.emplace_back(renderNode.get(), &dummyCanvas); bool hasRun = false; - ASSERT_TRUE(skiaDL.prepareListAndChildren(observer, info, false, - [&hasRun, renderNode, infoPtr](RenderNode* n, TreeObserver& observer, TreeInfo& i, bool r) { - hasRun = true; - ASSERT_EQ(renderNode.get(), n); - ASSERT_EQ(infoPtr, &i); - ASSERT_FALSE(r); - })); + ASSERT_TRUE(skiaDL.prepareListAndChildren( + observer, info, false, + [&hasRun, renderNode, infoPtr](RenderNode* n, TreeObserver& observer, TreeInfo& i, + bool r) { + hasRun = true; + ASSERT_EQ(renderNode.get(), n); + ASSERT_EQ(infoPtr, &i); + ASSERT_FALSE(r); + })); ASSERT_TRUE(hasRun); canvasContext->destroy(); @@ -169,7 +171,5 @@ TEST(SkiaDisplayList, updateChildren) { sp<RenderNode> renderNode = new RenderNode(); SkCanvas dummyCanvas; skiaDL.mChildNodes.emplace_back(renderNode.get(), &dummyCanvas); - skiaDL.updateChildren([renderNode](RenderNode* n) { - ASSERT_EQ(renderNode.get(), n); - }); + skiaDL.updateChildren([renderNode](RenderNode* n) { ASSERT_EQ(renderNode.get(), n); }); } diff --git a/libs/hwui/tests/unit/SkiaPipelineTests.cpp b/libs/hwui/tests/unit/SkiaPipelineTests.cpp index b397b151ad76..8fdb0e347afa 100644 --- a/libs/hwui/tests/unit/SkiaPipelineTests.cpp +++ b/libs/hwui/tests/unit/SkiaPipelineTests.cpp @@ -14,22 +14,22 @@ * limitations under the License. */ -#include <gtest/gtest.h> #include <VectorDrawable.h> +#include <gtest/gtest.h> +#include <SkClipStack.h> +#include <SkLiteRecorder.h> +#include <SkSurface_Base.h> +#include <string.h> #include "AnimationContext.h" #include "DamageAccumulator.h" #include "IContextFactory.h" +#include "SkiaCanvas.h" #include "pipeline/skia/SkiaDisplayList.h" -#include "pipeline/skia/SkiaRecordingCanvas.h" #include "pipeline/skia/SkiaOpenGLPipeline.h" +#include "pipeline/skia/SkiaRecordingCanvas.h" #include "renderthread/CanvasContext.h" #include "tests/common/TestUtils.h" -#include "SkiaCanvas.h" -#include <SkClipStack.h> -#include <SkLiteRecorder.h> -#include <SkSurface_Base.h> -#include <string.h> using namespace android; using namespace android::uirenderer; @@ -37,10 +37,31 @@ using namespace android::uirenderer::renderthread; using namespace android::uirenderer::skiapipeline; RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderFrame) { - auto redNode = TestUtils::createSkiaNode(0, 0, 1, 1, - [](RenderProperties& props, SkiaRecordingCanvas& redCanvas) { - redCanvas.drawColor(SK_ColorRED, SkBlendMode::kSrcOver); - }); + auto redNode = TestUtils::createSkiaNode( + 0, 0, 1, 1, [](RenderProperties& props, SkiaRecordingCanvas& redCanvas) { + redCanvas.drawColor(SK_ColorRED, SkBlendMode::kSrcOver); + }); + LayerUpdateQueue layerUpdateQueue; + SkRect dirty = SkRect::MakeLargest(); + std::vector<sp<RenderNode>> renderNodes; + renderNodes.push_back(redNode); + bool opaque = true; + android::uirenderer::Rect contentDrawBounds(0, 0, 1, 1); + auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread); + auto surface = SkSurface::MakeRasterN32Premul(1, 1); + surface->getCanvas()->drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver); + ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE); + pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, false, contentDrawBounds, + surface); + ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorRED); +} + +RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, testOnPrepareTree) { + auto redNode = TestUtils::createSkiaNode( + 0, 0, 1, 1, [](RenderProperties& props, SkiaRecordingCanvas& redCanvas) { + redCanvas.drawColor(SK_ColorRED, SkBlendMode::kSrcOver); + }); + LayerUpdateQueue layerUpdateQueue; SkRect dirty = SkRect::MakeLargest(); std::vector<sp<RenderNode>> renderNodes; @@ -48,22 +69,34 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderFrame) { bool opaque = true; android::uirenderer::Rect contentDrawBounds(0, 0, 1, 1); auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread); + { + // add a pointer to a deleted vector drawable object in the pipeline + sp<VectorDrawableRoot> dirtyVD(new VectorDrawableRoot(new VectorDrawable::Group())); + dirtyVD->mutateProperties()->setScaledSize(5, 5); + pipeline->getVectorDrawables()->push_back(dirtyVD.get()); + } + + // pipeline should clean list of dirty vector drawables before prepare tree + pipeline->onPrepareTree(); + auto surface = SkSurface::MakeRasterN32Premul(1, 1); surface->getCanvas()->drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver); ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE); - pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, - opaque, false, contentDrawBounds, surface); + + // drawFrame will crash if "SkiaPipeline::onPrepareTree" did not clean invalid VD pointer + pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, false, contentDrawBounds, + surface); ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorRED); } RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderFrameCheckOpaque) { - auto halfGreenNode = TestUtils::createSkiaNode(0, 0, 2, 2, - [](RenderProperties& props, SkiaRecordingCanvas& bottomHalfGreenCanvas) { - SkPaint greenPaint; - greenPaint.setColor(SK_ColorGREEN); - greenPaint.setStyle(SkPaint::kFill_Style); - bottomHalfGreenCanvas.drawRect(0, 1, 2, 2, greenPaint); - }); + auto halfGreenNode = TestUtils::createSkiaNode( + 0, 0, 2, 2, [](RenderProperties& props, SkiaRecordingCanvas& bottomHalfGreenCanvas) { + SkPaint greenPaint; + greenPaint.setColor(SK_ColorGREEN); + greenPaint.setStyle(SkPaint::kFill_Style); + bottomHalfGreenCanvas.drawRect(0, 1, 2, 2, greenPaint); + }); LayerUpdateQueue layerUpdateQueue; SkRect dirty = SkRect::MakeLargest(); std::vector<sp<RenderNode>> renderNodes; @@ -73,21 +106,21 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderFrameCheckOpaque) { auto surface = SkSurface::MakeRasterN32Premul(2, 2); surface->getCanvas()->drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver); ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE); - pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, - true, false, contentDrawBounds, surface); + pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, true, false, contentDrawBounds, + surface); ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE); ASSERT_EQ(TestUtils::getColor(surface, 0, 1), SK_ColorGREEN); - pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, - false, false, contentDrawBounds, surface); + pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, false, false, contentDrawBounds, + surface); ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned int)SK_ColorTRANSPARENT); ASSERT_EQ(TestUtils::getColor(surface, 0, 1), SK_ColorGREEN); } RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderFrameCheckDirtyRect) { - auto redNode = TestUtils::createSkiaNode(0, 0, 2, 2, - [](RenderProperties& props, SkiaRecordingCanvas& redCanvas) { - redCanvas.drawColor(SK_ColorRED, SkBlendMode::kSrcOver); - }); + auto redNode = TestUtils::createSkiaNode( + 0, 0, 2, 2, [](RenderProperties& props, SkiaRecordingCanvas& redCanvas) { + redCanvas.drawColor(SK_ColorRED, SkBlendMode::kSrcOver); + }); LayerUpdateQueue layerUpdateQueue; SkRect dirty = SkRect::MakeXYWH(0, 1, 2, 1); std::vector<sp<RenderNode>> renderNodes; @@ -97,8 +130,8 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderFrameCheckDirtyRect) { auto surface = SkSurface::MakeRasterN32Premul(2, 2); surface->getCanvas()->drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver); ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE); - pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, - true, false, contentDrawBounds, surface); + pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, true, false, contentDrawBounds, + surface); ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE); ASSERT_EQ(TestUtils::getColor(surface, 1, 0), SK_ColorBLUE); ASSERT_EQ(TestUtils::getColor(surface, 0, 1), SK_ColorRED); @@ -106,27 +139,27 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderFrameCheckDirtyRect) { } RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderLayer) { - auto redNode = TestUtils::createSkiaNode(0, 0, 1, 1, - [](RenderProperties& props, SkiaRecordingCanvas& redCanvas) { - redCanvas.drawColor(SK_ColorRED, SkBlendMode::kSrcOver); - }); + auto redNode = TestUtils::createSkiaNode( + 0, 0, 1, 1, [](RenderProperties& props, SkiaRecordingCanvas& redCanvas) { + redCanvas.drawColor(SK_ColorRED, SkBlendMode::kSrcOver); + }); auto surfaceLayer1 = SkSurface::MakeRasterN32Premul(1, 1); surfaceLayer1->getCanvas()->drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver); ASSERT_EQ(TestUtils::getColor(surfaceLayer1, 0, 0), SK_ColorWHITE); redNode->setLayerSurface(surfaceLayer1); - //create a 2nd 2x2 layer and add it to the queue as well. - //make the layer's dirty area one half of the layer and verify only the dirty half is updated. - auto blueNode = TestUtils::createSkiaNode(0, 0, 2, 2, - [](RenderProperties& props, SkiaRecordingCanvas& blueCanvas) { - blueCanvas.drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver); - }); + // create a 2nd 2x2 layer and add it to the queue as well. + // make the layer's dirty area one half of the layer and verify only the dirty half is updated. + auto blueNode = TestUtils::createSkiaNode( + 0, 0, 2, 2, [](RenderProperties& props, SkiaRecordingCanvas& blueCanvas) { + blueCanvas.drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver); + }); auto surfaceLayer2 = SkSurface::MakeRasterN32Premul(2, 2); surfaceLayer2->getCanvas()->drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver); ASSERT_EQ(TestUtils::getColor(surfaceLayer2, 0, 0), SK_ColorWHITE); blueNode->setLayerSurface(surfaceLayer2); - //attach both layers to the update queue + // attach both layers to the update queue LayerUpdateQueue layerUpdateQueue; SkRect dirty = SkRect::MakeLargest(); layerUpdateQueue.enqueueLayerWithDamage(redNode.get(), dirty); @@ -136,7 +169,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderLayer) { bool opaque = true; FrameBuilder::LightGeometry lightGeometry; lightGeometry.radius = 1.0f; - lightGeometry.center = { 0.0f, 0.0f, 0.0f }; + lightGeometry.center = {0.0f, 0.0f, 0.0f}; BakedOpRenderer::LightInfo lightInfo; auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread); pipeline->renderLayers(lightGeometry, &layerUpdateQueue, opaque, false, lightInfo); @@ -151,16 +184,16 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderLayer) { RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderOverdraw) { ScopedProperty<bool> prop(Properties::debugOverdraw, true); - auto whiteNode = TestUtils::createSkiaNode(0, 0, 1, 1, - [](RenderProperties& props, SkiaRecordingCanvas& canvas) { - canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver); - }); + auto whiteNode = TestUtils::createSkiaNode( + 0, 0, 1, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { + canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver); + }); LayerUpdateQueue layerUpdateQueue; SkRect dirty = SkRect::MakeXYWH(0, 0, 1, 1); std::vector<sp<RenderNode>> renderNodes; renderNodes.push_back(whiteNode); bool opaque = true; - //empty contentDrawBounds is avoiding backdrop/content logic, which would lead to less overdraw + // empty contentDrawBounds is avoiding backdrop/content logic, which would lead to less overdraw android::uirenderer::Rect contentDrawBounds(0, 0, 0, 0); auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread); auto surface = SkSurface::MakeRasterN32Premul(1, 1); @@ -170,39 +203,39 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderOverdraw) { ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE); // Single draw, should be white. - pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, - false, contentDrawBounds, surface); + pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, false, contentDrawBounds, + surface); ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorWHITE); // 1 Overdraw, should be blue blended onto white. renderNodes.push_back(whiteNode); - pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, - false, contentDrawBounds, surface); - ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned) 0xffd0d0ff); + pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, false, contentDrawBounds, + surface); + ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned)0xffd0d0ff); // 2 Overdraw, should be green blended onto white renderNodes.push_back(whiteNode); - pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, - false, contentDrawBounds, surface); - ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned) 0xffd0ffd0); + pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, false, contentDrawBounds, + surface); + ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned)0xffd0ffd0); // 3 Overdraw, should be pink blended onto white. renderNodes.push_back(whiteNode); - pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, - false, contentDrawBounds, surface); - ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned) 0xffffc0c0); + pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, false, contentDrawBounds, + surface); + ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned)0xffffc0c0); // 4 Overdraw, should be red blended onto white. renderNodes.push_back(whiteNode); - pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, - false, contentDrawBounds, surface); - ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned) 0xffff8080); + pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, false, contentDrawBounds, + surface); + ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned)0xffff8080); // 5 Overdraw, should be red blended onto white. renderNodes.push_back(whiteNode); - pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, - false, contentDrawBounds, surface); - ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned) 0xffff8080); + pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, false, contentDrawBounds, + surface); + ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned)0xffff8080); } namespace { @@ -212,15 +245,9 @@ public: DeferLayer() : SkSurface_Base(T().imageInfo(), nullptr) {} virtual ~DeferLayer() {} - SkCanvas* onNewCanvas() override { - return new T(); - } - sk_sp<SkSurface> onNewSurface(const SkImageInfo&) override { - return nullptr; - } - sk_sp<SkImage> onNewImageSnapshot() override { - return nullptr; - } + SkCanvas* onNewCanvas() override { return new T(); } + sk_sp<SkSurface> onNewSurface(const SkImageInfo&) override { return nullptr; } + sk_sp<SkImage> onNewImageSnapshot() override { return nullptr; } T* canvas() { return static_cast<T*>(getCanvas()); } void onCopyOnWrite(ContentChangeMode) override {} }; @@ -233,28 +260,28 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, deferRenderNodeScene) { void onDrawRect(const SkRect& rect, const SkPaint& paint) override { SkMatrix expected; switch (mDrawCounter++) { - case 0: - // background - left side - EXPECT_EQ(SkRect::MakeLTRB(600, 100, 700, 500), TestUtils::getClipBounds(this)); - expected.setTranslate(100, 100); - break; - case 1: - // background - top side - EXPECT_EQ(SkRect::MakeLTRB(100, 400, 600, 500), TestUtils::getClipBounds(this)); - expected.setTranslate(100, 100); - break; - case 2: - // content - EXPECT_EQ(SkRect::MakeLTRB(100, 100, 700, 500), TestUtils::getClipBounds(this)); - expected.setTranslate(-50, -50); - break; - case 3: - // overlay - EXPECT_EQ(SkRect::MakeLTRB(0, 0, 800, 600), TestUtils::getClipBounds(this)); - expected.reset(); - break; - default: - ADD_FAILURE() << "Too many rects observed"; + case 0: + // background - left side + EXPECT_EQ(SkRect::MakeLTRB(600, 100, 700, 500), TestUtils::getClipBounds(this)); + expected.setTranslate(100, 100); + break; + case 1: + // background - top side + EXPECT_EQ(SkRect::MakeLTRB(100, 400, 600, 500), TestUtils::getClipBounds(this)); + expected.setTranslate(100, 100); + break; + case 2: + // content + EXPECT_EQ(SkRect::MakeLTRB(100, 100, 700, 500), TestUtils::getClipBounds(this)); + expected.setTranslate(-50, -50); + break; + case 3: + // overlay + EXPECT_EQ(SkRect::MakeLTRB(0, 0, 800, 600), TestUtils::getClipBounds(this)); + expected.reset(); + break; + default: + ADD_FAILURE() << "Too many rects observed"; } EXPECT_EQ(expected, getTotalMatrix()); } @@ -266,23 +293,26 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, deferRenderNodeScene) { transparentPaint.setAlpha(128); // backdrop - nodes.push_back(TestUtils::createSkiaNode(100, 100, 700, 500, // 600x400 + nodes.push_back(TestUtils::createSkiaNode( + 100, 100, 700, 500, // 600x400 [&transparentPaint](RenderProperties& props, SkiaRecordingCanvas& canvas) { - canvas.drawRect(0, 0, 600, 400, transparentPaint); - })); + canvas.drawRect(0, 0, 600, 400, transparentPaint); + })); // content - android::uirenderer::Rect contentDrawBounds(150, 150, 650, 450); // 500x300 - nodes.push_back(TestUtils::createSkiaNode(0, 0, 800, 600, + android::uirenderer::Rect contentDrawBounds(150, 150, 650, 450); // 500x300 + nodes.push_back(TestUtils::createSkiaNode( + 0, 0, 800, 600, [&transparentPaint](RenderProperties& props, SkiaRecordingCanvas& canvas) { - canvas.drawRect(0, 0, 800, 600, transparentPaint); - })); + canvas.drawRect(0, 0, 800, 600, transparentPaint); + })); // overlay - nodes.push_back(TestUtils::createSkiaNode(0, 0, 800, 600, + nodes.push_back(TestUtils::createSkiaNode( + 0, 0, 800, 600, [&transparentPaint](RenderProperties& props, SkiaRecordingCanvas& canvas) { - canvas.drawRect(0, 0, 800, 200, transparentPaint); - })); + canvas.drawRect(0, 0, 800, 200, transparentPaint); + })); LayerUpdateQueue layerUpdateQueue; SkRect dirty = SkRect::MakeWH(800, 600); @@ -297,8 +327,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, clipped) { static const int CANVAS_HEIGHT = 200; class ClippedTestCanvas : public SkCanvas { public: - ClippedTestCanvas() : SkCanvas(CANVAS_WIDTH, CANVAS_HEIGHT) { - } + ClippedTestCanvas() : SkCanvas(CANVAS_WIDTH, CANVAS_HEIGHT) {} void onDrawImage(const SkImage*, SkScalar dx, SkScalar dy, const SkPaint*) override { EXPECT_EQ(0, mDrawCounter++); EXPECT_EQ(SkRect::MakeLTRB(10, 20, 30, 40), TestUtils::getClipBounds(this)); @@ -308,18 +337,19 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, clipped) { }; std::vector<sp<RenderNode>> nodes; - nodes.push_back(TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, + nodes.push_back(TestUtils::createSkiaNode( + 0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { - sk_sp<Bitmap> bitmap(TestUtils::createBitmap(CANVAS_WIDTH, CANVAS_HEIGHT)); - canvas.drawBitmap(*bitmap, 0, 0, nullptr); - })); + sk_sp<Bitmap> bitmap(TestUtils::createBitmap(CANVAS_WIDTH, CANVAS_HEIGHT)); + canvas.drawBitmap(*bitmap, 0, 0, nullptr); + })); LayerUpdateQueue layerUpdateQueue; SkRect dirty = SkRect::MakeLTRB(10, 20, 30, 40); auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread); sk_sp<DeferLayer<ClippedTestCanvas>> surface(new DeferLayer<ClippedTestCanvas>()); pipeline->renderFrame(layerUpdateQueue, dirty, nodes, true, false, - SkRect::MakeWH(CANVAS_WIDTH, CANVAS_HEIGHT), surface); + SkRect::MakeWH(CANVAS_WIDTH, CANVAS_HEIGHT), surface); EXPECT_EQ(1, surface->canvas()->mDrawCounter); } @@ -328,8 +358,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, clip_replace) { static const int CANVAS_HEIGHT = 50; class ClipReplaceTestCanvas : public SkCanvas { public: - ClipReplaceTestCanvas() : SkCanvas(CANVAS_WIDTH, CANVAS_HEIGHT) { - } + ClipReplaceTestCanvas() : SkCanvas(CANVAS_WIDTH, CANVAS_HEIGHT) {} void onDrawPaint(const SkPaint&) { EXPECT_EQ(0, mDrawCounter++); EXPECT_EQ(SkRect::MakeLTRB(20, 10, 30, 40), TestUtils::getClipBounds(this)) @@ -339,17 +368,17 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, clip_replace) { }; std::vector<sp<RenderNode>> nodes; - nodes.push_back(TestUtils::createSkiaNode(20, 20, 30, 30, - [](RenderProperties& props, SkiaRecordingCanvas& canvas) { - canvas.clipRect(0, -20, 10, 30, SkClipOp::kReplace_deprecated); - canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver); - })); + nodes.push_back(TestUtils::createSkiaNode( + 20, 20, 30, 30, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { + canvas.clipRect(0, -20, 10, 30, SkClipOp::kReplace_deprecated); + canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver); + })); LayerUpdateQueue layerUpdateQueue; SkRect dirty = SkRect::MakeLTRB(10, 10, 40, 40); auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread); sk_sp<DeferLayer<ClipReplaceTestCanvas>> surface(new DeferLayer<ClipReplaceTestCanvas>()); pipeline->renderFrame(layerUpdateQueue, dirty, nodes, true, false, - SkRect::MakeWH(CANVAS_WIDTH, CANVAS_HEIGHT), surface); + SkRect::MakeWH(CANVAS_WIDTH, CANVAS_HEIGHT), surface); EXPECT_EQ(1, surface->canvas()->mDrawCounter); } diff --git a/libs/hwui/tests/unit/SkiaRenderPropertiesTests.cpp b/libs/hwui/tests/unit/SkiaRenderPropertiesTests.cpp index 95c6ed6059b0..ad5fdacc8594 100644 --- a/libs/hwui/tests/unit/SkiaRenderPropertiesTests.cpp +++ b/libs/hwui/tests/unit/SkiaRenderPropertiesTests.cpp @@ -14,23 +14,23 @@ * limitations under the License. */ -#include <gtest/gtest.h> #include <VectorDrawable.h> +#include <gtest/gtest.h> +#include <SkClipStack.h> +#include <SkLiteRecorder.h> +#include <SkSurface_Base.h> +#include <string.h> #include "AnimationContext.h" #include "DamageAccumulator.h" +#include "FatalTestCanvas.h" #include "IContextFactory.h" +#include "SkiaCanvas.h" #include "pipeline/skia/SkiaDisplayList.h" #include "pipeline/skia/SkiaPipeline.h" #include "pipeline/skia/SkiaRecordingCanvas.h" #include "renderthread/CanvasContext.h" #include "tests/common/TestUtils.h" -#include "SkiaCanvas.h" -#include <SkSurface_Base.h> -#include <SkLiteRecorder.h> -#include <SkClipStack.h> -#include "FatalTestCanvas.h" -#include <string.h> using namespace android; using namespace android::uirenderer; @@ -40,7 +40,7 @@ using namespace android::uirenderer::skiapipeline; namespace { static void testProperty(std::function<void(RenderProperties&)> propSetupCallback, - std::function<void(const SkCanvas&)> opValidateCallback) { + std::function<void(const SkCanvas&)> opValidateCallback) { static const int CANVAS_WIDTH = 100; static const int CANVAS_HEIGHT = 100; class PropertyTestCanvas : public TestCanvasBase { @@ -57,79 +57,88 @@ static void testProperty(std::function<void(RenderProperties&)> propSetupCallbac std::function<void(const SkCanvas&)> mCallback; }; - auto node = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, + auto node = TestUtils::createSkiaNode( + 0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, [propSetupCallback](RenderProperties& props, SkiaRecordingCanvas& canvas) { - propSetupCallback(props); - SkPaint paint; - paint.setColor(SK_ColorWHITE); - canvas.drawRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, paint); - }); + propSetupCallback(props); + SkPaint paint; + paint.setColor(SK_ColorWHITE); + canvas.drawRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, paint); + }); PropertyTestCanvas canvas(opValidateCallback); RenderNodeDrawable drawable(node.get(), &canvas, true); canvas.drawDrawable(&drawable); EXPECT_EQ(1, canvas.mDrawCounter); } - } TEST(RenderNodeDrawable, renderPropClipping) { - testProperty([](RenderProperties& properties) { - properties.setClipToBounds(true); - properties.setClipBounds(android::uirenderer::Rect(10, 20, 300, 400)); - }, [](const SkCanvas& canvas) { - EXPECT_EQ(SkRect::MakeLTRB(10, 20, 100, 100), TestUtils::getClipBounds(&canvas)) - << "Clip rect should be intersection of node bounds and clip bounds"; - }); + testProperty( + [](RenderProperties& properties) { + properties.setClipToBounds(true); + properties.setClipBounds(android::uirenderer::Rect(10, 20, 300, 400)); + }, + [](const SkCanvas& canvas) { + EXPECT_EQ(SkRect::MakeLTRB(10, 20, 100, 100), TestUtils::getClipBounds(&canvas)) + << "Clip rect should be intersection of node bounds and clip bounds"; + }); } TEST(RenderNodeDrawable, renderPropRevealClip) { - testProperty([](RenderProperties& properties) { - properties.mutableRevealClip().set(true, 50, 50, 25); - }, [](const SkCanvas& canvas) { - EXPECT_EQ(SkRect::MakeLTRB(25, 25, 75, 75), TestUtils::getClipBounds(&canvas)); - }); + testProperty( + [](RenderProperties& properties) { + properties.mutableRevealClip().set(true, 50, 50, 25); + }, + [](const SkCanvas& canvas) { + EXPECT_EQ(SkRect::MakeLTRB(25, 25, 75, 75), TestUtils::getClipBounds(&canvas)); + }); } TEST(RenderNodeDrawable, renderPropOutlineClip) { - testProperty([](RenderProperties& properties) { - properties.mutableOutline().setShouldClip(true); - properties.mutableOutline().setRoundRect(10, 20, 30, 40, 5.0f, 0.5f); - }, [](const SkCanvas& canvas) { - EXPECT_EQ(SkRect::MakeLTRB(10, 20, 30, 40), TestUtils::getClipBounds(&canvas)); - }); + testProperty( + [](RenderProperties& properties) { + properties.mutableOutline().setShouldClip(true); + properties.mutableOutline().setRoundRect(10, 20, 30, 40, 5.0f, 0.5f); + }, + [](const SkCanvas& canvas) { + EXPECT_EQ(SkRect::MakeLTRB(10, 20, 30, 40), TestUtils::getClipBounds(&canvas)); + }); } TEST(RenderNodeDrawable, renderPropTransform) { - testProperty([](RenderProperties& properties) { - properties.setLeftTopRightBottom(10, 10, 110, 110); - - SkMatrix staticMatrix = SkMatrix::MakeScale(1.2f, 1.2f); - properties.setStaticMatrix(&staticMatrix); - - // ignored, since static overrides animation - SkMatrix animationMatrix = SkMatrix::MakeTrans(15, 15); - properties.setAnimationMatrix(&animationMatrix); - - properties.setTranslationX(10); - properties.setTranslationY(20); - properties.setScaleX(0.5f); - properties.setScaleY(0.7f); - }, [](const SkCanvas& canvas) { - Matrix4 matrix; - matrix.loadTranslate(10, 10, 0); // left, top - matrix.scale(1.2f, 1.2f, 1); // static matrix - // ignore animation matrix, since static overrides it - - // translation xy - matrix.translate(10, 20); - - // scale xy (from default pivot - center) - matrix.translate(50, 50); - matrix.scale(0.5f, 0.7f, 1); - matrix.translate(-50, -50); - Matrix4 actual(canvas.getTotalMatrix()); - EXPECT_MATRIX_APPROX_EQ(matrix, actual) - << "Op draw matrix must match expected combination of transformation properties"; - }); + testProperty( + [](RenderProperties& properties) { + properties.setLeftTopRightBottom(10, 10, 110, 110); + + SkMatrix staticMatrix = SkMatrix::MakeScale(1.2f, 1.2f); + properties.setStaticMatrix(&staticMatrix); + + // ignored, since static overrides animation + SkMatrix animationMatrix = SkMatrix::MakeTrans(15, 15); + properties.setAnimationMatrix(&animationMatrix); + + properties.setTranslationX(10); + properties.setTranslationY(20); + properties.setScaleX(0.5f); + properties.setScaleY(0.7f); + }, + [](const SkCanvas& canvas) { + Matrix4 matrix; + matrix.loadTranslate(10, 10, 0); // left, top + matrix.scale(1.2f, 1.2f, 1); // static matrix + // ignore animation matrix, since static overrides it + + // translation xy + matrix.translate(10, 20); + + // scale xy (from default pivot - center) + matrix.translate(50, 50); + matrix.scale(0.5f, 0.7f, 1); + matrix.translate(-50, -50); + Matrix4 actual(canvas.getTotalMatrix()); + EXPECT_MATRIX_APPROX_EQ(matrix, actual) << "Op draw matrix must match expected " + "combination of transformation " + "properties"; + }); } diff --git a/libs/hwui/tests/unit/SnapshotTests.cpp b/libs/hwui/tests/unit/SnapshotTests.cpp index 11797a8fc1db..9d673c82d4d0 100644 --- a/libs/hwui/tests/unit/SnapshotTests.cpp +++ b/libs/hwui/tests/unit/SnapshotTests.cpp @@ -32,16 +32,16 @@ TEST(Snapshot, serializeIntersectedClip) { LinearAllocator allocator; ClipRect rect(Rect(0, 0, 75, 75)); { - auto intersectWithChild = child->serializeIntersectedClip(allocator, - &rect, Matrix4::identity()); + auto intersectWithChild = + child->serializeIntersectedClip(allocator, &rect, Matrix4::identity()); ASSERT_NE(nullptr, intersectWithChild); EXPECT_EQ(Rect(50, 50, 75, 75), intersectWithChild->rect) << "Expect intersect with child"; } rect.intersectWithRoot = true; { - auto intersectWithRoot = child->serializeIntersectedClip(allocator, - &rect, Matrix4::identity()); + auto intersectWithRoot = + child->serializeIntersectedClip(allocator, &rect, Matrix4::identity()); ASSERT_NE(nullptr, intersectWithRoot); EXPECT_EQ(Rect(10, 10, 75, 75), intersectWithRoot->rect) << "Expect intersect with root"; } diff --git a/libs/hwui/tests/unit/StringUtilsTests.cpp b/libs/hwui/tests/unit/StringUtilsTests.cpp index b60e96c756ec..04fbb512e598 100644 --- a/libs/hwui/tests/unit/StringUtilsTests.cpp +++ b/libs/hwui/tests/unit/StringUtilsTests.cpp @@ -34,7 +34,7 @@ TEST(StringUtils, advancedBuildSet) { auto collection = StringUtils::split("GL_ext1 GL_ext2 GL_ext3"); EXPECT_TRUE(collection.has("GL_ext1")); - EXPECT_FALSE(collection.has("GL_ext")); // string present, but not in list + EXPECT_FALSE(collection.has("GL_ext")); // string present, but not in list } TEST(StringUtils, sizePrinter) { diff --git a/libs/hwui/tests/unit/TestUtilsTests.cpp b/libs/hwui/tests/unit/TestUtilsTests.cpp index 490595773724..742fe4210780 100644 --- a/libs/hwui/tests/unit/TestUtilsTests.cpp +++ b/libs/hwui/tests/unit/TestUtilsTests.cpp @@ -14,8 +14,8 @@ * limitations under the License. */ -#include "tests/common/TestUtils.h" #include "Properties.h" +#include "tests/common/TestUtils.h" #include <gtest/gtest.h> diff --git a/libs/hwui/tests/unit/TextDropShadowCacheTests.cpp b/libs/hwui/tests/unit/TextDropShadowCacheTests.cpp index 8312bda8d67d..92d05e44c6ca 100644 --- a/libs/hwui/tests/unit/TextDropShadowCacheTests.cpp +++ b/libs/hwui/tests/unit/TextDropShadowCacheTests.cpp @@ -18,8 +18,8 @@ #include "GammaFontRenderer.h" #include "TextDropShadowCache.h" -#include "utils/Blur.h" #include "tests/common/TestUtils.h" +#include "utils/Blur.h" #include <SkPaint.h> @@ -29,6 +29,7 @@ using namespace android::uirenderer; RENDERTHREAD_OPENGL_PIPELINE_TEST(TextDropShadowCache, addRemove) { SkPaint paint; paint.setTextSize(20); + paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); GammaFontRenderer gammaFontRenderer; FontRenderer& fontRenderer = gammaFontRenderer.getFontRenderer(); @@ -40,15 +41,15 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(TextDropShadowCache, addRemove) { std::vector<float> positions; float totalAdvance; uirenderer::Rect bounds; - TestUtils::layoutTextUnscaled(paint, "This is a test", - &glyphs, &positions, &totalAdvance, &bounds); + TestUtils::layoutTextUnscaled(paint, "This is a test", &glyphs, &positions, &totalAdvance, + &bounds); EXPECT_TRUE(bounds.contains(5, -10, 100, 0)) << "Expect input to be nontrivially sized"; ShadowTexture* texture = cache.get(&paint, glyphs.data(), glyphs.size(), 10, positions.data()); ASSERT_TRUE(texture); ASSERT_FALSE(texture->cleanup); - ASSERT_EQ((uint32_t) texture->objectSize(), cache.getSize()); + ASSERT_EQ((uint32_t)texture->objectSize(), cache.getSize()); ASSERT_TRUE(cache.getSize()); cache.clear(); ASSERT_EQ(cache.getSize(), 0u); diff --git a/libs/hwui/tests/unit/ThreadBaseTests.cpp b/libs/hwui/tests/unit/ThreadBaseTests.cpp new file mode 100644 index 000000000000..1168ff211202 --- /dev/null +++ b/libs/hwui/tests/unit/ThreadBaseTests.cpp @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gtest/gtest.h> + +#include "thread/ThreadBase.h" +#include "utils/TimeUtils.h" + +#include <chrono> +#include "unistd.h" + +using namespace android; +using namespace android::uirenderer; + +static ThreadBase& thread() { + class TestThread : public ThreadBase, public virtual RefBase {}; + static sp<TestThread> thread = []() -> auto { + sp<TestThread> ret{new TestThread}; + ret->start("TestThread"); + return ret; + } + (); + return *thread; +} + +static WorkQueue& queue() { + return thread().queue(); +} + +TEST(ThreadBase, post) { + std::atomic_bool ran(false); + queue().post([&ran]() { ran = true; }); + for (int i = 0; !ran && i < 1000; i++) { + usleep(1); + } + ASSERT_TRUE(ran) << "Failed to flip atomic after 1 second"; +} + +TEST(ThreadBase, postDelay) { + using clock = WorkQueue::clock; + + std::promise<nsecs_t> ranAtPromise; + auto queuedAt = clock::now(); + queue().postDelayed(100_us, [&]() { ranAtPromise.set_value(clock::now()); }); + auto ranAt = ranAtPromise.get_future().get(); + auto ranAfter = ranAt - queuedAt; + ASSERT_TRUE(ranAfter > 90_us) << "Ran after " << ns2us(ranAfter) << "us <= 90us"; +} + +TEST(ThreadBase, runSync) { + pid_t thisTid = gettid(); + pid_t otherTid = thisTid; + + auto result = queue().runSync([&otherTid]() -> auto { + otherTid = gettid(); + return 42; + }); + + ASSERT_EQ(42, result); + ASSERT_NE(thisTid, otherTid); +} + +TEST(ThreadBase, async) { + pid_t thisTid = gettid(); + pid_t thisPid = getpid(); + + auto otherTid = queue().async([]() -> auto { return gettid(); }); + auto otherPid = queue().async([]() -> auto { return getpid(); }); + auto result = queue().async([]() -> auto { return 42; }); + + ASSERT_NE(thisTid, otherTid.get()); + ASSERT_EQ(thisPid, otherPid.get()); + ASSERT_EQ(42, result.get()); +} + +TEST(ThreadBase, lifecyclePerf) { + struct EventCount { + std::atomic_int construct{0}; + std::atomic_int destruct{0}; + std::atomic_int copy{0}; + std::atomic_int move{0}; + }; + + struct Counter { + Counter(EventCount* count) : mCount(count) { mCount->construct++; } + + Counter(const Counter& other) : mCount(other.mCount) { + if (mCount) mCount->copy++; + } + + Counter(Counter&& other) : mCount(other.mCount) { + other.mCount = nullptr; + if (mCount) mCount->move++; + } + + Counter& operator=(const Counter& other) { + mCount = other.mCount; + if (mCount) mCount->copy++; + return *this; + } + + Counter& operator=(Counter&& other) { + mCount = other.mCount; + other.mCount = nullptr; + if (mCount) mCount->move++; + return *this; + } + + ~Counter() { + if (mCount) mCount->destruct++; + } + + EventCount* mCount; + }; + + EventCount count; + { + Counter counter{&count}; + queue().runSync([c = std::move(counter)](){}); + } + ASSERT_EQ(1, count.construct.load()); + ASSERT_EQ(1, count.destruct.load()); + ASSERT_EQ(0, count.copy.load()); + ASSERT_LE(1, count.move.load()); +} + +int lifecycleTestHelper(const sp<VirtualLightRefBase>& test) { + return queue().runSync([t = test]()->int { return t->getStrongCount(); }); +} + +TEST(ThreadBase, lifecycle) { + sp<VirtualLightRefBase> dummyObject{new VirtualLightRefBase}; + ASSERT_EQ(1, dummyObject->getStrongCount()); + ASSERT_EQ(2, queue().runSync([dummyObject]() -> int { return dummyObject->getStrongCount(); })); + ASSERT_EQ(1, dummyObject->getStrongCount()); + ASSERT_EQ(2, lifecycleTestHelper(dummyObject)); + ASSERT_EQ(1, dummyObject->getStrongCount()); +}
\ No newline at end of file diff --git a/libs/hwui/tests/unit/TypefaceTests.cpp b/libs/hwui/tests/unit/TypefaceTests.cpp index c90b6f0e9cdd..66d6f527e604 100644 --- a/libs/hwui/tests/unit/TypefaceTests.cpp +++ b/libs/hwui/tests/unit/TypefaceTests.cpp @@ -52,25 +52,25 @@ std::shared_ptr<minikin::FontFamily> buildFamily(const char* fileName) { SkData::MakeWithProc(data, st.st_size, unmap, reinterpret_cast<void*>(st.st_size)); std::unique_ptr<SkStreamAsset> fontData(new SkMemoryStream(skData)); sk_sp<SkFontMgr> fm(SkFontMgr::RefDefault()); - sk_sp<SkTypeface> typeface(fm->createFromStream(fontData.release())); + sk_sp<SkTypeface> typeface(fm->makeFromStream(std::move(fontData))); LOG_ALWAYS_FATAL_IF(typeface == nullptr, "Failed to make typeface from %s", fileName); std::shared_ptr<minikin::MinikinFont> font = std::make_shared<MinikinFontSkia>( std::move(typeface), data, st.st_size, 0, std::vector<minikin::FontVariation>()); return std::make_shared<minikin::FontFamily>( - std::vector<minikin::Font>({ minikin::Font(std::move(font), minikin::FontStyle()) })); + std::vector<minikin::Font>({minikin::Font(std::move(font), minikin::FontStyle())})); } std::vector<std::shared_ptr<minikin::FontFamily>> makeSingleFamlyVector(const char* fileName) { - return std::vector<std::shared_ptr<minikin::FontFamily>>({ buildFamily(fileName) }); + return std::vector<std::shared_ptr<minikin::FontFamily>>({buildFamily(fileName)}); } TEST(TypefaceTest, resolveDefault_and_setDefaultTest) { - std::unique_ptr<Typeface> regular( - Typeface::createFromFamilies(makeSingleFamlyVector(kRobotoRegular), - RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE)); + std::unique_ptr<Typeface> regular(Typeface::createFromFamilies( + makeSingleFamlyVector(kRobotoRegular), RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE)); EXPECT_EQ(regular.get(), Typeface::resolveDefault(regular.get())); - Typeface* old = Typeface::resolveDefault(nullptr); // Keep the original to restore it later. + // Keep the original to restore it later. + const Typeface* old = Typeface::resolveDefault(nullptr); ASSERT_NE(nullptr, old); Typeface::setDefault(regular.get()); @@ -81,161 +81,176 @@ TEST(TypefaceTest, resolveDefault_and_setDefaultTest) { TEST(TypefaceTest, createWithDifferentBaseWeight) { std::unique_ptr<Typeface> bold(Typeface::createWithDifferentBaseWeight(nullptr, 700)); - EXPECT_EQ(7, bold->fStyle.getWeight()); - EXPECT_FALSE(bold->fStyle.getItalic()); - EXPECT_EQ(SkTypeface::kNormal, bold->fSkiaStyle); + EXPECT_EQ(700, bold->fStyle.weight()); + EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, bold->fStyle.slant()); + EXPECT_EQ(Typeface::kNormal, bold->fAPIStyle); std::unique_ptr<Typeface> light(Typeface::createWithDifferentBaseWeight(nullptr, 300)); - EXPECT_EQ(3, light->fStyle.getWeight()); - EXPECT_FALSE(light->fStyle.getItalic()); - EXPECT_EQ(SkTypeface::kNormal, light->fSkiaStyle); + EXPECT_EQ(300, light->fStyle.weight()); + EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, light->fStyle.slant()); + EXPECT_EQ(Typeface::kNormal, light->fAPIStyle); } TEST(TypefaceTest, createRelativeTest_fromRegular) { // In Java, Typeface.create(Typeface.DEFAULT, Typeface.NORMAL); - std::unique_ptr<Typeface> normal(Typeface::createRelative(nullptr, SkTypeface::kNormal)); - EXPECT_EQ(4, normal->fStyle.getWeight()); - EXPECT_FALSE(normal->fStyle.getItalic()); - EXPECT_EQ(SkTypeface::kNormal, normal->fSkiaStyle); + std::unique_ptr<Typeface> normal(Typeface::createRelative(nullptr, Typeface::kNormal)); + EXPECT_EQ(400, normal->fStyle.weight()); + EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, normal->fStyle.slant()); + EXPECT_EQ(Typeface::kNormal, normal->fAPIStyle); // In Java, Typeface.create(Typeface.DEFAULT, Typeface.BOLD); - std::unique_ptr<Typeface> bold(Typeface::createRelative(nullptr, SkTypeface::kBold)); - EXPECT_EQ(7, bold->fStyle.getWeight()); - EXPECT_FALSE(bold->fStyle.getItalic()); - EXPECT_EQ(SkTypeface::kBold, bold->fSkiaStyle); + std::unique_ptr<Typeface> bold(Typeface::createRelative(nullptr, Typeface::kBold)); + EXPECT_EQ(700, bold->fStyle.weight()); + EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, bold->fStyle.slant()); + EXPECT_EQ(Typeface::kBold, bold->fAPIStyle); // In Java, Typeface.create(Typeface.DEFAULT, Typeface.ITALIC); - std::unique_ptr<Typeface> italic(Typeface::createRelative(nullptr, SkTypeface::kItalic)); - EXPECT_EQ(4, italic->fStyle.getWeight()); - EXPECT_TRUE(italic->fStyle.getItalic()); - EXPECT_EQ(SkTypeface::kItalic, italic->fSkiaStyle); + std::unique_ptr<Typeface> italic(Typeface::createRelative(nullptr, Typeface::kItalic)); + EXPECT_EQ(400, italic->fStyle.weight()); + EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, italic->fStyle.slant()); + EXPECT_EQ(Typeface::kItalic, italic->fAPIStyle); // In Java, Typeface.create(Typeface.DEFAULT, Typeface.BOLD_ITALIC); - std::unique_ptr<Typeface> boldItalic( - Typeface::createRelative(nullptr, SkTypeface::kBoldItalic)); - EXPECT_EQ(7, boldItalic->fStyle.getWeight()); - EXPECT_TRUE(boldItalic->fStyle.getItalic()); - EXPECT_EQ(SkTypeface::kBoldItalic, boldItalic->fSkiaStyle); + std::unique_ptr<Typeface> boldItalic(Typeface::createRelative(nullptr, Typeface::kBoldItalic)); + EXPECT_EQ(700, boldItalic->fStyle.weight()); + EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, boldItalic->fStyle.slant()); + EXPECT_EQ(Typeface::kBoldItalic, boldItalic->fAPIStyle); } TEST(TypefaceTest, createRelativeTest_BoldBase) { std::unique_ptr<Typeface> base(Typeface::createWithDifferentBaseWeight(nullptr, 700)); - // In Java, Typeface.create(Typeface.create("sans-serif-bold"), Typeface.NORMAL); - std::unique_ptr<Typeface> normal(Typeface::createRelative(base.get(), SkTypeface::kNormal)); - EXPECT_EQ(7, normal->fStyle.getWeight()); - EXPECT_FALSE(normal->fStyle.getItalic()); - EXPECT_EQ(SkTypeface::kNormal, normal->fSkiaStyle); - - // In Java, Typeface.create(Typeface.create("sans-serif-bold"), Typeface.BOLD); - std::unique_ptr<Typeface> bold(Typeface::createRelative(base.get(), SkTypeface::kBold)); - EXPECT_EQ(10, bold->fStyle.getWeight()); - EXPECT_FALSE(bold->fStyle.getItalic()); - EXPECT_EQ(SkTypeface::kBold, bold->fSkiaStyle); - - // In Java, Typeface.create(Typeface.create("sans-serif-bold"), Typeface.ITALIC); - std::unique_ptr<Typeface> italic(Typeface::createRelative(base.get(), SkTypeface::kItalic)); - EXPECT_EQ(7, italic->fStyle.getWeight()); - EXPECT_TRUE(italic->fStyle.getItalic()); - EXPECT_EQ(SkTypeface::kItalic, italic->fSkiaStyle); - - // In Java, Typeface.create(Typeface.create("sans-serif-bold"), Typeface.BOLD_ITALIC); - std::unique_ptr<Typeface> - boldItalic(Typeface::createRelative(base.get(), SkTypeface::kBoldItalic)); - EXPECT_EQ(10, boldItalic->fStyle.getWeight()); - EXPECT_TRUE(boldItalic->fStyle.getItalic()); - EXPECT_EQ(SkTypeface::kBoldItalic, boldItalic->fSkiaStyle); + // In Java, Typeface.create(Typeface.create("sans-serif-bold"), + // Typeface.NORMAL); + std::unique_ptr<Typeface> normal(Typeface::createRelative(base.get(), Typeface::kNormal)); + EXPECT_EQ(700, normal->fStyle.weight()); + EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, normal->fStyle.slant()); + EXPECT_EQ(Typeface::kNormal, normal->fAPIStyle); + + // In Java, Typeface.create(Typeface.create("sans-serif-bold"), + // Typeface.BOLD); + std::unique_ptr<Typeface> bold(Typeface::createRelative(base.get(), Typeface::kBold)); + EXPECT_EQ(1000, bold->fStyle.weight()); + EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, bold->fStyle.slant()); + EXPECT_EQ(Typeface::kBold, bold->fAPIStyle); + + // In Java, Typeface.create(Typeface.create("sans-serif-bold"), + // Typeface.ITALIC); + std::unique_ptr<Typeface> italic(Typeface::createRelative(base.get(), Typeface::kItalic)); + EXPECT_EQ(700, italic->fStyle.weight()); + EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, italic->fStyle.slant()); + EXPECT_EQ(Typeface::kItalic, italic->fAPIStyle); + + // In Java, Typeface.create(Typeface.create("sans-serif-bold"), + // Typeface.BOLD_ITALIC); + std::unique_ptr<Typeface> boldItalic( + Typeface::createRelative(base.get(), Typeface::kBoldItalic)); + EXPECT_EQ(1000, boldItalic->fStyle.weight()); + EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, boldItalic->fStyle.slant()); + EXPECT_EQ(Typeface::kBoldItalic, boldItalic->fAPIStyle); } TEST(TypefaceTest, createRelativeTest_LightBase) { std::unique_ptr<Typeface> base(Typeface::createWithDifferentBaseWeight(nullptr, 300)); - // In Java, Typeface.create(Typeface.create("sans-serif-light"), Typeface.NORMAL); - std::unique_ptr<Typeface> normal(Typeface::createRelative(base.get(), SkTypeface::kNormal)); - EXPECT_EQ(3, normal->fStyle.getWeight()); - EXPECT_FALSE(normal->fStyle.getItalic()); - EXPECT_EQ(SkTypeface::kNormal, normal->fSkiaStyle); - - // In Java, Typeface.create(Typeface.create("sans-serif-light"), Typeface.BOLD); - std::unique_ptr<Typeface> bold(Typeface::createRelative(base.get(), SkTypeface::kBold)); - EXPECT_EQ(6, bold->fStyle.getWeight()); - EXPECT_FALSE(bold->fStyle.getItalic()); - EXPECT_EQ(SkTypeface::kBold, bold->fSkiaStyle); - - // In Java, Typeface.create(Typeface.create("sans-serif-light"), Typeface.ITLIC); - std::unique_ptr<Typeface> italic(Typeface::createRelative(base.get(), SkTypeface::kItalic)); - EXPECT_EQ(3, italic->fStyle.getWeight()); - EXPECT_TRUE(italic->fStyle.getItalic()); - EXPECT_EQ(SkTypeface::kItalic, italic->fSkiaStyle); - - // In Java, Typeface.create(Typeface.create("sans-serif-light"), Typeface.BOLD_ITALIC); - std::unique_ptr<Typeface> - boldItalic(Typeface::createRelative(base.get(), SkTypeface::kBoldItalic)); - EXPECT_EQ(6, boldItalic->fStyle.getWeight()); - EXPECT_TRUE(boldItalic->fStyle.getItalic()); - EXPECT_EQ(SkTypeface::kBoldItalic, boldItalic->fSkiaStyle); + // In Java, Typeface.create(Typeface.create("sans-serif-light"), + // Typeface.NORMAL); + std::unique_ptr<Typeface> normal(Typeface::createRelative(base.get(), Typeface::kNormal)); + EXPECT_EQ(300, normal->fStyle.weight()); + EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, normal->fStyle.slant()); + EXPECT_EQ(Typeface::kNormal, normal->fAPIStyle); + + // In Java, Typeface.create(Typeface.create("sans-serif-light"), + // Typeface.BOLD); + std::unique_ptr<Typeface> bold(Typeface::createRelative(base.get(), Typeface::kBold)); + EXPECT_EQ(600, bold->fStyle.weight()); + EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, bold->fStyle.slant()); + EXPECT_EQ(Typeface::kBold, bold->fAPIStyle); + + // In Java, Typeface.create(Typeface.create("sans-serif-light"), + // Typeface.ITLIC); + std::unique_ptr<Typeface> italic(Typeface::createRelative(base.get(), Typeface::kItalic)); + EXPECT_EQ(300, italic->fStyle.weight()); + EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, italic->fStyle.slant()); + EXPECT_EQ(Typeface::kItalic, italic->fAPIStyle); + + // In Java, Typeface.create(Typeface.create("sans-serif-light"), + // Typeface.BOLD_ITALIC); + std::unique_ptr<Typeface> boldItalic( + Typeface::createRelative(base.get(), Typeface::kBoldItalic)); + EXPECT_EQ(600, boldItalic->fStyle.weight()); + EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, boldItalic->fStyle.slant()); + EXPECT_EQ(Typeface::kBoldItalic, boldItalic->fAPIStyle); } TEST(TypefaceTest, createRelativeTest_fromBoldStyled) { - std::unique_ptr<Typeface> base(Typeface::createRelative(nullptr, SkTypeface::kBold)); - - // In Java, Typeface.create(Typeface.create(Typeface.DEFAULT, Typeface.BOLD), Typeface.NORMAL); - std::unique_ptr<Typeface> normal(Typeface::createRelative(base.get(), SkTypeface::kNormal)); - EXPECT_EQ(4, normal->fStyle.getWeight()); - EXPECT_FALSE(normal->fStyle.getItalic()); - EXPECT_EQ(SkTypeface::kNormal, normal->fSkiaStyle); - - // In Java Typeface.create(Typeface.create(Typeface.DEFAULT, Typeface.BOLD), Typeface.BOLD); - std::unique_ptr<Typeface> bold(Typeface::createRelative(base.get(), SkTypeface::kBold)); - EXPECT_EQ(7, bold->fStyle.getWeight()); - EXPECT_FALSE(bold->fStyle.getItalic()); - EXPECT_EQ(SkTypeface::kBold, bold->fSkiaStyle); - - // In Java, Typeface.create(Typeface.create(Typeface.DEFAULT, Typeface.BOLD), Typeface.ITALIC); - std::unique_ptr<Typeface> italic(Typeface::createRelative(base.get(), SkTypeface::kItalic)); - EXPECT_EQ(4, normal->fStyle.getWeight()); - EXPECT_TRUE(italic->fStyle.getItalic()); - EXPECT_EQ(SkTypeface::kItalic, italic->fSkiaStyle); + std::unique_ptr<Typeface> base(Typeface::createRelative(nullptr, Typeface::kBold)); + + // In Java, Typeface.create(Typeface.create(Typeface.DEFAULT, Typeface.BOLD), + // Typeface.NORMAL); + std::unique_ptr<Typeface> normal(Typeface::createRelative(base.get(), Typeface::kNormal)); + EXPECT_EQ(400, normal->fStyle.weight()); + EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, normal->fStyle.slant()); + EXPECT_EQ(Typeface::kNormal, normal->fAPIStyle); + + // In Java Typeface.create(Typeface.create(Typeface.DEFAULT, Typeface.BOLD), + // Typeface.BOLD); + std::unique_ptr<Typeface> bold(Typeface::createRelative(base.get(), Typeface::kBold)); + EXPECT_EQ(700, bold->fStyle.weight()); + EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, bold->fStyle.slant()); + EXPECT_EQ(Typeface::kBold, bold->fAPIStyle); + + // In Java, Typeface.create(Typeface.create(Typeface.DEFAULT, Typeface.BOLD), + // Typeface.ITALIC); + std::unique_ptr<Typeface> italic(Typeface::createRelative(base.get(), Typeface::kItalic)); + EXPECT_EQ(400, normal->fStyle.weight()); + EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, italic->fStyle.slant()); + EXPECT_EQ(Typeface::kItalic, italic->fAPIStyle); // In Java, - // Typeface.create(Typeface.create(Typeface.DEFAULT, Typeface.BOLD), Typeface.BOLD_ITALIC); - std::unique_ptr<Typeface> - boldItalic(Typeface::createRelative(base.get(), SkTypeface::kBoldItalic)); - EXPECT_EQ(7, boldItalic->fStyle.getWeight()); - EXPECT_TRUE(boldItalic->fStyle.getItalic()); - EXPECT_EQ(SkTypeface::kBoldItalic, boldItalic->fSkiaStyle); + // Typeface.create(Typeface.create(Typeface.DEFAULT, Typeface.BOLD), + // Typeface.BOLD_ITALIC); + std::unique_ptr<Typeface> boldItalic( + Typeface::createRelative(base.get(), Typeface::kBoldItalic)); + EXPECT_EQ(700, boldItalic->fStyle.weight()); + EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, boldItalic->fStyle.slant()); + EXPECT_EQ(Typeface::kBoldItalic, boldItalic->fAPIStyle); } TEST(TypefaceTest, createRelativeTest_fromItalicStyled) { - std::unique_ptr<Typeface> base(Typeface::createRelative(nullptr, SkTypeface::kItalic)); + std::unique_ptr<Typeface> base(Typeface::createRelative(nullptr, Typeface::kItalic)); // In Java, - // Typeface.create(Typeface.create(Typeface.DEFAULT, Typeface.ITALIC), Typeface.NORMAL); - std::unique_ptr<Typeface> normal(Typeface::createRelative(base.get(), SkTypeface::kNormal)); - EXPECT_EQ(4, normal->fStyle.getWeight()); - EXPECT_FALSE(normal->fStyle.getItalic()); - EXPECT_EQ(SkTypeface::kNormal, normal->fSkiaStyle); - - // In Java, Typeface.create(Typeface.create(Typeface.DEFAULT, Typeface.ITALIC), Typeface.BOLD); - std::unique_ptr<Typeface> bold(Typeface::createRelative(base.get(), SkTypeface::kBold)); - EXPECT_EQ(7, bold->fStyle.getWeight()); - EXPECT_FALSE(bold->fStyle.getItalic()); - EXPECT_EQ(SkTypeface::kBold, bold->fSkiaStyle); + // Typeface.create(Typeface.create(Typeface.DEFAULT, Typeface.ITALIC), + // Typeface.NORMAL); + std::unique_ptr<Typeface> normal(Typeface::createRelative(base.get(), Typeface::kNormal)); + EXPECT_EQ(400, normal->fStyle.weight()); + EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, normal->fStyle.slant()); + EXPECT_EQ(Typeface::kNormal, normal->fAPIStyle); + + // In Java, Typeface.create(Typeface.create(Typeface.DEFAULT, + // Typeface.ITALIC), Typeface.BOLD); + std::unique_ptr<Typeface> bold(Typeface::createRelative(base.get(), Typeface::kBold)); + EXPECT_EQ(700, bold->fStyle.weight()); + EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, bold->fStyle.slant()); + EXPECT_EQ(Typeface::kBold, bold->fAPIStyle); // In Java, - // Typeface.create(Typeface.create(Typeface.DEFAULT, Typeface.ITALIC), Typeface.ITALIC); - std::unique_ptr<Typeface> italic(Typeface::createRelative(base.get(), SkTypeface::kItalic)); - EXPECT_EQ(4, italic->fStyle.getWeight()); - EXPECT_TRUE(italic->fStyle.getItalic()); - EXPECT_EQ(SkTypeface::kItalic, italic->fSkiaStyle); + // Typeface.create(Typeface.create(Typeface.DEFAULT, Typeface.ITALIC), + // Typeface.ITALIC); + std::unique_ptr<Typeface> italic(Typeface::createRelative(base.get(), Typeface::kItalic)); + EXPECT_EQ(400, italic->fStyle.weight()); + EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, italic->fStyle.slant()); + EXPECT_EQ(Typeface::kItalic, italic->fAPIStyle); // In Java, - // Typeface.create(Typeface.create(Typeface.DEFAULT, Typeface.ITALIC), Typeface.BOLD_ITALIC); - std::unique_ptr<Typeface> - boldItalic(Typeface::createRelative(base.get(), SkTypeface::kBoldItalic)); - EXPECT_EQ(7, boldItalic->fStyle.getWeight()); - EXPECT_TRUE(boldItalic->fStyle.getItalic()); - EXPECT_EQ(SkTypeface::kBoldItalic, boldItalic->fSkiaStyle); + // Typeface.create(Typeface.create(Typeface.DEFAULT, Typeface.ITALIC), + // Typeface.BOLD_ITALIC); + std::unique_ptr<Typeface> boldItalic( + Typeface::createRelative(base.get(), Typeface::kBoldItalic)); + EXPECT_EQ(700, boldItalic->fStyle.weight()); + EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, boldItalic->fStyle.slant()); + EXPECT_EQ(Typeface::kBoldItalic, boldItalic->fAPIStyle); } TEST(TypefaceTest, createRelativeTest_fromSpecifiedStyled) { @@ -245,174 +260,179 @@ TEST(TypefaceTest, createRelativeTest_fromSpecifiedStyled) { // Typeface typeface = new Typeface.Builder(invalid).setFallback("sans-serif") // .setWeight(700).setItalic(false).build(); // Typeface.create(typeface, Typeface.NORMAL); - std::unique_ptr<Typeface> normal(Typeface::createRelative(base.get(), SkTypeface::kNormal)); - EXPECT_EQ(4, normal->fStyle.getWeight()); - EXPECT_FALSE(normal->fStyle.getItalic()); - EXPECT_EQ(SkTypeface::kNormal, normal->fSkiaStyle); + std::unique_ptr<Typeface> normal(Typeface::createRelative(base.get(), Typeface::kNormal)); + EXPECT_EQ(400, normal->fStyle.weight()); + EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, normal->fStyle.slant()); + EXPECT_EQ(Typeface::kNormal, normal->fAPIStyle); // In Java, // Typeface typeface = new Typeface.Builder(invalid).setFallback("sans-serif") // .setWeight(700).setItalic(false).build(); // Typeface.create(typeface, Typeface.BOLD); - std::unique_ptr<Typeface> bold(Typeface::createRelative(base.get(), SkTypeface::kBold)); - EXPECT_EQ(7, bold->fStyle.getWeight()); - EXPECT_FALSE(bold->fStyle.getItalic()); - EXPECT_EQ(SkTypeface::kBold, bold->fSkiaStyle); + std::unique_ptr<Typeface> bold(Typeface::createRelative(base.get(), Typeface::kBold)); + EXPECT_EQ(700, bold->fStyle.weight()); + EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, bold->fStyle.slant()); + EXPECT_EQ(Typeface::kBold, bold->fAPIStyle); // In Java, // Typeface typeface = new Typeface.Builder(invalid).setFallback("sans-serif") // .setWeight(700).setItalic(false).build(); // Typeface.create(typeface, Typeface.ITALIC); - std::unique_ptr<Typeface> italic(Typeface::createRelative(base.get(), SkTypeface::kItalic)); - EXPECT_EQ(4, italic->fStyle.getWeight()); - EXPECT_TRUE(italic->fStyle.getItalic()); - EXPECT_EQ(SkTypeface::kItalic, italic->fSkiaStyle); + std::unique_ptr<Typeface> italic(Typeface::createRelative(base.get(), Typeface::kItalic)); + EXPECT_EQ(400, italic->fStyle.weight()); + EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, italic->fStyle.slant()); + EXPECT_EQ(Typeface::kItalic, italic->fAPIStyle); // In Java, // Typeface typeface = new Typeface.Builder(invalid).setFallback("sans-serif") // .setWeight(700).setItalic(false).build(); // Typeface.create(typeface, Typeface.BOLD_ITALIC); - std::unique_ptr<Typeface> - boldItalic(Typeface::createRelative(base.get(), SkTypeface::kBoldItalic)); - EXPECT_EQ(7, boldItalic->fStyle.getWeight()); - EXPECT_TRUE(boldItalic->fStyle.getItalic()); - EXPECT_EQ(SkTypeface::kBoldItalic, boldItalic->fSkiaStyle); + std::unique_ptr<Typeface> boldItalic( + Typeface::createRelative(base.get(), Typeface::kBoldItalic)); + EXPECT_EQ(700, boldItalic->fStyle.weight()); + EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, boldItalic->fStyle.slant()); + EXPECT_EQ(Typeface::kBoldItalic, boldItalic->fAPIStyle); } TEST(TypefaceTest, createAbsolute) { // In Java, - // new Typeface.Builder(invalid).setFallback("sans-serif").setWeight(400).setItalic(false) + // new + // Typeface.Builder(invalid).setFallback("sans-serif").setWeight(400).setItalic(false) // .build(); std::unique_ptr<Typeface> regular(Typeface::createAbsolute(nullptr, 400, false)); - EXPECT_EQ(4, regular->fStyle.getWeight()); - EXPECT_FALSE(regular->fStyle.getItalic()); - EXPECT_EQ(SkTypeface::kNormal, regular->fSkiaStyle); + EXPECT_EQ(400, regular->fStyle.weight()); + EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, regular->fStyle.slant()); + EXPECT_EQ(Typeface::kNormal, regular->fAPIStyle); // In Java, - // new Typeface.Builder(invalid).setFallback("sans-serif").setWeight(700).setItalic(false) + // new + // Typeface.Builder(invalid).setFallback("sans-serif").setWeight(700).setItalic(false) // .build(); std::unique_ptr<Typeface> bold(Typeface::createAbsolute(nullptr, 700, false)); - EXPECT_EQ(7, bold->fStyle.getWeight()); - EXPECT_FALSE(bold->fStyle.getItalic()); - EXPECT_EQ(SkTypeface::kBold, bold->fSkiaStyle); + EXPECT_EQ(700, bold->fStyle.weight()); + EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, bold->fStyle.slant()); + EXPECT_EQ(Typeface::kBold, bold->fAPIStyle); // In Java, - // new Typeface.Builder(invalid).setFallback("sans-serif").setWeight(400).setItalic(true) + // new + // Typeface.Builder(invalid).setFallback("sans-serif").setWeight(400).setItalic(true) // .build(); std::unique_ptr<Typeface> italic(Typeface::createAbsolute(nullptr, 400, true)); - EXPECT_EQ(4, italic->fStyle.getWeight()); - EXPECT_TRUE(italic->fStyle.getItalic()); - EXPECT_EQ(SkTypeface::kItalic, italic->fSkiaStyle); + EXPECT_EQ(400, italic->fStyle.weight()); + EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, italic->fStyle.slant()); + EXPECT_EQ(Typeface::kItalic, italic->fAPIStyle); // In Java, - // new Typeface.Builder(invalid).setFallback("sans-serif").setWeight(700).setItalic(true) + // new + // Typeface.Builder(invalid).setFallback("sans-serif").setWeight(700).setItalic(true) // .build(); std::unique_ptr<Typeface> boldItalic(Typeface::createAbsolute(nullptr, 700, true)); - EXPECT_EQ(7, boldItalic->fStyle.getWeight()); - EXPECT_TRUE(boldItalic->fStyle.getItalic()); - EXPECT_EQ(SkTypeface::kBoldItalic, boldItalic->fSkiaStyle); + EXPECT_EQ(700, boldItalic->fStyle.weight()); + EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, boldItalic->fStyle.slant()); + EXPECT_EQ(Typeface::kBoldItalic, boldItalic->fAPIStyle); // In Java, - // new Typeface.Builder(invalid).setFallback("sans-serif").setWeight(1100).setItalic(true) + // new + // Typeface.Builder(invalid).setFallback("sans-serif").setWeight(1100).setItalic(true) // .build(); std::unique_ptr<Typeface> over1000(Typeface::createAbsolute(nullptr, 1100, false)); - EXPECT_EQ(10, over1000->fStyle.getWeight()); - EXPECT_FALSE(over1000->fStyle.getItalic()); - EXPECT_EQ(SkTypeface::kBold, over1000->fSkiaStyle); + EXPECT_EQ(1000, over1000->fStyle.weight()); + EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, over1000->fStyle.slant()); + EXPECT_EQ(Typeface::kBold, over1000->fAPIStyle); } TEST(TypefaceTest, createFromFamilies_Single) { - // In Java, new Typeface.Builder("Roboto-Regular.ttf").setWeight(400).setItalic(false).build(); + // In Java, new + // Typeface.Builder("Roboto-Regular.ttf").setWeight(400).setItalic(false).build(); std::unique_ptr<Typeface> regular( Typeface::createFromFamilies(makeSingleFamlyVector(kRobotoRegular), 400, false)); - EXPECT_EQ(4, regular->fStyle.getWeight()); - EXPECT_FALSE(regular->fStyle.getItalic()); - EXPECT_EQ(SkTypeface::kNormal, regular->fSkiaStyle); + EXPECT_EQ(400, regular->fStyle.weight()); + EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, regular->fStyle.slant()); + EXPECT_EQ(Typeface::kNormal, regular->fAPIStyle); - // In Java, new Typeface.Builder("Roboto-Bold.ttf").setWeight(700).setItalic(false).build(); + // In Java, new + // Typeface.Builder("Roboto-Bold.ttf").setWeight(700).setItalic(false).build(); std::unique_ptr<Typeface> bold( Typeface::createFromFamilies(makeSingleFamlyVector(kRobotoBold), 700, false)); - EXPECT_EQ(7, bold->fStyle.getWeight()); - EXPECT_FALSE(bold->fStyle.getItalic()); - EXPECT_EQ(SkTypeface::kBold, bold->fSkiaStyle); + EXPECT_EQ(700, bold->fStyle.weight()); + EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, bold->fStyle.slant()); + EXPECT_EQ(Typeface::kBold, bold->fAPIStyle); - // In Java, new Typeface.Builder("Roboto-Italic.ttf").setWeight(400).setItalic(true).build(); + // In Java, new + // Typeface.Builder("Roboto-Italic.ttf").setWeight(400).setItalic(true).build(); std::unique_ptr<Typeface> italic( Typeface::createFromFamilies(makeSingleFamlyVector(kRobotoItalic), 400, true)); - EXPECT_EQ(4, italic->fStyle.getWeight()); - EXPECT_TRUE(italic->fStyle.getItalic()); - EXPECT_EQ(SkTypeface::kItalic, italic->fSkiaStyle); + EXPECT_EQ(400, italic->fStyle.weight()); + EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, italic->fStyle.slant()); + EXPECT_EQ(Typeface::kItalic, italic->fAPIStyle); // In Java, - // new Typeface.Builder("Roboto-BoldItalic.ttf").setWeight(700).setItalic(true).build(); + // new + // Typeface.Builder("Roboto-BoldItalic.ttf").setWeight(700).setItalic(true).build(); std::unique_ptr<Typeface> boldItalic( Typeface::createFromFamilies(makeSingleFamlyVector(kRobotoBoldItalic), 700, true)); - EXPECT_EQ(7, boldItalic->fStyle.getWeight()); - EXPECT_TRUE(boldItalic->fStyle.getItalic()); - EXPECT_EQ(SkTypeface::kItalic, italic->fSkiaStyle); + EXPECT_EQ(700, boldItalic->fStyle.weight()); + EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, boldItalic->fStyle.slant()); + EXPECT_EQ(Typeface::kItalic, italic->fAPIStyle); // In Java, - // new Typeface.Builder("Roboto-BoldItalic.ttf").setWeight(1100).setItalic(false).build(); + // new + // Typeface.Builder("Roboto-BoldItalic.ttf").setWeight(1100).setItalic(false).build(); std::unique_ptr<Typeface> over1000( Typeface::createFromFamilies(makeSingleFamlyVector(kRobotoBold), 1100, false)); - EXPECT_EQ(10, over1000->fStyle.getWeight()); - EXPECT_FALSE(over1000->fStyle.getItalic()); - EXPECT_EQ(SkTypeface::kBold, over1000->fSkiaStyle); + EXPECT_EQ(1000, over1000->fStyle.weight()); + EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, over1000->fStyle.slant()); + EXPECT_EQ(Typeface::kBold, over1000->fAPIStyle); } TEST(TypefaceTest, createFromFamilies_Single_resolveByTable) { // In Java, new Typeface.Builder("Roboto-Regular.ttf").build(); - std::unique_ptr<Typeface> regular( - Typeface::createFromFamilies(makeSingleFamlyVector(kRobotoRegular), - RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE)); - EXPECT_EQ(4, regular->fStyle.getWeight()); - EXPECT_FALSE(regular->fStyle.getItalic()); - EXPECT_EQ(SkTypeface::kNormal, regular->fSkiaStyle); + std::unique_ptr<Typeface> regular(Typeface::createFromFamilies( + makeSingleFamlyVector(kRobotoRegular), RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE)); + EXPECT_EQ(400, regular->fStyle.weight()); + EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, regular->fStyle.slant()); + EXPECT_EQ(Typeface::kNormal, regular->fAPIStyle); // In Java, new Typeface.Builder("Roboto-Bold.ttf").build(); - std::unique_ptr<Typeface> bold( - Typeface::createFromFamilies(makeSingleFamlyVector(kRobotoBold), - RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE)); - EXPECT_EQ(7, bold->fStyle.getWeight()); - EXPECT_FALSE(bold->fStyle.getItalic()); - EXPECT_EQ(SkTypeface::kBold, bold->fSkiaStyle); + std::unique_ptr<Typeface> bold(Typeface::createFromFamilies( + makeSingleFamlyVector(kRobotoBold), RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE)); + EXPECT_EQ(700, bold->fStyle.weight()); + EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, bold->fStyle.slant()); + EXPECT_EQ(Typeface::kBold, bold->fAPIStyle); // In Java, new Typeface.Builder("Roboto-Italic.ttf").build(); - std::unique_ptr<Typeface> italic( - Typeface::createFromFamilies(makeSingleFamlyVector(kRobotoItalic), - RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE)); - EXPECT_EQ(4, italic->fStyle.getWeight()); - EXPECT_TRUE(italic->fStyle.getItalic()); - EXPECT_EQ(SkTypeface::kItalic, italic->fSkiaStyle); + std::unique_ptr<Typeface> italic(Typeface::createFromFamilies( + makeSingleFamlyVector(kRobotoItalic), RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE)); + EXPECT_EQ(400, italic->fStyle.weight()); + EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, italic->fStyle.slant()); + EXPECT_EQ(Typeface::kItalic, italic->fAPIStyle); // In Java, new Typeface.Builder("Roboto-BoldItalic.ttf").build(); std::unique_ptr<Typeface> boldItalic( Typeface::createFromFamilies(makeSingleFamlyVector(kRobotoBoldItalic), - RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE)); - EXPECT_EQ(7, boldItalic->fStyle.getWeight()); - EXPECT_TRUE(boldItalic->fStyle.getItalic()); - EXPECT_EQ(SkTypeface::kItalic, italic->fSkiaStyle); + RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE)); + EXPECT_EQ(700, boldItalic->fStyle.weight()); + EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, boldItalic->fStyle.slant()); + EXPECT_EQ(Typeface::kItalic, italic->fAPIStyle); } TEST(TypefaceTest, createFromFamilies_Family) { std::vector<std::shared_ptr<minikin::FontFamily>> families = { buildFamily(kRobotoRegular), buildFamily(kRobotoBold), buildFamily(kRobotoItalic), - buildFamily(kRobotoBoldItalic) - }; - std::unique_ptr<Typeface> typeface(Typeface::createFromFamilies(std::move(families), - RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE)); - EXPECT_EQ(4, typeface->fStyle.getWeight()); - EXPECT_FALSE(typeface->fStyle.getItalic()); + buildFamily(kRobotoBoldItalic)}; + std::unique_ptr<Typeface> typeface(Typeface::createFromFamilies( + std::move(families), RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE)); + EXPECT_EQ(400, typeface->fStyle.weight()); + EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, typeface->fStyle.slant()); } TEST(TypefaceTest, createFromFamilies_Family_withoutRegular) { std::vector<std::shared_ptr<minikin::FontFamily>> families = { - buildFamily(kRobotoBold), buildFamily(kRobotoItalic), buildFamily(kRobotoBoldItalic) - }; - std::unique_ptr<Typeface> typeface(Typeface::createFromFamilies(std::move(families), - RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE)); - EXPECT_EQ(7, typeface->fStyle.getWeight()); - EXPECT_FALSE(typeface->fStyle.getItalic()); + buildFamily(kRobotoBold), buildFamily(kRobotoItalic), buildFamily(kRobotoBoldItalic)}; + std::unique_ptr<Typeface> typeface(Typeface::createFromFamilies( + std::move(families), RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE)); + EXPECT_EQ(700, typeface->fStyle.weight()); + EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, typeface->fStyle.slant()); } } // namespace diff --git a/libs/hwui/tests/unit/VectorDrawableAtlasTests.cpp b/libs/hwui/tests/unit/VectorDrawableAtlasTests.cpp index 17f8176bcfdc..0c95fdd42851 100644 --- a/libs/hwui/tests/unit/VectorDrawableAtlasTests.cpp +++ b/libs/hwui/tests/unit/VectorDrawableAtlasTests.cpp @@ -16,9 +16,9 @@ #include <gtest/gtest.h> -#include "tests/common/TestUtils.h" #include <GrRectanizer.h> #include "pipeline/skia/VectorDrawableAtlas.h" +#include "tests/common/TestUtils.h" using namespace android; using namespace android::uirenderer; @@ -26,16 +26,16 @@ using namespace android::uirenderer::renderthread; using namespace android::uirenderer::skiapipeline; RENDERTHREAD_SKIA_PIPELINE_TEST(VectorDrawableAtlas, addGetRemove) { - VectorDrawableAtlas atlas(100*100); + VectorDrawableAtlas atlas(100 * 100); atlas.prepareForDraw(renderThread.getGrContext()); - //create 150 rects 10x10, which won't fit in the atlas (atlas can fit no more than 100 rects) + // create 150 rects 10x10, which won't fit in the atlas (atlas can fit no more than 100 rects) const int MAX_RECTS = 150; AtlasEntry VDRects[MAX_RECTS]; sk_sp<SkSurface> atlasSurface; - //check we are able to allocate new rects - //check that rects in the atlas do not intersect + // check we are able to allocate new rects + // check that rects in the atlas do not intersect for (uint32_t i = 0; i < MAX_RECTS; i++) { VDRects[i] = atlas.requestNewEntry(10, 10, renderThread.getGrContext()); if (0 == i) { @@ -45,7 +45,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(VectorDrawableAtlas, addGetRemove) { ASSERT_TRUE(VDRects[i].surface.get() != nullptr); ASSERT_TRUE(VDRects[i].rect.width() == 10 && VDRects[i].rect.height() == 10); - //nothing in the atlas should intersect + // nothing in the atlas should intersect if (atlasSurface.get() == VDRects[i].surface.get()) { for (uint32_t j = 0; j < i; j++) { if (atlasSurface.get() == VDRects[j].surface.get()) { @@ -55,16 +55,16 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(VectorDrawableAtlas, addGetRemove) { } } - //first 1/3 rects should all be in the same surface - for (uint32_t i = 1; i < MAX_RECTS/3; i++) { + // first 1/3 rects should all be in the same surface + for (uint32_t i = 1; i < MAX_RECTS / 3; i++) { ASSERT_NE(VDRects[i].key, VDRects[0].key); ASSERT_EQ(VDRects[i].surface.get(), atlasSurface.get()); } - //first rect is using atlas and last is a standalone surface - ASSERT_NE(VDRects[0].surface.get(), VDRects[MAX_RECTS-1].surface.get()); + // first rect is using atlas and last is a standalone surface + ASSERT_NE(VDRects[0].surface.get(), VDRects[MAX_RECTS - 1].surface.get()); - //check getEntry returns the same surfaces that we had created + // check getEntry returns the same surfaces that we had created for (uint32_t i = 0; i < MAX_RECTS; i++) { auto VDRect = atlas.getEntry(VDRects[i].key); ASSERT_TRUE(VDRect.key != INVALID_ATLAS_KEY); @@ -74,9 +74,9 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(VectorDrawableAtlas, addGetRemove) { atlas.releaseEntry(VDRect.key); } - //check that any new rects will be allocated in the atlas, even that rectanizer is full. - //rects in the atlas should not intersect. - for (uint32_t i = 0; i < MAX_RECTS/3; i++) { + // check that any new rects will be allocated in the atlas, even that rectanizer is full. + // rects in the atlas should not intersect. + for (uint32_t i = 0; i < MAX_RECTS / 3; i++) { VDRects[i] = atlas.requestNewEntry(10, 10, renderThread.getGrContext()); ASSERT_TRUE(VDRects[i].key != INVALID_ATLAS_KEY); ASSERT_EQ(VDRects[i].surface.get(), atlasSurface.get()); @@ -87,25 +87,24 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(VectorDrawableAtlas, addGetRemove) { } } - RENDERTHREAD_SKIA_PIPELINE_TEST(VectorDrawableAtlas, disallowSharedSurface) { - VectorDrawableAtlas atlas(100*100); - //don't allow to use a shared surface + VectorDrawableAtlas atlas(100 * 100); + // don't allow to use a shared surface atlas.setStorageMode(VectorDrawableAtlas::StorageMode::disallowSharedSurface); atlas.prepareForDraw(renderThread.getGrContext()); - //create 150 rects 10x10, which won't fit in the atlas (atlas can fit no more than 100 rects) + // create 150 rects 10x10, which won't fit in the atlas (atlas can fit no more than 100 rects) const int MAX_RECTS = 150; AtlasEntry VDRects[MAX_RECTS]; - //check we are able to allocate new rects - //check that rects in the atlas use unique surfaces + // check we are able to allocate new rects + // check that rects in the atlas use unique surfaces for (uint32_t i = 0; i < MAX_RECTS; i++) { VDRects[i] = atlas.requestNewEntry(10, 10, renderThread.getGrContext()); ASSERT_TRUE(VDRects[i].key != INVALID_ATLAS_KEY); ASSERT_TRUE(VDRects[i].surface.get() != nullptr); ASSERT_TRUE(VDRects[i].rect.width() == 10 && VDRects[i].rect.height() == 10); - //nothing in the atlas should use the same surface + // nothing in the atlas should use the same surface for (uint32_t j = 0; j < i; j++) { ASSERT_NE(VDRects[i].surface.get(), VDRects[j].surface.get()); } @@ -113,17 +112,17 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(VectorDrawableAtlas, disallowSharedSurface) { } RENDERTHREAD_SKIA_PIPELINE_TEST(VectorDrawableAtlas, repack) { - VectorDrawableAtlas atlas(100*100); + VectorDrawableAtlas atlas(100 * 100); ASSERT_FALSE(atlas.isFragmented()); atlas.prepareForDraw(renderThread.getGrContext()); ASSERT_FALSE(atlas.isFragmented()); - //create 150 rects 10x10, which won't fit in the atlas (atlas can fit no more than 100 rects) + // create 150 rects 10x10, which won't fit in the atlas (atlas can fit no more than 100 rects) const int MAX_RECTS = 150; AtlasEntry VDRects[MAX_RECTS]; sk_sp<SkSurface> atlasSurface; - //fill the atlas with check we are able to allocate new rects + // fill the atlas with check we are able to allocate new rects for (uint32_t i = 0; i < MAX_RECTS; i++) { VDRects[i] = atlas.requestNewEntry(10, 10, renderThread.getGrContext()); if (0 == i) { @@ -134,13 +133,13 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(VectorDrawableAtlas, repack) { ASSERT_FALSE(atlas.isFragmented()); - //first 1/3 rects should all be in the same surface - for (uint32_t i = 1; i < MAX_RECTS/3; i++) { + // first 1/3 rects should all be in the same surface + for (uint32_t i = 1; i < MAX_RECTS / 3; i++) { ASSERT_NE(VDRects[i].key, VDRects[0].key); ASSERT_EQ(VDRects[i].surface.get(), atlasSurface.get()); } - //release all entries + // release all entries for (uint32_t i = 0; i < MAX_RECTS; i++) { auto VDRect = atlas.getEntry(VDRects[i].key); ASSERT_TRUE(VDRect.key != INVALID_ATLAS_KEY); @@ -149,9 +148,9 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(VectorDrawableAtlas, repack) { ASSERT_FALSE(atlas.isFragmented()); - //allocate 4x4 rects, which will fragment the atlas badly, because each entry occupies a 10x10 - //area - for (uint32_t i = 0; i < 4*MAX_RECTS; i++) { + // allocate 4x4 rects, which will fragment the atlas badly, because each entry occupies a 10x10 + // area + for (uint32_t i = 0; i < 4 * MAX_RECTS; i++) { AtlasEntry entry = atlas.requestNewEntry(4, 4, renderThread.getGrContext()); ASSERT_TRUE(entry.key != INVALID_ATLAS_KEY); } diff --git a/libs/hwui/tests/unit/VectorDrawableTests.cpp b/libs/hwui/tests/unit/VectorDrawableTests.cpp index 1c2156765fe6..e7496f707318 100644 --- a/libs/hwui/tests/unit/VectorDrawableTests.cpp +++ b/libs/hwui/tests/unit/VectorDrawableTests.cpp @@ -33,195 +33,200 @@ struct TestData { }; const static TestData sTestDataSet[] = { - // TestData with scientific notation -2e3 etc. - { - // Path - "M2.000000,22.000000l20.000000,0.000000 1e0-2e3z", - { - // Verbs - {'M', 'l', 'z'}, - // Verb sizes - {2, 4, 0}, - // Points - {2, 22, 20, 0, 1, -2000}, - }, - [](SkPath* outPath) { - outPath->moveTo(2, 22); - outPath->rLineTo(20, 0); - outPath->rLineTo(1, -2000); - outPath->close(); - outPath->moveTo(2, 22); - } - }, - - // Comprehensive data, containing all the verbs possible. - { - // Path - "M 1 1 m 2 2, l 3 3 L 3 3 H 4 h4 V5 v5, Q6 6 6 6 q 6 6 6 6t 7 7 T 7 7 C 8 8 8 8 8 8 c 8 8 8 8 8 8 S 9 9 9 9 s 9 9 9 9 A 10 10 0 1 1 10 10 a 10 10 0 1 1 10 10", - { - // Verbs - {'M', 'm', 'l', 'L', 'H', 'h', 'V', 'v', 'Q', 'q', 't', 'T', 'C', 'c', 'S', 's', 'A', 'a'}, - // VerbSizes - {2, 2, 2, 2, 1, 1, 1, 1, 4, 4, 2, 2, 6, 6, 4, 4, 7, 7}, - // Points - {1.0, 1.0, 2.0, 2.0, 3.0, 3.0, 3.0, 3.0, 4.0, 4.0, 5.0, 5.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 7.0, 7.0, 7.0, 7.0, 8.0, 8.0, 8.0, 8.0, 8.0, 8.0, 8.0, 8.0, 8.0, 8.0, 8.0, 8.0, 9.0, 9.0, 9.0, 9.0, 9.0, 9.0, 9.0, 9.0, 10.0, 10.0, 0.0, 1.0, 1.0, 10.0, 10.0, 10.0, 10.0, 0.0, 1.0, 1.0, 10.0, 10.0, } - }, - [](SkPath* outPath) { - outPath->moveTo(1.0, 1.0); - outPath->rMoveTo(2.0, 2.0); - outPath->rLineTo(3.0, 3.0); - outPath->lineTo(3.0, 3.0); - outPath->lineTo(4.0, 3.0); - outPath->rLineTo(4.0, 0); - outPath->lineTo(8.0, 5.0); - outPath->rLineTo(0, 5.0); - outPath->quadTo(6.0, 6.0, 6.0, 6.0); - outPath->rQuadTo(6.0, 6.0, 6.0, 6.0); - outPath->rQuadTo(0.0, 0.0, 7.0, 7.0); - outPath->quadTo(26.0, 26.0, 7.0, 7.0); - outPath->cubicTo(8.0, 8.0, 8.0, 8.0, 8.0, 8.0); - outPath->rCubicTo(8.0, 8.0, 8.0, 8.0, 8.0, 8.0); - outPath->cubicTo(16.0, 16.0, 9.0, 9.0, 9.0, 9.0); - outPath->rCubicTo(0.0, 0.0, 9.0, 9.0, 9.0, 9.0); - outPath->cubicTo(18.447775037328352, 20.404243860300607, 17.998389141249767, 22.8911717921705, 16.737515350332117, 24.986664170401575); - outPath->cubicTo(15.476641559414468, 27.08215654863265, 13.489843598291483, 28.644011882390082, 11.155893964798905, 29.37447073281729); - outPath->cubicTo(8.821944331306327, 30.1049295832445, 6.299226382436471, 29.954422532383525, 4.0686829203897235, 28.951642951534332); - outPath->cubicTo(1.838139458342976, 27.94886337068514, 0.05113662931485696, 26.161860541657013, -0.9516429515343354, 23.931317079610267); - outPath->cubicTo(-1.9544225323835278, 21.70077361756352, -2.1049295832444987, 19.178055668693663, -1.37447073281729, 16.844106035201087); - outPath->cubicTo(-0.6440118823900814, 14.51015640170851, 0.9178434513673546, 12.523358440585524, 3.0133358295984305, 11.262484649667876); - outPath->cubicTo(5.108828207829506, 10.001610858750228, 7.5957561396993984, 9.552224962671648, 10.000000000000005, 10.0); - outPath->cubicTo(10.0, 7.348852265086975, 11.054287646850167, 4.803576729418881, 12.928932188134523, 2.9289321881345254); - outPath->cubicTo(14.803576729418879, 1.0542876468501696, 17.348852265086972, 4.870079381441987E-16, 19.999999999999996, 0.0); - outPath->cubicTo(22.65114773491302, -4.870079381441987E-16, 25.19642327058112, 1.0542876468501678, 27.071067811865476, 2.9289321881345227); - outPath->cubicTo(28.94571235314983, 4.803576729418878, 30.0, 7.348852265086974, 30.0, 9.999999999999998); - outPath->cubicTo(30.0, 12.651147734913023, 28.94571235314983, 15.19642327058112, 27.071067811865476, 17.071067811865476); - outPath->cubicTo(25.19642327058112, 18.94571235314983, 22.651147734913028, 20.0, 20.000000000000004, 20.0); - } - }, - - // Check box VectorDrawable path data - { - // Path - "M 0.0,-1.0 l 0.0,0.0 c 0.5522847498,0.0 1.0,0.4477152502 1.0,1.0 l 0.0,0.0 c 0.0,0.5522847498 -0.4477152502,1.0 -1.0,1.0 l 0.0,0.0 c -0.5522847498,0.0 -1.0,-0.4477152502 -1.0,-1.0 l 0.0,0.0 c 0.0,-0.5522847498 0.4477152502,-1.0 1.0,-1.0 Z M 7.0,-9.0 c 0.0,0.0 -14.0,0.0 -14.0,0.0 c -1.1044921875,0.0 -2.0,0.8955078125 -2.0,2.0 c 0.0,0.0 0.0,14.0 0.0,14.0 c 0.0,1.1044921875 0.8955078125,2.0 2.0,2.0 c 0.0,0.0 14.0,0.0 14.0,0.0 c 1.1044921875,0.0 2.0,-0.8955078125 2.0,-2.0 c 0.0,0.0 0.0,-14.0 0.0,-14.0 c 0.0,-1.1044921875 -0.8955078125,-2.0 -2.0,-2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z", - { - {'M', 'l', 'c', 'l', 'c', 'l', 'c', 'l', 'c', 'Z', 'M', 'c', 'c', 'c', 'c', 'c', 'c', 'c', 'c', 'c', 'Z'}, - {2, 2, 6, 2, 6, 2, 6, 2, 6, 0, 2, 6, 6, 6, 6, 6, 6, 6, 6, 6, 0}, - {0.0, -1.0, 0.0, 0.0, 0.5522848, 0.0, 1.0, 0.44771525, 1.0, 1.0, 0.0, 0.0, 0.0, 0.5522848, -0.44771525, 1.0, -1.0, 1.0, 0.0, 0.0, -0.5522848, 0.0, -1.0, -0.44771525, -1.0, -1.0, 0.0, 0.0, 0.0, -0.5522848, 0.44771525, -1.0, 1.0, -1.0, 7.0, -9.0, 0.0, 0.0, -14.0, 0.0, -14.0, 0.0, -1.1044922, 0.0, -2.0, 0.8955078, -2.0, 2.0, 0.0, 0.0, 0.0, 14.0, 0.0, 14.0, 0.0, 1.1044922, 0.8955078, 2.0, 2.0, 2.0, 0.0, 0.0, 14.0, 0.0, 14.0, 0.0, 1.1044922, 0.0, 2.0, -0.8955078, 2.0, -2.0, 0.0, 0.0, 0.0, -14.0, 0.0, -14.0, 0.0, -1.1044922, -0.8955078, -2.0, -2.0, -2.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, - }, - [](SkPath* outPath) { - outPath->moveTo(0.0, -1.0); - outPath->rLineTo(0.0, 0.0); - outPath->rCubicTo(0.5522848, 0.0, 1.0, 0.44771525, 1.0, 1.0); - outPath->rLineTo(0.0, 0.0); - outPath->rCubicTo(0.0, 0.5522848, -0.44771525, 1.0, -1.0, 1.0); - outPath->rLineTo(0.0, 0.0); - outPath->rCubicTo(-0.5522848, 0.0, -1.0, -0.44771525, -1.0, -1.0); - outPath->rLineTo(0.0, 0.0); - outPath->rCubicTo(0.0, -0.5522848, 0.44771525, -1.0, 1.0, -1.0); - outPath->close(); - outPath->moveTo(0.0, -1.0); - outPath->moveTo(7.0, -9.0); - outPath->rCubicTo(0.0, 0.0, -14.0, 0.0, -14.0, 0.0); - outPath->rCubicTo(-1.1044922, 0.0, -2.0, 0.8955078, -2.0, 2.0); - outPath->rCubicTo(0.0, 0.0, 0.0, 14.0, 0.0, 14.0); - outPath->rCubicTo(0.0, 1.1044922, 0.8955078, 2.0, 2.0, 2.0); - outPath->rCubicTo(0.0, 0.0, 14.0, 0.0, 14.0, 0.0); - outPath->rCubicTo(1.1044922, 0.0, 2.0, -0.8955078, 2.0, -2.0); - outPath->rCubicTo(0.0, 0.0, 0.0, -14.0, 0.0, -14.0); - outPath->rCubicTo(0.0, -1.1044922, -0.8955078, -2.0, -2.0, -2.0); - outPath->rCubicTo(0.0, 0.0, 0.0, 0.0, 0.0, 0.0); - outPath->close(); - outPath->moveTo(7.0, -9.0); - } - }, - - // pie1 in progress bar - { - "M300,70 a230,230 0 1,0 1,0 z", - { - {'M', 'a', 'z', }, - {2, 7, 0, }, - {300.0, 70.0, 230.0, 230.0, 0.0, 1.0, 0.0, 1.0, 0.0, }, - }, - [](SkPath* outPath) { - outPath->moveTo(300.0, 70.0); - outPath->cubicTo(239.06697794203706, 70.13246340443499, 180.6164396449267, 94.47383115953485, 137.6004913602211, 137.6302781499585); - outPath->cubicTo(94.58454307551551, 180.78672514038215, 70.43390412842275, 239.3163266242308, 70.50013586976587, 300.2494566687817); - outPath->cubicTo(70.56636761110899, 361.1825867133326, 94.84418775550249, 419.65954850554147, 137.9538527586204, 462.72238058830936); - outPath->cubicTo(181.06351776173827, 505.7852126710772, 239.5668339599056, 529.999456521097, 300.49999999999994, 529.999456521097); - outPath->cubicTo(361.43316604009436, 529.999456521097, 419.93648223826176, 505.78521267107726, 463.0461472413797, 462.7223805883093); - outPath->cubicTo(506.1558122444976, 419.65954850554135, 530.433632388891, 361.1825867133324, 530.4998641302341, 300.2494566687815); - outPath->cubicTo(530.5660958715771, 239.31632662423056, 506.4154569244844, 180.7867251403819, 463.3995086397787, 137.6302781499583); - outPath->cubicTo(420.383560355073, 94.47383115953468, 361.93302205796255, 70.13246340443492, 300.9999999999996, 70.00000000000003); - outPath->close(); - outPath->moveTo(300.0, 70.0); - } - }, - - // Random long data - { - // Path - "M5.3,13.2c-0.1,0.0 -0.3,0.0 -0.4,-0.1c-0.3,-0.2 -0.4,-0.7 -0.2,-1.0c1.3,-1.9 2.9,-3.4 4.9,-4.5c4.1,-2.2 9.3,-2.2 13.4,0.0c1.9,1.1 3.6,2.5 4.9,4.4c0.2,0.3 0.1,0.8 -0.2,1.0c-0.3,0.2 -0.8,0.1 -1.0,-0.2c-1.2,-1.7 -2.6,-3.0 -4.3,-4.0c-3.7,-2.0 -8.3,-2.0 -12.0,0.0c-1.7,0.9 -3.2,2.3 -4.3,4.0C5.7,13.1 5.5,13.2 5.3,13.2z", - { - // Verbs - {'M', 'c', 'c', 'c', 'c', 'c', 'c', 'c', 'c', 'c', 'c', 'C', 'z'}, - // Verb sizes - {2, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 0}, - // Points - {5.3, 13.2, -0.1, 0, -0.3, 0, -0.4, -0.1, -0.3, -0.2, -0.4, -0.7, -0.2, -1, 1.3, -1.9, 2.9, -3.4, 4.9, -4.5, 4.1, -2.2, 9.3, -2.2, 13.4, 0, 1.9, 1.1, 3.6, 2.5, 4.9, 4.4, 0.2, 0.3, 0.1, 0.8, -0.2, 1, -0.3, 0.2, -0.8, 0.1, -1, -0.2, -1.2, -1.7, -2.6, -3, -4.3, -4, -3.7, -2, -8.3, -2, -12, 0, -1.7, 0.9, -3.2, 2.3, -4.3, 4, 5.7, 13.1, 5.5, 13.2, 5.3, 13.2}, - }, - [](SkPath* outPath) { - outPath->moveTo(5.3, 13.2); - outPath->rCubicTo(-0.1, 0.0, -0.3, 0.0, -0.4, -0.1); - outPath->rCubicTo(-0.3, -0.2, -0.4, -0.7, -0.2, -1.0); - outPath->rCubicTo(1.3, -1.9, 2.9, -3.4, 4.9, -4.5); - outPath->rCubicTo(4.1, -2.2, 9.3, -2.2, 13.4, 0.0); - outPath->rCubicTo(1.9, 1.1, 3.6, 2.5, 4.9, 4.4); - outPath->rCubicTo(0.2, 0.3, 0.1, 0.8, -0.2, 1.0); - outPath->rCubicTo(-0.3, 0.2, -0.8, 0.1, -1.0, -0.2); - outPath->rCubicTo(-1.2, -1.7, -2.6, -3.0, -4.3, -4.0); - outPath->rCubicTo(-3.7, -2.0, -8.3, -2.0, -12.0, 0.0); - outPath->rCubicTo(-1.7, 0.9, -3.2, 2.3, -4.3, 4.0); - outPath->cubicTo(5.7, 13.1, 5.5, 13.2, 5.3, 13.2); - outPath->close(); - outPath->moveTo(5.3, 13.2); - } - }, - - // Extreme case with numbers and decimal points crunched together - { - // Path - "l0.0.0.5.0.0.5-0.5.0.0-.5z", - { - // Verbs - {'l', 'z'}, - // Verb sizes - {10, 0}, - // Points - {0, 0, 0.5, 0, 0, 0.5, -0.5, 0, 0, -0.5}, - }, - [](SkPath* outPath) { - outPath->rLineTo(0.0, 0.0); - outPath->rLineTo(0.5, 0.0); - outPath->rLineTo(0.0, 0.5); - outPath->rLineTo(-0.5, 0.0); - outPath->rLineTo(0.0, -0.5); - outPath->close(); - outPath->moveTo(0.0, 0.0); - } - }, - - // Empty test data - { - "", - { - // Verbs - {}, - {}, - {}, - }, - [](SkPath* outPath) {} - } + // TestData with scientific notation -2e3 etc. + {// Path + "M2.000000,22.000000l20.000000,0.000000 1e0-2e3z", + { + // Verbs + {'M', 'l', 'z'}, + // Verb sizes + {2, 4, 0}, + // Points + {2, 22, 20, 0, 1, -2000}, + }, + [](SkPath* outPath) { + outPath->moveTo(2, 22); + outPath->rLineTo(20, 0); + outPath->rLineTo(1, -2000); + outPath->close(); + outPath->moveTo(2, 22); + }}, + + // Comprehensive data, containing all the verbs possible. + {// Path + "M 1 1 m 2 2, l 3 3 L 3 3 H 4 h4 V5 v5, Q6 6 6 6 q 6 6 6 6t 7 7 T 7 7 C 8 8 8 8 8 8 c 8 8 " + "8 8 8 8 S 9 9 9 9 s 9 9 9 9 A 10 10 0 1 1 10 10 a 10 10 0 1 1 10 10", + {// Verbs + {'M', 'm', 'l', 'L', 'H', 'h', 'V', 'v', 'Q', 'q', 't', 'T', 'C', 'c', 'S', 's', 'A', + 'a'}, + // VerbSizes + {2, 2, 2, 2, 1, 1, 1, 1, 4, 4, 2, 2, 6, 6, 4, 4, 7, 7}, + // Points + { + 1.0, 1.0, 2.0, 2.0, 3.0, 3.0, 3.0, 3.0, 4.0, 4.0, 5.0, 5.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 7.0, 7.0, 7.0, 7.0, 8.0, 8.0, 8.0, 8.0, 8.0, 8.0, + 8.0, 8.0, 8.0, 8.0, 8.0, 8.0, 9.0, 9.0, 9.0, 9.0, 9.0, 9.0, 9.0, 9.0, 10.0, + 10.0, 0.0, 1.0, 1.0, 10.0, 10.0, 10.0, 10.0, 0.0, 1.0, 1.0, 10.0, 10.0, + }}, + [](SkPath* outPath) { + outPath->moveTo(1.0, 1.0); + outPath->rMoveTo(2.0, 2.0); + outPath->rLineTo(3.0, 3.0); + outPath->lineTo(3.0, 3.0); + outPath->lineTo(4.0, 3.0); + outPath->rLineTo(4.0, 0); + outPath->lineTo(8.0, 5.0); + outPath->rLineTo(0, 5.0); + outPath->quadTo(6.0, 6.0, 6.0, 6.0); + outPath->rQuadTo(6.0, 6.0, 6.0, 6.0); + outPath->rQuadTo(0.0, 0.0, 7.0, 7.0); + outPath->quadTo(26.0, 26.0, 7.0, 7.0); + outPath->cubicTo(8.0, 8.0, 8.0, 8.0, 8.0, 8.0); + outPath->rCubicTo(8.0, 8.0, 8.0, 8.0, 8.0, 8.0); + outPath->cubicTo(16.0, 16.0, 9.0, 9.0, 9.0, 9.0); + outPath->rCubicTo(0.0, 0.0, 9.0, 9.0, 9.0, 9.0); + outPath->arcTo(10.0, 10.0, 0.0, SkPath::kLarge_ArcSize, SkPath::kCW_Direction, 10.0, 10.0); + outPath->arcTo(10.0, 10.0, 0.0, SkPath::kLarge_ArcSize, SkPath::kCW_Direction, 20.0, 20.0); + }}, + + // Check box VectorDrawable path data + {// Path + "M 0.0,-1.0 l 0.0,0.0 c 0.5522847498,0.0 1.0,0.4477152502 1.0,1.0 l 0.0,0.0 c " + "0.0,0.5522847498 -0.4477152502,1.0 -1.0,1.0 l 0.0,0.0 c -0.5522847498,0.0 " + "-1.0,-0.4477152502 -1.0,-1.0 l 0.0,0.0 c 0.0,-0.5522847498 0.4477152502,-1.0 1.0,-1.0 Z " + "M 7.0,-9.0 c 0.0,0.0 -14.0,0.0 -14.0,0.0 c -1.1044921875,0.0 -2.0,0.8955078125 -2.0,2.0 " + "c 0.0,0.0 0.0,14.0 0.0,14.0 c 0.0,1.1044921875 0.8955078125,2.0 2.0,2.0 c 0.0,0.0 " + "14.0,0.0 14.0,0.0 c 1.1044921875,0.0 2.0,-0.8955078125 2.0,-2.0 c 0.0,0.0 0.0,-14.0 " + "0.0,-14.0 c 0.0,-1.1044921875 -0.8955078125,-2.0 -2.0,-2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z", + { + {'M', 'l', 'c', 'l', 'c', 'l', 'c', 'l', 'c', 'Z', 'M', + 'c', 'c', 'c', 'c', 'c', 'c', 'c', 'c', 'c', 'Z'}, + {2, 2, 6, 2, 6, 2, 6, 2, 6, 0, 2, 6, 6, 6, 6, 6, 6, 6, 6, 6, 0}, + {0.0, -1.0, 0.0, 0.0, 0.5522848, 0.0, 1.0, + 0.44771525, 1.0, 1.0, 0.0, 0.0, 0.0, 0.5522848, + -0.44771525, 1.0, -1.0, 1.0, 0.0, 0.0, -0.5522848, + 0.0, -1.0, -0.44771525, -1.0, -1.0, 0.0, 0.0, + 0.0, -0.5522848, 0.44771525, -1.0, 1.0, -1.0, 7.0, + -9.0, 0.0, 0.0, -14.0, 0.0, -14.0, 0.0, + -1.1044922, 0.0, -2.0, 0.8955078, -2.0, 2.0, 0.0, + 0.0, 0.0, 14.0, 0.0, 14.0, 0.0, 1.1044922, + 0.8955078, 2.0, 2.0, 2.0, 0.0, 0.0, 14.0, + 0.0, 14.0, 0.0, 1.1044922, 0.0, 2.0, -0.8955078, + 2.0, -2.0, 0.0, 0.0, 0.0, -14.0, 0.0, + -14.0, 0.0, -1.1044922, -0.8955078, -2.0, -2.0, -2.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + }, + [](SkPath* outPath) { + outPath->moveTo(0.0, -1.0); + outPath->rLineTo(0.0, 0.0); + outPath->rCubicTo(0.5522848, 0.0, 1.0, 0.44771525, 1.0, 1.0); + outPath->rLineTo(0.0, 0.0); + outPath->rCubicTo(0.0, 0.5522848, -0.44771525, 1.0, -1.0, 1.0); + outPath->rLineTo(0.0, 0.0); + outPath->rCubicTo(-0.5522848, 0.0, -1.0, -0.44771525, -1.0, -1.0); + outPath->rLineTo(0.0, 0.0); + outPath->rCubicTo(0.0, -0.5522848, 0.44771525, -1.0, 1.0, -1.0); + outPath->close(); + outPath->moveTo(0.0, -1.0); + outPath->moveTo(7.0, -9.0); + outPath->rCubicTo(0.0, 0.0, -14.0, 0.0, -14.0, 0.0); + outPath->rCubicTo(-1.1044922, 0.0, -2.0, 0.8955078, -2.0, 2.0); + outPath->rCubicTo(0.0, 0.0, 0.0, 14.0, 0.0, 14.0); + outPath->rCubicTo(0.0, 1.1044922, 0.8955078, 2.0, 2.0, 2.0); + outPath->rCubicTo(0.0, 0.0, 14.0, 0.0, 14.0, 0.0); + outPath->rCubicTo(1.1044922, 0.0, 2.0, -0.8955078, 2.0, -2.0); + outPath->rCubicTo(0.0, 0.0, 0.0, -14.0, 0.0, -14.0); + outPath->rCubicTo(0.0, -1.1044922, -0.8955078, -2.0, -2.0, -2.0); + outPath->rCubicTo(0.0, 0.0, 0.0, 0.0, 0.0, 0.0); + outPath->close(); + outPath->moveTo(7.0, -9.0); + }}, + + // pie1 in progress bar + {"M300,70 a230,230 0 1,0 1,0 z", + { + { + 'M', 'a', 'z', + }, + { + 2, 7, 0, + }, + { + 300.0, 70.0, 230.0, 230.0, 0.0, 1.0, 0.0, 1.0, 0.0, + }, + }, + [](SkPath* outPath) { + outPath->moveTo(300.0, 70.0); + outPath->arcTo(230.0, 230.0, 0.0, SkPath::kLarge_ArcSize, SkPath::kCCW_Direction, 301.0, 70.0); + outPath->close(); + outPath->moveTo(300.0, 70.0); + }}, + + // Random long data + {// Path + "M5.3,13.2c-0.1,0.0 -0.3,0.0 -0.4,-0.1c-0.3,-0.2 -0.4,-0.7 -0.2,-1.0c1.3,-1.9 2.9,-3.4 " + "4.9,-4.5c4.1,-2.2 9.3,-2.2 13.4,0.0c1.9,1.1 3.6,2.5 4.9,4.4c0.2,0.3 0.1,0.8 " + "-0.2,1.0c-0.3,0.2 -0.8,0.1 -1.0,-0.2c-1.2,-1.7 -2.6,-3.0 -4.3,-4.0c-3.7,-2.0 -8.3,-2.0 " + "-12.0,0.0c-1.7,0.9 -3.2,2.3 -4.3,4.0C5.7,13.1 5.5,13.2 5.3,13.2z", + { + // Verbs + {'M', 'c', 'c', 'c', 'c', 'c', 'c', 'c', 'c', 'c', 'c', 'C', 'z'}, + // Verb sizes + {2, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 0}, + // Points + {5.3, 13.2, -0.1, 0, -0.3, 0, -0.4, -0.1, -0.3, -0.2, -0.4, -0.7, -0.2, -1, + 1.3, -1.9, 2.9, -3.4, 4.9, -4.5, 4.1, -2.2, 9.3, -2.2, 13.4, 0, 1.9, 1.1, + 3.6, 2.5, 4.9, 4.4, 0.2, 0.3, 0.1, 0.8, -0.2, 1, -0.3, 0.2, -0.8, 0.1, + -1, -0.2, -1.2, -1.7, -2.6, -3, -4.3, -4, -3.7, -2, -8.3, -2, -12, 0, + -1.7, 0.9, -3.2, 2.3, -4.3, 4, 5.7, 13.1, 5.5, 13.2, 5.3, 13.2}, + }, + [](SkPath* outPath) { + outPath->moveTo(5.3, 13.2); + outPath->rCubicTo(-0.1, 0.0, -0.3, 0.0, -0.4, -0.1); + outPath->rCubicTo(-0.3, -0.2, -0.4, -0.7, -0.2, -1.0); + outPath->rCubicTo(1.3, -1.9, 2.9, -3.4, 4.9, -4.5); + outPath->rCubicTo(4.1, -2.2, 9.3, -2.2, 13.4, 0.0); + outPath->rCubicTo(1.9, 1.1, 3.6, 2.5, 4.9, 4.4); + outPath->rCubicTo(0.2, 0.3, 0.1, 0.8, -0.2, 1.0); + outPath->rCubicTo(-0.3, 0.2, -0.8, 0.1, -1.0, -0.2); + outPath->rCubicTo(-1.2, -1.7, -2.6, -3.0, -4.3, -4.0); + outPath->rCubicTo(-3.7, -2.0, -8.3, -2.0, -12.0, 0.0); + outPath->rCubicTo(-1.7, 0.9, -3.2, 2.3, -4.3, 4.0); + outPath->cubicTo(5.7, 13.1, 5.5, 13.2, 5.3, 13.2); + outPath->close(); + outPath->moveTo(5.3, 13.2); + }}, + + // Extreme case with numbers and decimal points crunched together + {// Path + "l0.0.0.5.0.0.5-0.5.0.0-.5z", + { + // Verbs + {'l', 'z'}, + // Verb sizes + {10, 0}, + // Points + {0, 0, 0.5, 0, 0, 0.5, -0.5, 0, 0, -0.5}, + }, + [](SkPath* outPath) { + outPath->rLineTo(0.0, 0.0); + outPath->rLineTo(0.5, 0.0); + outPath->rLineTo(0.0, 0.5); + outPath->rLineTo(-0.5, 0.0); + outPath->rLineTo(0.0, -0.5); + outPath->close(); + outPath->moveTo(0.0, 0.0); + }}, + + // Empty test data + {"", + { + // Verbs + {}, + {}, + {}, + }, + [](SkPath* outPath) {}} }; @@ -231,22 +236,21 @@ struct StringPath { }; const StringPath sStringPaths[] = { - {"3e...3", false}, // Not starting with a verb and ill-formatted float - {"L.M.F.A.O", false}, // No floats following verbs - {"m 1 1", true}, // Valid path data - {"\n \t z", true}, // Valid path data with leading spaces - {"1-2e34567", false}, // Not starting with a verb and ill-formatted float - {"f 4 5", false}, // Invalid verb - {"\r ", false} // Empty string + {"3e...3", false}, // Not starting with a verb and ill-formatted float + {"L.M.F.A.O", false}, // No floats following verbs + {"m 1 1", true}, // Valid path data + {"\n \t z", true}, // Valid path data with leading spaces + {"1-2e34567", false}, // Not starting with a verb and ill-formatted float + {"f 4 5", false}, // Invalid verb + {"\r ", false} // Empty string }; - static bool hasSameVerbs(const PathData& from, const PathData& to) { return from.verbs == to.verbs && from.verbSizes == to.verbSizes; } TEST(PathParser, parseStringForData) { - for (TestData testData: sTestDataSet) { + for (TestData testData : sTestDataSet) { PathParser::ParseResult result; // Test generated path data against the given data. PathData pathData; @@ -259,14 +263,14 @@ TEST(PathParser, parseStringForData) { PathParser::ParseResult result; PathData pathData; SkPath skPath; - PathParser::getPathDataFromAsciiString(&pathData, &result, - stringPath.stringPath, strlen(stringPath.stringPath)); + PathParser::getPathDataFromAsciiString(&pathData, &result, stringPath.stringPath, + strlen(stringPath.stringPath)); EXPECT_EQ(stringPath.isValid, !result.failureOccurred); } } TEST(VectorDrawableUtils, createSkPathFromPathData) { - for (TestData testData: sTestDataSet) { + for (TestData testData : sTestDataSet) { SkPath expectedPath; testData.skPathLamda(&expectedPath); SkPath actualPath; @@ -276,7 +280,7 @@ TEST(VectorDrawableUtils, createSkPathFromPathData) { } TEST(PathParser, parseAsciiStringForSkPath) { - for (TestData testData: sTestDataSet) { + for (TestData testData : sTestDataSet) { PathParser::ParseResult result; size_t length = strlen(testData.pathString); // Check the return value as well as the SkPath generated. @@ -293,14 +297,14 @@ TEST(PathParser, parseAsciiStringForSkPath) { PathParser::ParseResult result; SkPath skPath; PathParser::parseAsciiStringForSkPath(&skPath, &result, stringPath.stringPath, - strlen(stringPath.stringPath)); + strlen(stringPath.stringPath)); EXPECT_EQ(stringPath.isValid, !result.failureOccurred); } } TEST(VectorDrawableUtils, morphPathData) { - for (TestData fromData: sTestDataSet) { - for (TestData toData: sTestDataSet) { + for (TestData fromData : sTestDataSet) { + for (TestData toData : sTestDataSet) { bool canMorph = VectorDrawableUtils::canMorph(fromData.pathData, toData.pathData); if (fromData.pathData == toData.pathData) { EXPECT_TRUE(canMorph); @@ -314,11 +318,11 @@ TEST(VectorDrawableUtils, morphPathData) { TEST(VectorDrawableUtils, interpolatePathData) { // Interpolate path data with itself and every other path data - for (TestData fromData: sTestDataSet) { - for (TestData toData: sTestDataSet) { + for (TestData fromData : sTestDataSet) { + for (TestData toData : sTestDataSet) { PathData outData; bool success = VectorDrawableUtils::interpolatePathData(&outData, fromData.pathData, - toData.pathData, 0.5); + toData.pathData, 0.5); bool expectedToMorph = hasSameVerbs(fromData.pathData, toData.pathData); EXPECT_EQ(expectedToMorph, success); } @@ -335,12 +339,12 @@ TEST(VectorDrawableUtils, interpolatePathData) { PathData outData; // Interpolate the two path data with different fractions for (float fraction : fractions) { - bool success = VectorDrawableUtils::interpolatePathData( - &outData, fromPathData, toPathData, fraction); + bool success = VectorDrawableUtils::interpolatePathData(&outData, fromPathData, + toPathData, fraction); EXPECT_TRUE(success); for (size_t i = 0; i < outData.points.size(); i++) { - float expectedResult = fromPathData.points[i] * (1.0 - fraction) + - toPathData.points[i] * fraction; + float expectedResult = + fromPathData.points[i] * (1.0 - fraction) + toPathData.points[i] * fraction; EXPECT_TRUE(MathUtils::areEqual(expectedResult, outData.points[i])); } } @@ -348,7 +352,7 @@ TEST(VectorDrawableUtils, interpolatePathData) { } TEST(VectorDrawable, groupProperties) { - //TODO: Also need to test property sync and dirty flag when properties change. + // TODO: Also need to test property sync and dirty flag when properties change. VectorDrawable::Group group; VectorDrawable::Group::GroupProperties* properties = group.mutateProperties(); // Test default values, change values through setters and verify the change through getters. @@ -379,7 +383,6 @@ TEST(VectorDrawable, groupProperties) { EXPECT_EQ(0.0f, properties->getPivotY()); properties->setPivotY(1.0f); EXPECT_EQ(1.0f, properties->getPivotY()); - } TEST(VectorDrawable, drawPathWithoutIncrementingShaderRefCount) { @@ -402,5 +405,5 @@ TEST(VectorDrawable, drawPathWithoutIncrementingShaderRefCount) { EXPECT_TRUE(shader->unique()); } -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/tests/unit/main.cpp b/libs/hwui/tests/unit/main.cpp index cea84c057b63..406255d61d25 100644 --- a/libs/hwui/tests/unit/main.cpp +++ b/libs/hwui/tests/unit/main.cpp @@ -14,15 +14,15 @@ * limitations under the License. */ -#include "gtest/gtest.h" #include "gmock/gmock.h" +#include "gtest/gtest.h" #include "Caches.h" #include "debug/GlesDriver.h" #include "debug/NullGlesDriver.h" #include "hwui/Typeface.h" -#include "thread/TaskManager.h" #include "tests/common/LeakChecker.h" +#include "thread/TaskManager.h" #include <signal.h> @@ -31,17 +31,14 @@ using namespace android; using namespace android::uirenderer; static auto CRASH_SIGNALS = { - SIGABRT, - SIGSEGV, - SIGBUS, + SIGABRT, SIGSEGV, SIGBUS, }; static map<int, struct sigaction> gSigChain; static void gtestSigHandler(int sig, siginfo_t* siginfo, void* context) { auto testinfo = ::testing::UnitTest::GetInstance()->current_test_info(); - printf("[ FAILED ] %s.%s\n", testinfo->test_case_name(), - testinfo->name()); + printf("[ FAILED ] %s.%s\n", testinfo->test_case_name(), testinfo->name()); printf("[ FATAL! ] Process crashed, aborting tests!\n"); fflush(stdout); @@ -53,9 +50,7 @@ static void gtestSigHandler(int sig, siginfo_t* siginfo, void* context) { class TypefaceEnvironment : public testing::Environment { public: - virtual void SetUp() { - Typeface::setRobotoTypefaceForTest(); - } + virtual void SetUp() { Typeface::setRobotoTypefaceForTest(); } }; int main(int argc, char* argv[]) { @@ -83,4 +78,3 @@ int main(int argc, char* argv[]) { test::LeakChecker::checkForLeaks(); return ret; } - diff --git a/libs/hwui/thread/Barrier.h b/libs/hwui/thread/Barrier.h index 17f82ba9d67d..8faeee6b391a 100644 --- a/libs/hwui/thread/Barrier.h +++ b/libs/hwui/thread/Barrier.h @@ -24,8 +24,9 @@ namespace uirenderer { class Barrier { public: - explicit Barrier(Condition::WakeUpType type = Condition::WAKE_UP_ALL) : mType(type), mOpened(false) { } - ~Barrier() { } + explicit Barrier(Condition::WakeUpType type = Condition::WAKE_UP_ALL) + : mType(type), mOpened(false) {} + ~Barrier() {} void open() { Mutex::Autolock l(mLock); @@ -47,7 +48,7 @@ private: mutable Condition mCondition; }; -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android -#endif // ANDROID_HWUI_BARRIER_H +#endif // ANDROID_HWUI_BARRIER_H diff --git a/libs/hwui/thread/Future.h b/libs/hwui/thread/Future.h index 177eebd9b85f..45f3102492e3 100644 --- a/libs/hwui/thread/Future.h +++ b/libs/hwui/thread/Future.h @@ -24,11 +24,12 @@ namespace android { namespace uirenderer { -template<typename T> -class Future: public LightRefBase<Future<T> > { +template <typename T> +class Future : public LightRefBase<Future<T> > { public: - explicit Future(Condition::WakeUpType type = Condition::WAKE_UP_ONE): mBarrier(type), mResult() { } - ~Future() { } + explicit Future(Condition::WakeUpType type = Condition::WAKE_UP_ONE) + : mBarrier(type), mResult() {} + ~Future() {} /** * Returns the result of this future, blocking if @@ -52,7 +53,7 @@ private: T mResult; }; -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android -#endif // ANDROID_HWUI_FUTURE_H +#endif // ANDROID_HWUI_FUTURE_H diff --git a/libs/hwui/thread/Signal.h b/libs/hwui/thread/Signal.h index 93e6f4c27535..ffcd4b675a85 100644 --- a/libs/hwui/thread/Signal.h +++ b/libs/hwui/thread/Signal.h @@ -26,8 +26,9 @@ namespace uirenderer { class Signal { public: - explicit Signal(Condition::WakeUpType type = Condition::WAKE_UP_ALL) : mType(type), mSignaled(false) { } - ~Signal() { } + explicit Signal(Condition::WakeUpType type = Condition::WAKE_UP_ALL) + : mType(type), mSignaled(false) {} + ~Signal() {} void signal() { { @@ -52,7 +53,7 @@ private: mutable Condition mCondition; }; -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android -#endif // ANDROID_HWUI_SIGNAL_H +#endif // ANDROID_HWUI_SIGNAL_H diff --git a/libs/hwui/thread/Task.h b/libs/hwui/thread/Task.h index 7fcf5931c4b4..276a22f941fe 100644 --- a/libs/hwui/thread/Task.h +++ b/libs/hwui/thread/Task.h @@ -25,36 +25,30 @@ namespace android { namespace uirenderer { -class TaskBase: public RefBase { +class TaskBase : public RefBase { public: - TaskBase() { } - virtual ~TaskBase() { } + TaskBase() {} + virtual ~TaskBase() {} }; -template<typename T> -class Task: public TaskBase { +template <typename T> +class Task : public TaskBase { public: - Task(): mFuture(new Future<T>()) { } - virtual ~Task() { } + Task() : mFuture(new Future<T>()) {} + virtual ~Task() {} - T getResult() const { - return mFuture->get(); - } + T getResult() const { return mFuture->get(); } - void setResult(T result) { - mFuture->produce(result); - } + void setResult(T result) { mFuture->produce(result); } protected: - const sp<Future<T> >& future() const { - return mFuture; - } + const sp<Future<T> >& future() const { return mFuture; } private: sp<Future<T> > mFuture; }; -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android -#endif // ANDROID_HWUI_TASK_H +#endif // ANDROID_HWUI_TASK_H diff --git a/libs/hwui/thread/TaskManager.cpp b/libs/hwui/thread/TaskManager.cpp index d346b859526e..54b55e472095 100644 --- a/libs/hwui/thread/TaskManager.cpp +++ b/libs/hwui/thread/TaskManager.cpp @@ -17,8 +17,8 @@ #include <sys/resource.h> #include <sys/sysinfo.h> -#include "TaskManager.h" #include "Task.h" +#include "TaskManager.h" #include "TaskProcessor.h" #include "utils/MathUtils.h" @@ -129,5 +129,5 @@ void TaskManager::WorkerThread::exit() { mSignal.signal(); } -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/thread/TaskManager.h b/libs/hwui/thread/TaskManager.h index c9d12bc6044b..29b4fcdbfde9 100644 --- a/libs/hwui/thread/TaskManager.h +++ b/libs/hwui/thread/TaskManager.h @@ -58,7 +58,7 @@ private: template <typename T> friend class TaskProcessor; - template<typename T> + template <typename T> bool addTask(const sp<Task<T> >& task, const sp<TaskProcessor<T> >& processor) { return addTaskBase(sp<TaskBase>(task), sp<TaskProcessorBase>(processor)); } @@ -66,19 +66,18 @@ private: bool addTaskBase(const sp<TaskBase>& task, const sp<TaskProcessorBase>& processor); struct TaskWrapper { - TaskWrapper(): mTask(), mProcessor() { } + TaskWrapper() : mTask(), mProcessor() {} - TaskWrapper(const sp<TaskBase>& task, const sp<TaskProcessorBase>& processor): - mTask(task), mProcessor(processor) { - } + TaskWrapper(const sp<TaskBase>& task, const sp<TaskProcessorBase>& processor) + : mTask(task), mProcessor(processor) {} sp<TaskBase> mTask; sp<TaskProcessorBase> mProcessor; }; - class WorkerThread: public Thread { + class WorkerThread : public Thread { public: - explicit WorkerThread(const String8& name): mSignal(Condition::WAKE_UP_ONE), mName(name) { } + explicit WorkerThread(const String8& name) : mSignal(Condition::WAKE_UP_ONE), mName(name) {} bool addTask(const TaskWrapper& task); size_t getTaskCount() const; @@ -102,7 +101,7 @@ private: std::vector<sp<WorkerThread> > mThreads; }; -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android -#endif // ANDROID_HWUI_TASK_MANAGER_H +#endif // ANDROID_HWUI_TASK_MANAGER_H diff --git a/libs/hwui/thread/TaskProcessor.h b/libs/hwui/thread/TaskProcessor.h index 58674505c457..8117ae6409c4 100644 --- a/libs/hwui/thread/TaskProcessor.h +++ b/libs/hwui/thread/TaskProcessor.h @@ -25,19 +25,19 @@ namespace android { namespace uirenderer { -class TaskProcessorBase: public RefBase { +class TaskProcessorBase : public RefBase { public: - TaskProcessorBase() { } - virtual ~TaskProcessorBase() { }; + TaskProcessorBase() {} + virtual ~TaskProcessorBase(){}; virtual void process(const sp<TaskBase>& task) = 0; }; -template<typename T> -class TaskProcessor: public TaskProcessorBase { +template <typename T> +class TaskProcessor : public TaskProcessorBase { public: - explicit TaskProcessor(TaskManager* manager): mManager(manager) { } - virtual ~TaskProcessor() { } + explicit TaskProcessor(TaskManager* manager) : mManager(manager) {} + virtual ~TaskProcessor() {} void add(const sp<Task<T> >& task) { if (!addImpl(task)) { @@ -52,7 +52,7 @@ private: bool addImpl(const sp<Task<T> >& task); virtual void process(const sp<TaskBase>& task) override { - sp<Task<T> > realTask = static_cast<Task<T>* >(task.get()); + sp<Task<T> > realTask = static_cast<Task<T>*>(task.get()); // This is the right way to do it but sp<> doesn't play nice // sp<Task<T> > realTask = static_cast<sp<Task<T> > >(task); onProcess(realTask); @@ -61,7 +61,7 @@ private: TaskManager* mManager; }; -template<typename T> +template <typename T> bool TaskProcessor<T>::addImpl(const sp<Task<T> >& task) { if (mManager) { sp<TaskProcessor<T> > self(this); @@ -70,7 +70,7 @@ bool TaskProcessor<T>::addImpl(const sp<Task<T> >& task) { return false; } -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android -#endif // ANDROID_HWUI_TASK_PROCESSOR_H +#endif // ANDROID_HWUI_TASK_PROCESSOR_H diff --git a/libs/hwui/thread/ThreadBase.h b/libs/hwui/thread/ThreadBase.h new file mode 100644 index 000000000000..8068121f64cf --- /dev/null +++ b/libs/hwui/thread/ThreadBase.h @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef HWUI_THREADBASE_H +#define HWUI_THREADBASE_H + +#include "WorkQueue.h" +#include "utils/Macros.h" + +#include <utils/Looper.h> +#include <utils/Thread.h> + +#include <algorithm> + +namespace android::uirenderer { + +class ThreadBase : protected Thread { + PREVENT_COPY_AND_ASSIGN(ThreadBase); + +public: + ThreadBase() + : Thread(false) + , mLooper(new Looper(false)) + , mQueue([this]() { mLooper->wake(); }, mLock) {} + + WorkQueue& queue() { return mQueue; } + + void requestExit() { + Thread::requestExit(); + mLooper->wake(); + } + + void start(const char* name = "ThreadBase") { Thread::run(name); } + + void join() { Thread::join(); } + +protected: + void waitForWork() { + nsecs_t nextWakeup; + { + std::unique_lock lock{mLock}; + nextWakeup = mQueue.nextWakeup(lock); + } + int timeout = -1; + if (nextWakeup < std::numeric_limits<nsecs_t>::max()) { + timeout = ns2ms(nextWakeup - WorkQueue::clock::now()); + if (timeout < 0) timeout = 0; + } + int result = mLooper->pollOnce(timeout); + LOG_ALWAYS_FATAL_IF(result == Looper::POLL_ERROR, "RenderThread Looper POLL_ERROR!"); + } + + void processQueue() { mQueue.process(); } + + virtual bool threadLoop() override { + while (!exitPending()) { + waitForWork(); + processQueue(); + } + return false; + } + + sp<Looper> mLooper; + +private: + WorkQueue mQueue; + std::mutex mLock; +}; + +} // namespace android::uirenderer + +#endif // HWUI_THREADBASE_H diff --git a/libs/hwui/thread/WorkQueue.h b/libs/hwui/thread/WorkQueue.h new file mode 100644 index 000000000000..7a6e638e0568 --- /dev/null +++ b/libs/hwui/thread/WorkQueue.h @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef HWUI_WORKQUEUE_H +#define HWUI_WORKQUEUE_H + +#include "utils/Macros.h" + +#include <log/log.h> +#include <utils/Timers.h> + +#include <condition_variable> +#include <functional> +#include <future> +#include <mutex> +#include <variant> +#include <vector> + +namespace android::uirenderer { + +struct MonotonicClock { + static nsecs_t now() { return systemTime(CLOCK_MONOTONIC); } +}; + +class WorkQueue { + PREVENT_COPY_AND_ASSIGN(WorkQueue); + +public: + using clock = MonotonicClock; + +private: + struct WorkItem { + WorkItem() = delete; + WorkItem(const WorkItem& other) = delete; + WorkItem& operator=(const WorkItem& other) = delete; + WorkItem(WorkItem&& other) = default; + WorkItem& operator=(WorkItem&& other) = default; + + WorkItem(nsecs_t runAt, std::function<void()>&& work) + : runAt(runAt), work(std::move(work)) {} + + nsecs_t runAt; + std::function<void()> work; + }; + +public: + WorkQueue(std::function<void()>&& wakeFunc, std::mutex& lock) + : mWakeFunc(move(wakeFunc)), mLock(lock) {} + + void process() { + auto now = clock::now(); + std::vector<WorkItem> toProcess; + { + std::unique_lock _lock{mLock}; + if (mWorkQueue.empty()) return; + toProcess = std::move(mWorkQueue); + auto moveBack = find_if(std::begin(toProcess), std::end(toProcess), + [&now](WorkItem& item) { return item.runAt > now; }); + if (moveBack != std::end(toProcess)) { + mWorkQueue.reserve(std::distance(moveBack, std::end(toProcess)) + 5); + std::move(moveBack, std::end(toProcess), std::back_inserter(mWorkQueue)); + toProcess.erase(moveBack, std::end(toProcess)); + } + } + for (auto& item : toProcess) { + item.work(); + } + } + + template <class F> + void postAt(nsecs_t time, F&& func) { + enqueue(WorkItem{time, std::function<void()>(std::forward<F>(func))}); + } + + template <class F> + void postDelayed(nsecs_t delay, F&& func) { + enqueue(WorkItem{clock::now() + delay, std::function<void()>(std::forward<F>(func))}); + } + + template <class F> + void post(F&& func) { + postAt(0, std::forward<F>(func)); + } + + template <class F> + auto async(F&& func) -> std::future<decltype(func())> { + typedef std::packaged_task<decltype(func())()> task_t; + auto task = std::make_shared<task_t>(std::forward<F>(func)); + post([task]() { std::invoke(*task); }); + return task->get_future(); + } + + template <class F> + auto runSync(F&& func) -> decltype(func()) { + std::packaged_task<decltype(func())()> task{std::forward<F>(func)}; + post([&task]() { std::invoke(task); }); + return task.get_future().get(); + }; + + nsecs_t nextWakeup(std::unique_lock<std::mutex>& lock) { + if (mWorkQueue.empty()) { + return std::numeric_limits<nsecs_t>::max(); + } else { + return std::begin(mWorkQueue)->runAt; + } + } + +private: + void enqueue(WorkItem&& item) { + bool needsWakeup; + { + std::unique_lock _lock{mLock}; + auto insertAt = std::find_if( + std::begin(mWorkQueue), std::end(mWorkQueue), + [time = item.runAt](WorkItem & item) { return item.runAt > time; }); + needsWakeup = std::begin(mWorkQueue) == insertAt; + mWorkQueue.emplace(insertAt, std::move(item)); + } + if (needsWakeup) { + mWakeFunc(); + } + } + + std::function<void()> mWakeFunc; + + std::mutex& mLock; + std::vector<WorkItem> mWorkQueue; +}; + +} // namespace android::uirenderer + +#endif // HWUI_WORKQUEUE_H diff --git a/libs/hwui/utils/Blur.cpp b/libs/hwui/utils/Blur.cpp index 9b70765ee8ad..1bc5646993c9 100644 --- a/libs/hwui/utils/Blur.cpp +++ b/libs/hwui/utils/Blur.cpp @@ -38,7 +38,7 @@ float Blur::convertSigmaToRadius(float sigma) { // is within the conversion error tolerance then we attempt to snap to the // original integer boundary. uint32_t Blur::convertRadiusToInt(float radius) { - const float radiusCeil = ceilf(radius); + const float radiusCeil = ceilf(radius); if (MathUtils::areEqual(radiusCeil, radius)) { return radiusCeil; } @@ -75,46 +75,45 @@ void Blur::generateGaussianWeights(float* weights, float radius) { // the blur calculations // precompute some values float coeff1 = 1.0f / (sqrt(2.0f * pi) * sigma); - float coeff2 = - 1.0f / (2.0f * sigma * sigma); + float coeff2 = -1.0f / (2.0f * sigma * sigma); float normalizeFactor = 0.0f; - for (int32_t r = -intRadius; r <= intRadius; r ++) { - float floatR = (float) r; + for (int32_t r = -intRadius; r <= intRadius; r++) { + float floatR = (float)r; weights[r + intRadius] = coeff1 * pow(e, floatR * floatR * coeff2); normalizeFactor += weights[r + intRadius]; } - //Now we need to normalize the weights because all our coefficients need to add up to one + // Now we need to normalize the weights because all our coefficients need to add up to one normalizeFactor = 1.0f / normalizeFactor; - for (int32_t r = -intRadius; r <= intRadius; r ++) { + for (int32_t r = -intRadius; r <= intRadius; r++) { weights[r + intRadius] *= normalizeFactor; } } -void Blur::horizontal(float* weights, int32_t radius, - const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) { +void Blur::horizontal(float* weights, int32_t radius, const uint8_t* source, uint8_t* dest, + int32_t width, int32_t height) { float blurredPixel = 0.0f; float currentPixel = 0.0f; - for (int32_t y = 0; y < height; y ++) { - + for (int32_t y = 0; y < height; y++) { const uint8_t* input = source + y * width; uint8_t* output = dest + y * width; - for (int32_t x = 0; x < width; x ++) { + for (int32_t x = 0; x < width; x++) { blurredPixel = 0.0f; const float* gPtr = weights; // Optimization for non-border pixels if (x > radius && x < (width - radius)) { - const uint8_t *i = input + (x - radius); - for (int r = -radius; r <= radius; r ++) { - currentPixel = (float) (*i); + const uint8_t* i = input + (x - radius); + for (int r = -radius; r <= radius; r++) { + currentPixel = (float)(*i); blurredPixel += currentPixel * gPtr[0]; gPtr++; i++; } } else { - for (int32_t r = -radius; r <= radius; r ++) { + for (int32_t r = -radius; r <= radius; r++) { // Stepping left and right away from the pixel int validW = x + r; if (validW < 0) { @@ -124,40 +123,40 @@ void Blur::horizontal(float* weights, int32_t radius, validW = width - 1; } - currentPixel = (float) input[validW]; + currentPixel = (float)input[validW]; blurredPixel += currentPixel * gPtr[0]; gPtr++; } } *output = (uint8_t)blurredPixel; - output ++; + output++; } } } -void Blur::vertical(float* weights, int32_t radius, - const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) { +void Blur::vertical(float* weights, int32_t radius, const uint8_t* source, uint8_t* dest, + int32_t width, int32_t height) { float blurredPixel = 0.0f; float currentPixel = 0.0f; - for (int32_t y = 0; y < height; y ++) { + for (int32_t y = 0; y < height; y++) { uint8_t* output = dest + y * width; - for (int32_t x = 0; x < width; x ++) { + for (int32_t x = 0; x < width; x++) { blurredPixel = 0.0f; const float* gPtr = weights; const uint8_t* input = source + x; // Optimization for non-border pixels if (y > radius && y < (height - radius)) { - const uint8_t *i = input + ((y - radius) * width); - for (int32_t r = -radius; r <= radius; r ++) { - currentPixel = (float) (*i); + const uint8_t* i = input + ((y - radius) * width); + for (int32_t r = -radius; r <= radius; r++) { + currentPixel = (float)(*i); blurredPixel += currentPixel * gPtr[0]; gPtr++; i += width; } } else { - for (int32_t r = -radius; r <= radius; r ++) { + for (int32_t r = -radius; r <= radius; r++) { int validH = y + r; // Clamp to zero and width if (validH < 0) { @@ -167,17 +166,17 @@ void Blur::vertical(float* weights, int32_t radius, validH = height - 1; } - const uint8_t *i = input + validH * width; - currentPixel = (float) (*i); + const uint8_t* i = input + validH * width; + currentPixel = (float)(*i); blurredPixel += currentPixel * gPtr[0]; gPtr++; } } - *output = (uint8_t) blurredPixel; + *output = (uint8_t)blurredPixel; output++; } } } -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/utils/Blur.h b/libs/hwui/utils/Blur.h index 3f21832bf2b5..bec3837106e8 100644 --- a/libs/hwui/utils/Blur.h +++ b/libs/hwui/utils/Blur.h @@ -17,8 +17,8 @@ #ifndef ANDROID_HWUI_BLUR_H #define ANDROID_HWUI_BLUR_H -#include <stdint.h> #include <cutils/compiler.h> +#include <stdint.h> namespace android { namespace uirenderer { @@ -35,13 +35,13 @@ public: static uint32_t convertRadiusToInt(float radius); static void generateGaussianWeights(float* weights, float radius); - static void horizontal(float* weights, int32_t radius, const uint8_t* source, - uint8_t* dest, int32_t width, int32_t height); - static void vertical(float* weights, int32_t radius, const uint8_t* source, - uint8_t* dest, int32_t width, int32_t height); + static void horizontal(float* weights, int32_t radius, const uint8_t* source, uint8_t* dest, + int32_t width, int32_t height); + static void vertical(float* weights, int32_t radius, const uint8_t* source, uint8_t* dest, + int32_t width, int32_t height); }; -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android -#endif // ANDROID_HWUI_BLUR_H +#endif // ANDROID_HWUI_BLUR_H diff --git a/libs/hwui/utils/Color.cpp b/libs/hwui/utils/Color.cpp index 7d234b06b8ca..c2af867657bf 100644 --- a/libs/hwui/utils/Color.cpp +++ b/libs/hwui/utils/Color.cpp @@ -41,18 +41,17 @@ bool transferFunctionCloseToSRGB(const SkColorSpace* colorSpace) { const float sRGBParamG = 2.4f; // This comparison will catch Display P3 - return - almostEqual(sRGBParamA, transferFunction.fA) - && almostEqual(sRGBParamB, transferFunction.fB) - && almostEqual(sRGBParamC, transferFunction.fC) - && almostEqual(sRGBParamD, transferFunction.fD) - && almostEqual(sRGBParamE, transferFunction.fE) - && almostEqual(sRGBParamF, transferFunction.fF) - && almostEqual(sRGBParamG, transferFunction.fG); + return almostEqual(sRGBParamA, transferFunction.fA) && + almostEqual(sRGBParamB, transferFunction.fB) && + almostEqual(sRGBParamC, transferFunction.fC) && + almostEqual(sRGBParamD, transferFunction.fD) && + almostEqual(sRGBParamE, transferFunction.fE) && + almostEqual(sRGBParamF, transferFunction.fF) && + almostEqual(sRGBParamG, transferFunction.fG); } return false; } -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/utils/Color.h b/libs/hwui/utils/Color.h index 9c096601c826..4857a87ff97b 100644 --- a/libs/hwui/utils/Color.h +++ b/libs/hwui/utils/Color.h @@ -23,114 +23,94 @@ namespace android { namespace uirenderer { - namespace Color { - enum Color { - Red_500 = 0xFFF44336, - Pink_500 = 0xFFE91E63, - Purple_500 = 0xFF9C27B0, - DeepPurple_500 = 0xFF673AB7, - Indigo_500 = 0xFF3F51B5, - Blue_500 = 0xFF2196F3, - LightBlue_300 = 0xFF4FC3F7, - LightBlue_500 = 0xFF03A9F4, - Cyan_500 = 0xFF00BCD4, - Teal_500 = 0xFF009688, - Teal_700 = 0xFF00796B, - Green_500 = 0xFF4CAF50, - Green_700 = 0xFF388E3C, - LightGreen_500 = 0xFF8BC34A, - LightGreen_700 = 0xFF689F38, - Lime_500 = 0xFFCDDC39, - Yellow_500 = 0xFFFFEB3B, - Amber_500 = 0xFFFFC107, - Orange_500 = 0xFFFF9800, - DeepOrange_500 = 0xFFFF5722, - Brown_500 = 0xFF795548, - Grey_200 = 0xFFEEEEEE, - Grey_500 = 0xFF9E9E9E, - Grey_700 = 0xFF616161, - BlueGrey_500 = 0xFF607D8B, - Transparent = 0x00000000, - Black = 0xFF000000, - White = 0xFFFFFFFF, - }; - } +namespace Color { +enum Color { + Red_500 = 0xFFF44336, + Pink_500 = 0xFFE91E63, + Purple_500 = 0xFF9C27B0, + DeepPurple_500 = 0xFF673AB7, + Indigo_500 = 0xFF3F51B5, + Blue_500 = 0xFF2196F3, + LightBlue_300 = 0xFF4FC3F7, + LightBlue_500 = 0xFF03A9F4, + Cyan_500 = 0xFF00BCD4, + Teal_500 = 0xFF009688, + Teal_700 = 0xFF00796B, + Green_500 = 0xFF4CAF50, + Green_700 = 0xFF388E3C, + LightGreen_500 = 0xFF8BC34A, + LightGreen_700 = 0xFF689F38, + Lime_500 = 0xFFCDDC39, + Yellow_500 = 0xFFFFEB3B, + Amber_500 = 0xFFFFC107, + Orange_500 = 0xFFFF9800, + DeepOrange_500 = 0xFFFF5722, + Brown_500 = 0xFF795548, + Grey_200 = 0xFFEEEEEE, + Grey_500 = 0xFF9E9E9E, + Grey_700 = 0xFF616161, + BlueGrey_500 = 0xFF607D8B, + Transparent = 0x00000000, + Black = 0xFF000000, + White = 0xFFFFFFFF, +}; +} - static_assert(Color::White == SK_ColorWHITE, "color format has changed"); - static_assert(Color::Black == SK_ColorBLACK, "color format has changed"); +static_assert(Color::White == SK_ColorWHITE, "color format has changed"); +static_assert(Color::Black == SK_ColorBLACK, "color format has changed"); - // Array of bright (500 intensity) colors for synthetic content - static const Color::Color BrightColors[] = { - Color::Red_500, - Color::Pink_500, - Color::Purple_500, - Color::DeepPurple_500, - Color::Indigo_500, - Color::Blue_500, - Color::LightBlue_500, - Color::Cyan_500, - Color::Teal_500, - Color::Green_500, - Color::LightGreen_500, - Color::Lime_500, - Color::Yellow_500, - Color::Amber_500, - Color::Orange_500, - Color::DeepOrange_500, - Color::Brown_500, - Color::Grey_500, - Color::BlueGrey_500, - }; - static constexpr int BrightColorsCount = sizeof(BrightColors) / sizeof(Color::Color); +// Array of bright (500 intensity) colors for synthetic content +static const Color::Color BrightColors[] = { + Color::Red_500, Color::Pink_500, Color::Purple_500, Color::DeepPurple_500, + Color::Indigo_500, Color::Blue_500, Color::LightBlue_500, Color::Cyan_500, + Color::Teal_500, Color::Green_500, Color::LightGreen_500, Color::Lime_500, + Color::Yellow_500, Color::Amber_500, Color::Orange_500, Color::DeepOrange_500, + Color::Brown_500, Color::Grey_500, Color::BlueGrey_500, +}; +static constexpr int BrightColorsCount = sizeof(BrightColors) / sizeof(Color::Color); - enum class TransferFunctionType : int8_t { - None = 0, - Full, - Limited, - Gamma - }; +enum class TransferFunctionType : int8_t { None = 0, Full, Limited, Gamma }; - // Opto-electronic conversion function for the sRGB color space - // Takes a linear sRGB value and converts it to a gamma-encoded sRGB value - static constexpr float OECF_sRGB(float linear) { - // IEC 61966-2-1:1999 - return linear <= 0.0031308f ? - linear * 12.92f : (powf(linear, 1.0f / 2.4f) * 1.055f) - 0.055f; - } +// Opto-electronic conversion function for the sRGB color space +// Takes a linear sRGB value and converts it to a gamma-encoded sRGB value +static constexpr float OECF_sRGB(float linear) { + // IEC 61966-2-1:1999 + return linear <= 0.0031308f ? linear * 12.92f : (powf(linear, 1.0f / 2.4f) * 1.055f) - 0.055f; +} - // Opto-electronic conversion function for the sRGB color space - // Takes a linear sRGB value and converts it to a gamma-encoded sRGB value - // This function returns the input unmodified if linear blending is not enabled - static constexpr float OECF(float linear) { +// Opto-electronic conversion function for the sRGB color space +// Takes a linear sRGB value and converts it to a gamma-encoded sRGB value +// This function returns the input unmodified if linear blending is not enabled +static constexpr float OECF(float linear) { #ifdef ANDROID_ENABLE_LINEAR_BLENDING - return OECF_sRGB(linear); + return OECF_sRGB(linear); #else - return linear; + return linear; #endif - } +} - // Electro-optical conversion function for the sRGB color space - // Takes a gamma-encoded sRGB value and converts it to a linear sRGB value - static constexpr float EOCF_sRGB(float srgb) { - // IEC 61966-2-1:1999 - return srgb <= 0.04045f ? srgb / 12.92f : powf((srgb + 0.055f) / 1.055f, 2.4f); - } +// Electro-optical conversion function for the sRGB color space +// Takes a gamma-encoded sRGB value and converts it to a linear sRGB value +static constexpr float EOCF_sRGB(float srgb) { + // IEC 61966-2-1:1999 + return srgb <= 0.04045f ? srgb / 12.92f : powf((srgb + 0.055f) / 1.055f, 2.4f); +} - // Electro-optical conversion function for the sRGB color space - // Takes a gamma-encoded sRGB value and converts it to a linear sRGB value - // This function returns the input unmodified if linear blending is not enabled - static constexpr float EOCF(float srgb) { +// Electro-optical conversion function for the sRGB color space +// Takes a gamma-encoded sRGB value and converts it to a linear sRGB value +// This function returns the input unmodified if linear blending is not enabled +static constexpr float EOCF(float srgb) { #ifdef ANDROID_ENABLE_LINEAR_BLENDING - return EOCF_sRGB(srgb); + return EOCF_sRGB(srgb); #else - return srgb; + return srgb; #endif - } +} - // Returns whether the specified color space's transfer function can be - // approximated with the native sRGB transfer function. This method - // returns true for sRGB, gamma 2.2 and Display P3 for instance - bool transferFunctionCloseToSRGB(const SkColorSpace* colorSpace); +// Returns whether the specified color space's transfer function can be +// approximated with the native sRGB transfer function. This method +// returns true for sRGB, gamma 2.2 and Display P3 for instance +bool transferFunctionCloseToSRGB(const SkColorSpace* colorSpace); } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/utils/FatVector.h b/libs/hwui/utils/FatVector.h index df8cb076e763..eafe2f13c16d 100644 --- a/libs/hwui/utils/FatVector.h +++ b/libs/hwui/utils/FatVector.h @@ -30,8 +30,8 @@ #include <stddef.h> #include <stdlib.h> -#include <type_traits> #include <utils/Log.h> +#include <type_traits> #include <vector> @@ -43,28 +43,27 @@ class InlineStdAllocator { public: struct Allocation { PREVENT_COPY_AND_ASSIGN(Allocation); + public: - Allocation() {}; + Allocation(){}; // char array instead of T array, so memory is uninitialized, with no destructors run char array[sizeof(T) * SIZE]; bool inUse = false; }; - typedef T value_type; // needed to implement std::allocator - typedef T* pointer; // needed to implement std::allocator + typedef T value_type; // needed to implement std::allocator + typedef T* pointer; // needed to implement std::allocator - explicit InlineStdAllocator(Allocation& allocation) - : mAllocation(allocation) {} - InlineStdAllocator(const InlineStdAllocator& other) - : mAllocation(other.mAllocation) {} + explicit InlineStdAllocator(Allocation& allocation) : mAllocation(allocation) {} + InlineStdAllocator(const InlineStdAllocator& other) : mAllocation(other.mAllocation) {} ~InlineStdAllocator() {} T* allocate(size_t num, const void* = 0) { if (!mAllocation.inUse && num <= SIZE) { mAllocation.inUse = true; - return (T*) mAllocation.array; + return (T*)mAllocation.array; } else { - return (T*) malloc(num * sizeof(T)); + return (T*)malloc(num * sizeof(T)); } } @@ -88,20 +87,19 @@ public: template <typename T, size_t SIZE> class FatVector : public std::vector<T, InlineStdAllocator<T, SIZE>> { public: - FatVector() : std::vector<T, InlineStdAllocator<T, SIZE>>( - InlineStdAllocator<T, SIZE>(mAllocation)) { + FatVector() + : std::vector<T, InlineStdAllocator<T, SIZE>>( + InlineStdAllocator<T, SIZE>(mAllocation)) { this->reserve(SIZE); } - explicit FatVector(size_t capacity) : FatVector() { - this->resize(capacity); - } + explicit FatVector(size_t capacity) : FatVector() { this->resize(capacity); } private: typename InlineStdAllocator<T, SIZE>::Allocation mAllocation; }; -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android -#endif // ANDROID_FAT_VECTOR_H +#endif // ANDROID_FAT_VECTOR_H diff --git a/libs/hwui/utils/GLUtils.cpp b/libs/hwui/utils/GLUtils.cpp index 332097593c86..bf27300029c6 100644 --- a/libs/hwui/utils/GLUtils.cpp +++ b/libs/hwui/utils/GLUtils.cpp @@ -39,25 +39,25 @@ bool GLUtils::dumpGLErrors() { while ((status = glGetError()) != GL_NO_ERROR) { errorObserved = true; switch (status) { - case GL_INVALID_ENUM: - ALOGE("GL error: GL_INVALID_ENUM"); - break; - case GL_INVALID_VALUE: - ALOGE("GL error: GL_INVALID_VALUE"); - break; - case GL_INVALID_OPERATION: - ALOGE("GL error: GL_INVALID_OPERATION"); - break; - case GL_OUT_OF_MEMORY: - ALOGE("GL error: Out of memory!"); - break; - default: - ALOGE("GL error: 0x%x", status); + case GL_INVALID_ENUM: + ALOGE("GL error: GL_INVALID_ENUM"); + break; + case GL_INVALID_VALUE: + ALOGE("GL error: GL_INVALID_VALUE"); + break; + case GL_INVALID_OPERATION: + ALOGE("GL error: GL_INVALID_OPERATION"); + break; + case GL_OUT_OF_MEMORY: + ALOGE("GL error: Out of memory!"); + break; + default: + ALOGE("GL error: 0x%x", status); } } return errorObserved; #endif } -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/utils/GLUtils.h b/libs/hwui/utils/GLUtils.h index c12747805293..debfb5d06944 100644 --- a/libs/hwui/utils/GLUtils.h +++ b/libs/hwui/utils/GLUtils.h @@ -23,13 +23,14 @@ namespace android { namespace uirenderer { - #if DEBUG_OPENGL -#define GL_CHECKPOINT(LEVEL) \ - do { if (DEBUG_OPENGL >= DEBUG_LEVEL_##LEVEL) {\ - LOG_ALWAYS_FATAL_IF(android::uirenderer::GLUtils::dumpGLErrors(),\ - "GL errors! %s:%d", __FILE__, __LINE__);\ - } } while (0) +#define GL_CHECKPOINT(LEVEL) \ + do { \ + if (DEBUG_OPENGL >= DEBUG_LEVEL_##LEVEL) { \ + LOG_ALWAYS_FATAL_IF(android::uirenderer::GLUtils::dumpGLErrors(), "GL errors! %s:%d", \ + __FILE__, __LINE__); \ + } \ + } while (0) #else #define GL_CHECKPOINT(LEVEL) #endif @@ -42,7 +43,7 @@ public: */ static bool dumpGLErrors(); -}; // class GLUtils +}; // class GLUtils } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/utils/LinearAllocator.cpp b/libs/hwui/utils/LinearAllocator.cpp index d92bc0cd1fca..5a59de8b922a 100644 --- a/libs/hwui/utils/LinearAllocator.cpp +++ b/libs/hwui/utils/LinearAllocator.cpp @@ -30,10 +30,9 @@ #include <stdlib.h> #include <utils/Log.h> - // The ideal size of a page allocation (these need to be multiples of 8) -#define INITIAL_PAGE_SIZE ((size_t)512) // 512b -#define MAX_PAGE_SIZE ((size_t)131072) // 128kb +#define INITIAL_PAGE_SIZE ((size_t)512) // 512b +#define MAX_PAGE_SIZE ((size_t)131072) // 128kb // The maximum amount of wasted space we can have per page // Allocations exceeding this will have their own dedicated page @@ -48,7 +47,7 @@ #define ALIGN_SZ (sizeof(int)) #endif -#define ALIGN(x) (((x) + ALIGN_SZ - 1 ) & ~(ALIGN_SZ - 1)) +#define ALIGN(x) (((x) + ALIGN_SZ - 1) & ~(ALIGN_SZ - 1)) #define ALIGN_PTR(p) ((void*)(ALIGN((size_t)(p)))) #if LOG_NDEBUG @@ -79,7 +78,7 @@ static void _addAllocation(int count) { #define RM_ALLOCATION(size) _addAllocation(-1); #endif -#define min(x,y) (((x) < (y)) ? (x) : (y)) +#define min(x, y) (((x) < (y)) ? (x) : (y)) namespace android { namespace uirenderer { @@ -89,19 +88,13 @@ public: Page* next() { return mNextPage; } void setNext(Page* next) { mNextPage = next; } - Page() - : mNextPage(0) - {} + Page() : mNextPage(0) {} void* operator new(size_t /*size*/, void* buf) { return buf; } - void* start() { - return (void*) (((size_t)this) + sizeof(Page)); - } + void* start() { return (void*)(((size_t)this) + sizeof(Page)); } - void* end(int pageSize) { - return (void*) (((size_t)start()) + pageSize); - } + void* end(int pageSize) { return (void*)(((size_t)start()) + pageSize); } private: Page(const Page& /*other*/) {} @@ -109,15 +102,15 @@ private: }; LinearAllocator::LinearAllocator() - : mPageSize(INITIAL_PAGE_SIZE) - , mMaxAllocSize(INITIAL_PAGE_SIZE * MAX_WASTE_RATIO) - , mNext(0) - , mCurrentPage(0) - , mPages(0) - , mTotalAllocated(0) - , mWastedSpace(0) - , mPageCount(0) - , mDedicatedPageCount(0) {} + : mPageSize(INITIAL_PAGE_SIZE) + , mMaxAllocSize(INITIAL_PAGE_SIZE * MAX_WASTE_RATIO) + , mNext(0) + , mCurrentPage(0) + , mPages(0) + , mTotalAllocated(0) + , mWastedSpace(0) + , mPageCount(0) + , mDedicatedPageCount(0) {} LinearAllocator::~LinearAllocator(void) { while (mDtorList) { @@ -176,8 +169,7 @@ void* LinearAllocator::allocImpl(size_t size) { mDedicatedPageCount++; page->setNext(mPages); mPages = page; - if (!mCurrentPage) - mCurrentPage = mPages; + if (!mCurrentPage) mCurrentPage = mPages; return start(page); } ensureNext(size); @@ -225,8 +217,8 @@ void LinearAllocator::rewindIfLastAlloc(void* ptr, size_t allocSize) { runDestructorFor(ptr); // Don't bother rewinding across pages allocSize = ALIGN(allocSize); - if (ptr >= start(mCurrentPage) && ptr < end(mCurrentPage) - && ptr == ((char*)mNext - allocSize)) { + if (ptr >= start(mCurrentPage) && ptr < end(mCurrentPage) && + ptr == ((char*)mNext - allocSize)) { mWastedSpace += allocSize; mNext = ptr; } @@ -261,9 +253,9 @@ void LinearAllocator::dumpMemoryStats(const char* prefix) { ALOGD("%sTotal allocated: %.2f%s", prefix, prettySize, prettySuffix); prettySuffix = toSize(mWastedSpace, prettySize); ALOGD("%sWasted space: %.2f%s (%.1f%%)", prefix, prettySize, prettySuffix, - (float) mWastedSpace / (float) mTotalAllocated * 100.0f); + (float)mWastedSpace / (float)mTotalAllocated * 100.0f); ALOGD("%sPages %zu (dedicated %zu)", prefix, mPageCount, mDedicatedPageCount); } -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/utils/LinearAllocator.h b/libs/hwui/utils/LinearAllocator.h index f95a6fe5060e..03f685e8aca8 100644 --- a/libs/hwui/utils/LinearAllocator.h +++ b/libs/hwui/utils/LinearAllocator.h @@ -56,10 +56,10 @@ public: * Note that unlike create, for alloc the type is purely for compile-time error * checking and does not affect size. */ - template<class T> + template <class T> void* alloc(size_t size) { static_assert(std::is_trivially_destructible<T>::value, - "Error, type is non-trivial! did you mean to use create()?"); + "Error, type is non-trivial! did you mean to use create()?"); return allocImpl(size); } @@ -67,7 +67,7 @@ public: * Allocates an instance of the template type with the given construction parameters * and adds it to the automatic destruction list. */ - template<class T, typename... Params> + template <class T, typename... Params> T* create(Params&&... params) { T* ret = new (allocImpl(sizeof(T))) T(std::forward<Params>(params)...); if (!std::is_trivially_destructible<T>::value) { @@ -77,17 +77,17 @@ public: return ret; } - template<class T, typename... Params> + template <class T, typename... Params> T* create_trivial(Params&&... params) { static_assert(std::is_trivially_destructible<T>::value, - "Error, called create_trivial on a non-trivial type"); + "Error, called create_trivial on a non-trivial type"); return new (allocImpl(sizeof(T))) T(std::forward<Params>(params)...); } - template<class T> + template <class T> T* create_trivial_array(int count) { static_assert(std::is_trivially_destructible<T>::value, - "Error, called create_trivial_array on a non-trivial type"); + "Error, called create_trivial_array on a non-trivial type"); return reinterpret_cast<T*>(allocImpl(sizeof(T) * count)); } @@ -100,7 +100,7 @@ public: /** * Same as rewindIfLastAlloc(void*, size_t) */ - template<class T> + template <class T> void rewindIfLastAlloc(T* ptr) { rewindIfLastAlloc((void*)ptr, sizeof(T)); } @@ -134,7 +134,7 @@ private: Page* newPage(size_t pageSize); bool fitsInCurrentPage(size_t size); void ensureNext(size_t size); - void* start(Page *p); + void* start(Page* p); void* end(Page* p); size_t mPageSize; @@ -154,13 +154,11 @@ private: template <class T> class LinearStdAllocator { public: - typedef T value_type; // needed to implement std::allocator - typedef T* pointer; // needed to implement std::allocator + typedef T value_type; // needed to implement std::allocator + typedef T* pointer; // needed to implement std::allocator - explicit LinearStdAllocator(LinearAllocator& allocator) - : linearAllocator(allocator) {} - LinearStdAllocator(const LinearStdAllocator& other) - : linearAllocator(other.linearAllocator) {} + explicit LinearStdAllocator(LinearAllocator& allocator) : linearAllocator(allocator) {} + LinearStdAllocator(const LinearStdAllocator& other) : linearAllocator(other.linearAllocator) {} ~LinearStdAllocator() {} // rebind marks that allocators can be rebound to different types @@ -188,9 +186,13 @@ public: // return that all specializations of LinearStdAllocator are interchangeable template <class T1, class T2> -bool operator== (const LinearStdAllocator<T1>&, const LinearStdAllocator<T2>&) { return true; } +bool operator==(const LinearStdAllocator<T1>&, const LinearStdAllocator<T2>&) { + return true; +} template <class T1, class T2> -bool operator!= (const LinearStdAllocator<T1>&, const LinearStdAllocator<T2>&) { return false; } +bool operator!=(const LinearStdAllocator<T1>&, const LinearStdAllocator<T2>&) { + return false; +} template <class T> class LsaVector : public std::vector<T, LinearStdAllocator<T>> { @@ -199,7 +201,7 @@ public: : std::vector<T, LinearStdAllocator<T>>(allocator) {} }; -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android -#endif // ANDROID_LINEARALLOCATOR_H +#endif // ANDROID_LINEARALLOCATOR_H diff --git a/libs/hwui/utils/Macros.h b/libs/hwui/utils/Macros.h index 7212897bf5d3..d758f29d6dcb 100644 --- a/libs/hwui/utils/Macros.h +++ b/libs/hwui/utils/Macros.h @@ -19,21 +19,19 @@ #include <type_traits> #define PREVENT_COPY_AND_ASSIGN(Type) \ - private: \ - Type(const Type&) = delete; \ - void operator=(const Type&) = delete +private: \ + Type(const Type&) = delete; \ + void operator=(const Type&) = delete -#define HASHABLE_TYPE(Type) \ - bool operator==(const Type& other) const; \ - hash_t hash() const; \ - bool operator!=(const Type& other) const { return !(*this == other); } \ - friend inline hash_t hash_type(const Type& entry) { return entry.hash(); } +#define HASHABLE_TYPE(Type) \ + bool operator==(const Type& other) const; \ + hash_t hash() const; \ + bool operator!=(const Type& other) const { return !(*this == other); } \ + friend inline hash_t hash_type(const Type& entry) { return entry.hash(); } #define REQUIRE_COMPATIBLE_LAYOUT(Type) \ - static_assert(std::is_standard_layout<Type>::value, \ - #Type " must have standard layout") + static_assert(std::is_standard_layout<Type>::value, #Type " must have standard layout") -#define WARN_UNUSED_RESULT \ - __attribute__((warn_unused_result)) +#define WARN_UNUSED_RESULT __attribute__((warn_unused_result)) #endif /* MACROS_H */ diff --git a/libs/hwui/utils/MathUtils.h b/libs/hwui/utils/MathUtils.h index 8d20f2142e73..5475898bff28 100644 --- a/libs/hwui/utils/MathUtils.h +++ b/libs/hwui/utils/MathUtils.h @@ -16,8 +16,8 @@ #ifndef MATHUTILS_H #define MATHUTILS_H -#include <algorithm> #include <math.h> +#include <algorithm> namespace android { namespace uirenderer { @@ -34,9 +34,7 @@ public: return (value >= -NON_ZERO_EPSILON) && (value <= NON_ZERO_EPSILON); } - inline static bool isPositive(float value) { - return value >= NON_ZERO_EPSILON; - } + inline static bool isPositive(float value) { return value >= NON_ZERO_EPSILON; } /** * Clamps alpha value, and snaps when very near 0 or 1 @@ -69,28 +67,24 @@ public: * Returns the number of points (beyond two, the start and end) needed to form a polygonal * approximation of an arc, with a given threshold value. */ - inline static int divisionsNeededToApproximateArc(float radius, - float angleInRads, float threshold) { + inline static int divisionsNeededToApproximateArc(float radius, float angleInRads, + float threshold) { const float errConst = (-threshold / radius + 1); const float targetCosVal = 2 * errConst * errConst - 1; // needed divisions are rounded up from approximation - return (int)(ceilf(angleInRads / acos(targetCosVal)/2)) * 2; + return (int)(ceilf(angleInRads / acos(targetCosVal) / 2)) * 2; } - inline static bool areEqual(float valueA, float valueB) { - return isZero(valueA - valueB); - } + inline static bool areEqual(float valueA, float valueB) { return isZero(valueA - valueB); } - template<typename T> + template <typename T> static inline T clamp(T a, T minValue, T maxValue) { return std::min(std::max(a, minValue), maxValue); } - inline static float lerp(float v1, float v2, float t) { - return v1 + ((v2 - v1) * t); - } -}; // class MathUtils + inline static float lerp(float v1, float v2, float t) { return v1 + ((v2 - v1) * t); } +}; // class MathUtils } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/utils/PaintUtils.h b/libs/hwui/utils/PaintUtils.h index 2673be1cb6fa..233adae1df24 100644 --- a/libs/hwui/utils/PaintUtils.h +++ b/libs/hwui/utils/PaintUtils.h @@ -31,7 +31,6 @@ namespace uirenderer { */ class PaintUtils { public: - static inline GLenum getFilter(const SkPaint* paint) { if (!paint || paint->getFilterQuality() != kNone_SkFilterQuality) { return GL_LINEAR; @@ -40,18 +39,16 @@ public: } static bool isOpaquePaint(const SkPaint* paint) { - if (!paint) return true; // default (paintless) behavior is SrcOver, black + if (!paint) return true; // default (paintless) behavior is SrcOver, black - if (paint->getAlpha() != 0xFF - || PaintUtils::isBlendedShader(paint->getShader()) - || PaintUtils::isBlendedColorFilter(paint->getColorFilter())) { + if (paint->getAlpha() != 0xFF || PaintUtils::isBlendedShader(paint->getShader()) || + PaintUtils::isBlendedColorFilter(paint->getColorFilter())) { return false; } // Only let simple srcOver / src blending modes declare opaque, since behavior is clear. SkBlendMode mode = paint->getBlendMode(); - return mode == SkBlendMode::kSrcOver - || mode == SkBlendMode::kSrc; + return mode == SkBlendMode::kSrcOver || mode == SkBlendMode::kSrc; } static bool isBlendedShader(const SkShader* shader) { @@ -89,9 +86,7 @@ public: return false; } - static inline bool hasTextShadow(const SkPaint* paint) { - return getTextShadow(paint, nullptr); - } + static inline bool hasTextShadow(const SkPaint* paint) { return getTextShadow(paint, nullptr); } static inline SkBlendMode getBlendModeDirect(const SkPaint* paint) { return paint ? paint->getBlendMode() : SkBlendMode::kSrcOver; @@ -101,7 +96,7 @@ public: return paint ? paint->getAlpha() : 255; } -}; // class PaintUtils +}; // class PaintUtils } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/utils/Pair.h b/libs/hwui/utils/Pair.h index 0db3aa38d8b1..4bcd57629e0c 100644 --- a/libs/hwui/utils/Pair.h +++ b/libs/hwui/utils/Pair.h @@ -27,34 +27,34 @@ struct Pair { F first; S second; - Pair() { } - Pair(const Pair& o) : first(o.first), second(o.second) { } - Pair(const F& f, const S& s) : first(f), second(s) { } + Pair() {} + Pair(const Pair& o) : first(o.first), second(o.second) {} + Pair(const F& f, const S& s) : first(f), second(s) {} - inline const F& getFirst() const { - return first; - } + inline const F& getFirst() const { return first; } - inline const S& getSecond() const { - return second; - } + inline const S& getSecond() const { return second; } }; -}; // namespace uirenderer +}; // namespace uirenderer template <typename F, typename S> -struct trait_trivial_ctor< uirenderer::Pair<F, S> > -{ enum { value = aggregate_traits<F, S>::has_trivial_ctor }; }; +struct trait_trivial_ctor<uirenderer::Pair<F, S> > { + enum { value = aggregate_traits<F, S>::has_trivial_ctor }; +}; template <typename F, typename S> -struct trait_trivial_dtor< uirenderer::Pair<F, S> > -{ enum { value = aggregate_traits<F, S>::has_trivial_dtor }; }; +struct trait_trivial_dtor<uirenderer::Pair<F, S> > { + enum { value = aggregate_traits<F, S>::has_trivial_dtor }; +}; template <typename F, typename S> -struct trait_trivial_copy< uirenderer::Pair<F, S> > -{ enum { value = aggregate_traits<F, S>::has_trivial_copy }; }; +struct trait_trivial_copy<uirenderer::Pair<F, S> > { + enum { value = aggregate_traits<F, S>::has_trivial_copy }; +}; template <typename F, typename S> -struct trait_trivial_move< uirenderer::Pair<F, S> > -{ enum { value = aggregate_traits<F, S>::has_trivial_move }; }; +struct trait_trivial_move<uirenderer::Pair<F, S> > { + enum { value = aggregate_traits<F, S>::has_trivial_move }; +}; -}; // namespace android +}; // namespace android -#endif // ANDROID_HWUI_PAIR_H +#endif // ANDROID_HWUI_PAIR_H diff --git a/libs/hwui/utils/RingBuffer.h b/libs/hwui/utils/RingBuffer.h index 06bcdcd7d84b..b3e893139cf8 100644 --- a/libs/hwui/utils/RingBuffer.h +++ b/libs/hwui/utils/RingBuffer.h @@ -23,7 +23,7 @@ namespace android { namespace uirenderer { -template<class T, size_t SIZE> +template <class T, size_t SIZE> class RingBuffer { PREVENT_COPY_AND_ASSIGN(RingBuffer); @@ -42,21 +42,13 @@ public: return mBuffer[mHead]; } - T& front() { - return (*this)[0]; - } + T& front() { return (*this)[0]; } - T& back() { - return (*this)[size() - 1]; - } + T& back() { return (*this)[size() - 1]; } - T& operator[](size_t index) { - return mBuffer[(mHead + index + 1) % mCount]; - } + T& operator[](size_t index) { return mBuffer[(mHead + index + 1) % mCount]; } - const T& operator[](size_t index) const { - return mBuffer[(mHead + index + 1) % mCount]; - } + const T& operator[](size_t index) const { return mBuffer[(mHead + index + 1) % mCount]; } void clear() { mCount = 0; @@ -69,7 +61,7 @@ private: size_t mCount = 0; }; -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android #endif /* RINGBUFFER_H_ */ diff --git a/libs/hwui/utils/StringUtils.cpp b/libs/hwui/utils/StringUtils.cpp index 64a59705028a..5304b762f3dc 100644 --- a/libs/hwui/utils/StringUtils.cpp +++ b/libs/hwui/utils/StringUtils.cpp @@ -34,5 +34,5 @@ unordered_string_set StringUtils::split(const char* spacedList) { return set; } -}; // namespace uirenderer -}; // namespace android +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/utils/StringUtils.h b/libs/hwui/utils/StringUtils.h index af5d10f8522b..a10610a3d0da 100644 --- a/libs/hwui/utils/StringUtils.h +++ b/libs/hwui/utils/StringUtils.h @@ -30,9 +30,7 @@ namespace uirenderer { class unordered_string_set : public std::unordered_set<std::string> { public: - bool has(const char* str) { - return find(std::string(str)) != end(); - } + bool has(const char* str) { return find(std::string(str)) != end(); } }; class StringUtils { @@ -55,8 +53,8 @@ struct SizePrinter { } }; -class LogcatStream: public std::ostream { - class LogcatStreamBuf: public std::stringbuf { +class LogcatStream : public std::ostream { + class LogcatStreamBuf : public std::stringbuf { virtual int sync() { ALOGD("%s", str().c_str()); str(""); @@ -65,10 +63,9 @@ class LogcatStream: public std::ostream { }; LogcatStreamBuf buffer; + public: - LogcatStream() - :std::ostream(&buffer) { - } + LogcatStream() : std::ostream(&buffer) {} }; } /* namespace uirenderer */ diff --git a/libs/hwui/utils/TestWindowContext.cpp b/libs/hwui/utils/TestWindowContext.cpp index 492ca7fe62fa..700d3b3cf000 100644 --- a/libs/hwui/utils/TestWindowContext.cpp +++ b/libs/hwui/utils/TestWindowContext.cpp @@ -36,13 +36,13 @@ namespace { */ class ContextFactory : public android::uirenderer::IContextFactory { public: - android::uirenderer::AnimationContext* createAnimationContext - (android::uirenderer::renderthread::TimeLord& clock) override { + android::uirenderer::AnimationContext* createAnimationContext( + android::uirenderer::renderthread::TimeLord& clock) override { return new android::uirenderer::AnimationContext(clock); } }; -} // anonymous namespace +} // anonymous namespace namespace android { namespace uirenderer { @@ -54,45 +54,38 @@ namespace uirenderer { */ class TestWindowContext::TestWindowData { - public: - explicit TestWindowData(SkISize size) : mSize(size) { android::BufferQueue::createBufferQueue(&mProducer, &mConsumer); mCpuConsumer = new android::CpuConsumer(mConsumer, 1); mCpuConsumer->setName(android::String8("TestWindowContext")); mCpuConsumer->setDefaultBufferSize(mSize.width(), mSize.height()); mAndroidSurface = new android::Surface(mProducer); - native_window_set_buffers_dimensions(mAndroidSurface.get(), - mSize.width(), mSize.height()); - native_window_set_buffers_format(mAndroidSurface.get(), - android::PIXEL_FORMAT_RGBA_8888); - native_window_set_usage(mAndroidSurface.get(), - GRALLOC_USAGE_SW_READ_OFTEN | - GRALLOC_USAGE_SW_WRITE_NEVER | - GRALLOC_USAGE_HW_RENDER); + native_window_set_buffers_dimensions(mAndroidSurface.get(), mSize.width(), mSize.height()); + native_window_set_buffers_format(mAndroidSurface.get(), android::PIXEL_FORMAT_RGBA_8888); + native_window_set_usage(mAndroidSurface.get(), GRALLOC_USAGE_SW_READ_OFTEN | + GRALLOC_USAGE_SW_WRITE_NEVER | + GRALLOC_USAGE_HW_RENDER); mRootNode.reset(new android::uirenderer::RenderNode()); mRootNode->incStrong(nullptr); - mRootNode->mutateStagingProperties().setLeftTopRightBottom - (0, 0, mSize.width(), mSize.height()); + mRootNode->mutateStagingProperties().setLeftTopRightBottom(0, 0, mSize.width(), + mSize.height()); mRootNode->mutateStagingProperties().setClipToBounds(false); mRootNode->setPropertyFieldsDirty(android::uirenderer::RenderNode::GENERIC); ContextFactory factory; - mProxy.reset - (new android::uirenderer::renderthread::RenderProxy(false, - mRootNode.get(), - &factory)); + mProxy.reset(new android::uirenderer::renderthread::RenderProxy(false, mRootNode.get(), + &factory)); mProxy->loadSystemProperties(); mProxy->initialize(mAndroidSurface.get()); float lightX = mSize.width() / 2.0f; - android::uirenderer::Vector3 lightVector { lightX, -200.0f, 800.0f }; + android::uirenderer::Vector3 lightVector{lightX, -200.0f, 800.0f}; mProxy->setup(800.0f, 255 * 0.075f, 255 * 0.15f); mProxy->setLightCenter(lightVector); mCanvas.reset(new android::uirenderer::RecordingCanvas(mSize.width(), mSize.height())); } SkCanvas* prepareToDraw() { - //mCanvas->reset(mSize.width(), mSize.height()); + // mCanvas->reset(mSize.width(), mSize.height()); mCanvas->clipRect(0, 0, mSize.width(), mSize.height(), SkClipOp::kReplace_deprecated); return mCanvas->asSkCanvas(); } @@ -104,17 +97,15 @@ public: // the timings we record. } - void fence() { - mProxy->fence(); - } + void fence() { mProxy->fence(); } bool capturePixels(SkBitmap* bmp) { sk_sp<SkColorSpace> colorSpace = SkColorSpace::MakeSRGB(); SkImageInfo destinationConfig = - SkImageInfo::Make(mSize.width(), mSize.height(), - kRGBA_8888_SkColorType, kPremul_SkAlphaType, colorSpace); + SkImageInfo::Make(mSize.width(), mSize.height(), kRGBA_8888_SkColorType, + kPremul_SkAlphaType, colorSpace); bmp->allocPixels(destinationConfig); - android_memset32((uint32_t*) bmp->getPixels(), SK_ColorRED, + android_memset32((uint32_t*)bmp->getPixels(), SK_ColorRED, mSize.width() * mSize.height() * 4); android::CpuConsumer::LockedBuffer nativeBuffer; @@ -135,14 +126,13 @@ public: LOG_ALWAYS_FATAL_IF(nativeBuffer.format != android::PIXEL_FORMAT_RGBA_8888, "Native buffer not RGBA!"); - SkImageInfo nativeConfig = - SkImageInfo::Make(nativeBuffer.width, nativeBuffer.height, - kRGBA_8888_SkColorType, kPremul_SkAlphaType); + SkImageInfo nativeConfig = SkImageInfo::Make(nativeBuffer.width, nativeBuffer.height, + kRGBA_8888_SkColorType, kPremul_SkAlphaType); // Android stride is in pixels, Skia stride is in bytes SkBitmap nativeWrapper; - bool success = - nativeWrapper.installPixels(nativeConfig, nativeBuffer.data, nativeBuffer.stride * 4); + bool success = nativeWrapper.installPixels(nativeConfig, nativeBuffer.data, + nativeBuffer.stride * 4); if (!success) { SkDebugf("Failed to wrap HWUI buffer in a SkBitmap"); return false; @@ -150,8 +140,8 @@ public: LOG_ALWAYS_FATAL_IF(bmp->colorType() != kRGBA_8888_SkColorType, "Destination buffer not RGBA!"); - success = - nativeWrapper.readPixels(destinationConfig, bmp->getPixels(), bmp->rowBytes(), 0, 0); + success = nativeWrapper.readPixels(destinationConfig, bmp->getPixels(), bmp->rowBytes(), 0, + 0); if (!success) { SkDebugf("Failed to extract pixels from HWUI buffer"); return false; @@ -163,7 +153,6 @@ public: } private: - std::unique_ptr<android::uirenderer::RenderNode> mRootNode; std::unique_ptr<android::uirenderer::renderthread::RenderProxy> mProxy; std::unique_ptr<android::uirenderer::RecordingCanvas> mCanvas; @@ -174,15 +163,13 @@ private: SkISize mSize; }; - -TestWindowContext::TestWindowContext() : - mData (nullptr) { } +TestWindowContext::TestWindowContext() : mData(nullptr) {} TestWindowContext::~TestWindowContext() { delete mData; } -void TestWindowContext::initialize(int width, int height) { +void TestWindowContext::initialize(int width, int height) { mData = new TestWindowData(SkISize::Make(width, height)); } @@ -206,6 +193,5 @@ bool TestWindowContext::capturePixels(SkBitmap* bmp) { return mData ? mData->capturePixels(bmp) : false; } -} // namespace uirenderer -} // namespace android - +} // namespace uirenderer +} // namespace android diff --git a/libs/hwui/utils/TestWindowContext.h b/libs/hwui/utils/TestWindowContext.h index 48ec95216b14..17ad1e3fef55 100644 --- a/libs/hwui/utils/TestWindowContext.h +++ b/libs/hwui/utils/TestWindowContext.h @@ -31,9 +31,7 @@ namespace uirenderer { */ class ANDROID_API TestWindowContext { - public: - TestWindowContext(); ~TestWindowContext(); @@ -58,11 +56,9 @@ private: class TestWindowData; TestWindowData* mData; - }; } // namespace uirenderer } // namespace android #endif // TESTWINDOWCONTEXT_H_ - diff --git a/libs/hwui/utils/TimeUtils.h b/libs/hwui/utils/TimeUtils.h index ce181b766841..f66edeaa2a4e 100644 --- a/libs/hwui/utils/TimeUtils.h +++ b/libs/hwui/utils/TimeUtils.h @@ -21,15 +21,15 @@ namespace android { namespace uirenderer { -constexpr nsecs_t operator"" _s (unsigned long long s) { +constexpr nsecs_t operator"" _s(unsigned long long s) { return seconds_to_nanoseconds(s); } -constexpr nsecs_t operator"" _ms (unsigned long long ms) { +constexpr nsecs_t operator"" _ms(unsigned long long ms) { return milliseconds_to_nanoseconds(ms); } -constexpr nsecs_t operator"" _us (unsigned long long us) { +constexpr nsecs_t operator"" _us(unsigned long long us) { return microseconds_to_nanoseconds(us); } diff --git a/libs/hwui/utils/Timing.h b/libs/hwui/utils/Timing.h index 4b1fabe42c35..978c7bcf7a57 100644 --- a/libs/hwui/utils/Timing.h +++ b/libs/hwui/utils/Timing.h @@ -22,18 +22,16 @@ #define TIME_METHOD() MethodTimer __method_timer(__func__) class MethodTimer { public: - explicit MethodTimer(const char* name) - : mMethodName(name) { - gettimeofday(&mStart, nullptr); - } + explicit MethodTimer(const char* name) : mMethodName(name) { gettimeofday(&mStart, nullptr); } ~MethodTimer() { struct timeval stop; gettimeofday(&stop, nullptr); - long long elapsed = (stop.tv_sec * 1000000) - (mStart.tv_sec * 1000000) - + (stop.tv_usec - mStart.tv_usec); + long long elapsed = (stop.tv_sec * 1000000) - (mStart.tv_sec * 1000000) + + (stop.tv_usec - mStart.tv_usec); ALOGD("%s took %.2fms", mMethodName, elapsed / 1000.0); } + private: const char* mMethodName; struct timeval mStart; diff --git a/libs/hwui/utils/TraceUtils.h b/libs/hwui/utils/TraceUtils.h index ddc272c64851..1869d00396c0 100644 --- a/libs/hwui/utils/TraceUtils.h +++ b/libs/hwui/utils/TraceUtils.h @@ -18,11 +18,11 @@ #include <utils/Trace.h> -#define ATRACE_FORMAT(fmt, ...) \ - TraceUtils::TraceEnder __traceEnder = (TraceUtils::atraceFormatBegin(fmt, ##__VA_ARGS__), TraceUtils::TraceEnder()) +#define ATRACE_FORMAT(fmt, ...) \ + TraceUtils::TraceEnder __traceEnder = \ + (TraceUtils::atraceFormatBegin(fmt, ##__VA_ARGS__), TraceUtils::TraceEnder()) -#define ATRACE_FORMAT_BEGIN(fmt, ...) \ - TraceUtils::atraceFormatBegin(fmt, ##__VA_ARGS__) +#define ATRACE_FORMAT_BEGIN(fmt, ...) TraceUtils::atraceFormatBegin(fmt, ##__VA_ARGS__) namespace android { namespace uirenderer { @@ -48,7 +48,7 @@ public: ATRACE_BEGIN(buf); } -}; // class TraceUtils +}; // class TraceUtils } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/utils/VectorDrawableUtils.cpp b/libs/hwui/utils/VectorDrawableUtils.cpp index 6f0c96db4b1e..6b8f3154dd36 100644 --- a/libs/hwui/utils/VectorDrawableUtils.cpp +++ b/libs/hwui/utils/VectorDrawableUtils.cpp @@ -32,8 +32,8 @@ public: float ctrlPointY = 0; float currentSegmentStartX = 0; float currentSegmentStartY = 0; - void addCommand(SkPath* outPath, char previousCmd, - char cmd, const std::vector<float>* points, size_t start, size_t end); + void addCommand(SkPath* outPath, char previousCmd, char cmd, const std::vector<float>* points, + size_t start, size_t end); }; bool VectorDrawableUtils::canMorph(const PathData& morphFrom, const PathData& morphTo) { @@ -42,8 +42,8 @@ bool VectorDrawableUtils::canMorph(const PathData& morphFrom, const PathData& mo } for (unsigned int i = 0; i < morphFrom.verbs.size(); i++) { - if (morphFrom.verbs[i] != morphTo.verbs[i] - || morphFrom.verbSizes[i] != morphTo.verbSizes[i]) { + if (morphFrom.verbs[i] != morphTo.verbs[i] || + morphFrom.verbSizes[i] != morphTo.verbSizes[i]) { return false; } } @@ -51,7 +51,7 @@ bool VectorDrawableUtils::canMorph(const PathData& morphFrom, const PathData& mo } bool VectorDrawableUtils::interpolatePathData(PathData* outData, const PathData& morphFrom, - const PathData& morphTo, float fraction) { + const PathData& morphTo, float fraction) { if (!canMorph(morphFrom, morphTo)) { return false; } @@ -59,9 +59,9 @@ bool VectorDrawableUtils::interpolatePathData(PathData* outData, const PathData& return true; } - /** - * Convert an array of PathVerb to Path. - */ +/** +* Convert an array of PathVerb to Path. +*/ void VectorDrawableUtils::verbsToPath(SkPath* outPath, const PathData& data) { PathResolver resolver; char previousCommand = 'm'; @@ -70,7 +70,7 @@ void VectorDrawableUtils::verbsToPath(SkPath* outPath, const PathData& data) { for (unsigned int i = 0; i < data.verbs.size(); i++) { size_t verbSize = data.verbSizes[i]; resolver.addCommand(outPath, previousCommand, data.verbs[i], &data.points, start, - start + verbSize); + start + verbSize); previousCommand = data.verbs[i]; start += verbSize; } @@ -85,8 +85,8 @@ void VectorDrawableUtils::verbsToPath(SkPath* outPath, const PathData& data) { * @param nodeTo The end value as a PathVerb * @param fraction The fraction to interpolate. */ -void VectorDrawableUtils::interpolatePaths(PathData* outData, - const PathData& from, const PathData& to, float fraction) { +void VectorDrawableUtils::interpolatePaths(PathData* outData, const PathData& from, + const PathData& to, float fraction) { outData->points.resize(from.points.size()); outData->verbSizes = from.verbSizes; outData->verbs = from.verbs; @@ -96,396 +96,234 @@ void VectorDrawableUtils::interpolatePaths(PathData* outData, } } -/** - * Converts an arc to cubic Bezier segments and records them in p. - * - * @param p The target for the cubic Bezier segments - * @param cx The x coordinate center of the ellipse - * @param cy The y coordinate center of the ellipse - * @param a The radius of the ellipse in the horizontal direction - * @param b The radius of the ellipse in the vertical direction - * @param e1x E(eta1) x coordinate of the starting point of the arc - * @param e1y E(eta2) y coordinate of the starting point of the arc - * @param theta The angle that the ellipse bounding rectangle makes with horizontal plane - * @param start The start angle of the arc on the ellipse - * @param sweep The angle (positive or negative) of the sweep of the arc on the ellipse - */ -static void arcToBezier(SkPath* p, - double cx, - double cy, - double a, - double b, - double e1x, - double e1y, - double theta, - double start, - double sweep) { - // Taken from equations at: http://spaceroots.org/documents/ellipse/node8.html - // and http://www.spaceroots.org/documents/ellipse/node22.html - - // Maximum of 45 degrees per cubic Bezier segment - int numSegments = ceil(fabs(sweep * 4 / M_PI)); - - double eta1 = start; - double cosTheta = cos(theta); - double sinTheta = sin(theta); - double cosEta1 = cos(eta1); - double sinEta1 = sin(eta1); - double ep1x = (-a * cosTheta * sinEta1) - (b * sinTheta * cosEta1); - double ep1y = (-a * sinTheta * sinEta1) + (b * cosTheta * cosEta1); - - double anglePerSegment = sweep / numSegments; - for (int i = 0; i < numSegments; i++) { - double eta2 = eta1 + anglePerSegment; - double sinEta2 = sin(eta2); - double cosEta2 = cos(eta2); - double e2x = cx + (a * cosTheta * cosEta2) - (b * sinTheta * sinEta2); - double e2y = cy + (a * sinTheta * cosEta2) + (b * cosTheta * sinEta2); - double ep2x = -a * cosTheta * sinEta2 - b * sinTheta * cosEta2; - double ep2y = -a * sinTheta * sinEta2 + b * cosTheta * cosEta2; - double tanDiff2 = tan((eta2 - eta1) / 2); - double alpha = - sin(eta2 - eta1) * (sqrt(4 + (3 * tanDiff2 * tanDiff2)) - 1) / 3; - double q1x = e1x + alpha * ep1x; - double q1y = e1y + alpha * ep1y; - double q2x = e2x - alpha * ep2x; - double q2y = e2y - alpha * ep2y; - - p->cubicTo((float) q1x, - (float) q1y, - (float) q2x, - (float) q2y, - (float) e2x, - (float) e2y); - eta1 = eta2; - e1x = e2x; - e1y = e2y; - ep1x = ep2x; - ep1y = ep2y; - } -} - -inline double toRadians(float theta) { return theta * M_PI / 180;} - -static void drawArc(SkPath* p, - float x0, - float y0, - float x1, - float y1, - float a, - float b, - float theta, - bool isMoreThanHalf, - bool isPositiveArc) { - - /* Convert rotation angle from degrees to radians */ - double thetaD = toRadians(theta); - /* Pre-compute rotation matrix entries */ - double cosTheta = cos(thetaD); - double sinTheta = sin(thetaD); - /* Transform (x0, y0) and (x1, y1) into unit space */ - /* using (inverse) rotation, followed by (inverse) scale */ - double x0p = (x0 * cosTheta + y0 * sinTheta) / a; - double y0p = (-x0 * sinTheta + y0 * cosTheta) / b; - double x1p = (x1 * cosTheta + y1 * sinTheta) / a; - double y1p = (-x1 * sinTheta + y1 * cosTheta) / b; - - /* Compute differences and averages */ - double dx = x0p - x1p; - double dy = y0p - y1p; - double xm = (x0p + x1p) / 2; - double ym = (y0p + y1p) / 2; - /* Solve for intersecting unit circles */ - double dsq = dx * dx + dy * dy; - if (dsq == 0.0) { - VECTOR_DRAWABLE_LOGD("Points are coincident"); - return; /* Points are coincident */ - } - double disc = 1.0 / dsq - 1.0 / 4.0; - if (disc < 0.0) { - VECTOR_DRAWABLE_LOGD("Points are too far apart %f", dsq); - float adjust = (float) (sqrt(dsq) / 1.99999); - drawArc(p, x0, y0, x1, y1, a * adjust, - b * adjust, theta, isMoreThanHalf, isPositiveArc); - return; /* Points are too far apart */ - } - double s = sqrt(disc); - double sdx = s * dx; - double sdy = s * dy; - double cx; - double cy; - if (isMoreThanHalf == isPositiveArc) { - cx = xm - sdy; - cy = ym + sdx; - } else { - cx = xm + sdy; - cy = ym - sdx; - } - - double eta0 = atan2((y0p - cy), (x0p - cx)); - - double eta1 = atan2((y1p - cy), (x1p - cx)); - - double sweep = (eta1 - eta0); - if (isPositiveArc != (sweep >= 0)) { - if (sweep > 0) { - sweep -= 2 * M_PI; - } else { - sweep += 2 * M_PI; - } - } - - cx *= a; - cy *= b; - double tcx = cx; - cx = cx * cosTheta - cy * sinTheta; - cy = tcx * sinTheta + cy * cosTheta; - - arcToBezier(p, cx, cy, a, b, x0, y0, thetaD, eta0, sweep); -} - - - // Use the given verb, and points in the range [start, end) to insert a command into the SkPath. -void PathResolver::addCommand(SkPath* outPath, char previousCmd, - char cmd, const std::vector<float>* points, size_t start, size_t end) { - +void PathResolver::addCommand(SkPath* outPath, char previousCmd, char cmd, + const std::vector<float>* points, size_t start, size_t end) { int incr = 2; float reflectiveCtrlPointX; float reflectiveCtrlPointY; switch (cmd) { - case 'z': - case 'Z': - outPath->close(); - // Path is closed here, but we need to move the pen to the - // closed position. So we cache the segment's starting position, - // and restore it here. - currentX = currentSegmentStartX; - currentY = currentSegmentStartY; - ctrlPointX = currentSegmentStartX; - ctrlPointY = currentSegmentStartY; - outPath->moveTo(currentX, currentY); - break; - case 'm': - case 'M': - case 'l': - case 'L': - case 't': - case 'T': - incr = 2; - break; - case 'h': - case 'H': - case 'v': - case 'V': - incr = 1; - break; - case 'c': - case 'C': - incr = 6; - break; - case 's': - case 'S': - case 'q': - case 'Q': - incr = 4; - break; - case 'a': - case 'A': - incr = 7; - break; - } - - for (unsigned int k = start; k < end; k += incr) { - switch (cmd) { - case 'm': // moveto - Start a new sub-path (relative) - currentX += points->at(k + 0); - currentY += points->at(k + 1); - if (k > start) { - // According to the spec, if a moveto is followed by multiple - // pairs of coordinates, the subsequent pairs are treated as - // implicit lineto commands. - outPath->rLineTo(points->at(k + 0), points->at(k + 1)); - } else { - outPath->rMoveTo(points->at(k + 0), points->at(k + 1)); - currentSegmentStartX = currentX; - currentSegmentStartY = currentY; - } + case 'z': + case 'Z': + outPath->close(); + // Path is closed here, but we need to move the pen to the + // closed position. So we cache the segment's starting position, + // and restore it here. + currentX = currentSegmentStartX; + currentY = currentSegmentStartY; + ctrlPointX = currentSegmentStartX; + ctrlPointY = currentSegmentStartY; + outPath->moveTo(currentX, currentY); break; - case 'M': // moveto - Start a new sub-path - currentX = points->at(k + 0); - currentY = points->at(k + 1); - if (k > start) { - // According to the spec, if a moveto is followed by multiple - // pairs of coordinates, the subsequent pairs are treated as - // implicit lineto commands. - outPath->lineTo(points->at(k + 0), points->at(k + 1)); - } else { - outPath->moveTo(points->at(k + 0), points->at(k + 1)); - currentSegmentStartX = currentX; - currentSegmentStartY = currentY; - } - break; - case 'l': // lineto - Draw a line from the current point (relative) - outPath->rLineTo(points->at(k + 0), points->at(k + 1)); - currentX += points->at(k + 0); - currentY += points->at(k + 1); + case 'm': + case 'M': + case 'l': + case 'L': + case 't': + case 'T': + incr = 2; break; - case 'L': // lineto - Draw a line from the current point - outPath->lineTo(points->at(k + 0), points->at(k + 1)); - currentX = points->at(k + 0); - currentY = points->at(k + 1); + case 'h': + case 'H': + case 'v': + case 'V': + incr = 1; break; - case 'h': // horizontal lineto - Draws a horizontal line (relative) - outPath->rLineTo(points->at(k + 0), 0); - currentX += points->at(k + 0); + case 'c': + case 'C': + incr = 6; break; - case 'H': // horizontal lineto - Draws a horizontal line - outPath->lineTo(points->at(k + 0), currentY); - currentX = points->at(k + 0); + case 's': + case 'S': + case 'q': + case 'Q': + incr = 4; break; - case 'v': // vertical lineto - Draws a vertical line from the current point (r) - outPath->rLineTo(0, points->at(k + 0)); - currentY += points->at(k + 0); + case 'a': + case 'A': + incr = 7; break; - case 'V': // vertical lineto - Draws a vertical line from the current point - outPath->lineTo(currentX, points->at(k + 0)); - currentY = points->at(k + 0); - break; - case 'c': // curveto - Draws a cubic Bézier curve (relative) - outPath->rCubicTo(points->at(k + 0), points->at(k + 1), points->at(k + 2), points->at(k + 3), - points->at(k + 4), points->at(k + 5)); - - ctrlPointX = currentX + points->at(k + 2); - ctrlPointY = currentY + points->at(k + 3); - currentX += points->at(k + 4); - currentY += points->at(k + 5); + } - break; - case 'C': // curveto - Draws a cubic Bézier curve - outPath->cubicTo(points->at(k + 0), points->at(k + 1), points->at(k + 2), points->at(k + 3), - points->at(k + 4), points->at(k + 5)); - currentX = points->at(k + 4); - currentY = points->at(k + 5); - ctrlPointX = points->at(k + 2); - ctrlPointY = points->at(k + 3); - break; - case 's': // smooth curveto - Draws a cubic Bézier curve (reflective cp) - reflectiveCtrlPointX = 0; - reflectiveCtrlPointY = 0; - if (previousCmd == 'c' || previousCmd == 's' - || previousCmd == 'C' || previousCmd == 'S') { - reflectiveCtrlPointX = currentX - ctrlPointX; - reflectiveCtrlPointY = currentY - ctrlPointY; - } - outPath->rCubicTo(reflectiveCtrlPointX, reflectiveCtrlPointY, - points->at(k + 0), points->at(k + 1), - points->at(k + 2), points->at(k + 3)); - ctrlPointX = currentX + points->at(k + 0); - ctrlPointY = currentY + points->at(k + 1); - currentX += points->at(k + 2); - currentY += points->at(k + 3); - break; - case 'S': // shorthand/smooth curveto Draws a cubic Bézier curve(reflective cp) - reflectiveCtrlPointX = currentX; - reflectiveCtrlPointY = currentY; - if (previousCmd == 'c' || previousCmd == 's' - || previousCmd == 'C' || previousCmd == 'S') { - reflectiveCtrlPointX = 2 * currentX - ctrlPointX; - reflectiveCtrlPointY = 2 * currentY - ctrlPointY; - } - outPath->cubicTo(reflectiveCtrlPointX, reflectiveCtrlPointY, - points->at(k + 0), points->at(k + 1), points->at(k + 2), points->at(k + 3)); - ctrlPointX = points->at(k + 0); - ctrlPointY = points->at(k + 1); - currentX = points->at(k + 2); - currentY = points->at(k + 3); - break; - case 'q': // Draws a quadratic Bézier (relative) - outPath->rQuadTo(points->at(k + 0), points->at(k + 1), points->at(k + 2), points->at(k + 3)); - ctrlPointX = currentX + points->at(k + 0); - ctrlPointY = currentY + points->at(k + 1); - currentX += points->at(k + 2); - currentY += points->at(k + 3); - break; - case 'Q': // Draws a quadratic Bézier - outPath->quadTo(points->at(k + 0), points->at(k + 1), points->at(k + 2), points->at(k + 3)); - ctrlPointX = points->at(k + 0); - ctrlPointY = points->at(k + 1); - currentX = points->at(k + 2); - currentY = points->at(k + 3); - break; - case 't': // Draws a quadratic Bézier curve(reflective control point)(relative) - reflectiveCtrlPointX = 0; - reflectiveCtrlPointY = 0; - if (previousCmd == 'q' || previousCmd == 't' - || previousCmd == 'Q' || previousCmd == 'T') { - reflectiveCtrlPointX = currentX - ctrlPointX; - reflectiveCtrlPointY = currentY - ctrlPointY; - } - outPath->rQuadTo(reflectiveCtrlPointX, reflectiveCtrlPointY, - points->at(k + 0), points->at(k + 1)); - ctrlPointX = currentX + reflectiveCtrlPointX; - ctrlPointY = currentY + reflectiveCtrlPointY; - currentX += points->at(k + 0); - currentY += points->at(k + 1); - break; - case 'T': // Draws a quadratic Bézier curve (reflective control point) - reflectiveCtrlPointX = currentX; - reflectiveCtrlPointY = currentY; - if (previousCmd == 'q' || previousCmd == 't' - || previousCmd == 'Q' || previousCmd == 'T') { - reflectiveCtrlPointX = 2 * currentX - ctrlPointX; - reflectiveCtrlPointY = 2 * currentY - ctrlPointY; - } - outPath->quadTo(reflectiveCtrlPointX, reflectiveCtrlPointY, - points->at(k + 0), points->at(k + 1)); - ctrlPointX = reflectiveCtrlPointX; - ctrlPointY = reflectiveCtrlPointY; - currentX = points->at(k + 0); - currentY = points->at(k + 1); - break; - case 'a': // Draws an elliptical arc - // (rx ry x-axis-rotation large-arc-flag sweep-flag x y) - drawArc(outPath, - currentX, - currentY, - points->at(k + 5) + currentX, - points->at(k + 6) + currentY, - points->at(k + 0), - points->at(k + 1), - points->at(k + 2), - points->at(k + 3) != 0, - points->at(k + 4) != 0); - currentX += points->at(k + 5); - currentY += points->at(k + 6); - ctrlPointX = currentX; - ctrlPointY = currentY; - break; - case 'A': // Draws an elliptical arc - drawArc(outPath, - currentX, - currentY, - points->at(k + 5), - points->at(k + 6), - points->at(k + 0), - points->at(k + 1), - points->at(k + 2), - points->at(k + 3) != 0, - points->at(k + 4) != 0); - currentX = points->at(k + 5); - currentY = points->at(k + 6); - ctrlPointX = currentX; - ctrlPointY = currentY; - break; - default: - LOG_ALWAYS_FATAL("Unsupported command: %c", cmd); - break; + for (unsigned int k = start; k < end; k += incr) { + switch (cmd) { + case 'm': // moveto - Start a new sub-path (relative) + currentX += points->at(k + 0); + currentY += points->at(k + 1); + if (k > start) { + // According to the spec, if a moveto is followed by multiple + // pairs of coordinates, the subsequent pairs are treated as + // implicit lineto commands. + outPath->rLineTo(points->at(k + 0), points->at(k + 1)); + } else { + outPath->rMoveTo(points->at(k + 0), points->at(k + 1)); + currentSegmentStartX = currentX; + currentSegmentStartY = currentY; + } + break; + case 'M': // moveto - Start a new sub-path + currentX = points->at(k + 0); + currentY = points->at(k + 1); + if (k > start) { + // According to the spec, if a moveto is followed by multiple + // pairs of coordinates, the subsequent pairs are treated as + // implicit lineto commands. + outPath->lineTo(points->at(k + 0), points->at(k + 1)); + } else { + outPath->moveTo(points->at(k + 0), points->at(k + 1)); + currentSegmentStartX = currentX; + currentSegmentStartY = currentY; + } + break; + case 'l': // lineto - Draw a line from the current point (relative) + outPath->rLineTo(points->at(k + 0), points->at(k + 1)); + currentX += points->at(k + 0); + currentY += points->at(k + 1); + break; + case 'L': // lineto - Draw a line from the current point + outPath->lineTo(points->at(k + 0), points->at(k + 1)); + currentX = points->at(k + 0); + currentY = points->at(k + 1); + break; + case 'h': // horizontal lineto - Draws a horizontal line (relative) + outPath->rLineTo(points->at(k + 0), 0); + currentX += points->at(k + 0); + break; + case 'H': // horizontal lineto - Draws a horizontal line + outPath->lineTo(points->at(k + 0), currentY); + currentX = points->at(k + 0); + break; + case 'v': // vertical lineto - Draws a vertical line from the current point (r) + outPath->rLineTo(0, points->at(k + 0)); + currentY += points->at(k + 0); + break; + case 'V': // vertical lineto - Draws a vertical line from the current point + outPath->lineTo(currentX, points->at(k + 0)); + currentY = points->at(k + 0); + break; + case 'c': // curveto - Draws a cubic Bézier curve (relative) + outPath->rCubicTo(points->at(k + 0), points->at(k + 1), points->at(k + 2), + points->at(k + 3), points->at(k + 4), points->at(k + 5)); + + ctrlPointX = currentX + points->at(k + 2); + ctrlPointY = currentY + points->at(k + 3); + currentX += points->at(k + 4); + currentY += points->at(k + 5); + + break; + case 'C': // curveto - Draws a cubic Bézier curve + outPath->cubicTo(points->at(k + 0), points->at(k + 1), points->at(k + 2), + points->at(k + 3), points->at(k + 4), points->at(k + 5)); + currentX = points->at(k + 4); + currentY = points->at(k + 5); + ctrlPointX = points->at(k + 2); + ctrlPointY = points->at(k + 3); + break; + case 's': // smooth curveto - Draws a cubic Bézier curve (reflective cp) + reflectiveCtrlPointX = 0; + reflectiveCtrlPointY = 0; + if (previousCmd == 'c' || previousCmd == 's' || previousCmd == 'C' || + previousCmd == 'S') { + reflectiveCtrlPointX = currentX - ctrlPointX; + reflectiveCtrlPointY = currentY - ctrlPointY; + } + outPath->rCubicTo(reflectiveCtrlPointX, reflectiveCtrlPointY, points->at(k + 0), + points->at(k + 1), points->at(k + 2), points->at(k + 3)); + ctrlPointX = currentX + points->at(k + 0); + ctrlPointY = currentY + points->at(k + 1); + currentX += points->at(k + 2); + currentY += points->at(k + 3); + break; + case 'S': // shorthand/smooth curveto Draws a cubic Bézier curve(reflective cp) + reflectiveCtrlPointX = currentX; + reflectiveCtrlPointY = currentY; + if (previousCmd == 'c' || previousCmd == 's' || previousCmd == 'C' || + previousCmd == 'S') { + reflectiveCtrlPointX = 2 * currentX - ctrlPointX; + reflectiveCtrlPointY = 2 * currentY - ctrlPointY; + } + outPath->cubicTo(reflectiveCtrlPointX, reflectiveCtrlPointY, points->at(k + 0), + points->at(k + 1), points->at(k + 2), points->at(k + 3)); + ctrlPointX = points->at(k + 0); + ctrlPointY = points->at(k + 1); + currentX = points->at(k + 2); + currentY = points->at(k + 3); + break; + case 'q': // Draws a quadratic Bézier (relative) + outPath->rQuadTo(points->at(k + 0), points->at(k + 1), points->at(k + 2), + points->at(k + 3)); + ctrlPointX = currentX + points->at(k + 0); + ctrlPointY = currentY + points->at(k + 1); + currentX += points->at(k + 2); + currentY += points->at(k + 3); + break; + case 'Q': // Draws a quadratic Bézier + outPath->quadTo(points->at(k + 0), points->at(k + 1), points->at(k + 2), + points->at(k + 3)); + ctrlPointX = points->at(k + 0); + ctrlPointY = points->at(k + 1); + currentX = points->at(k + 2); + currentY = points->at(k + 3); + break; + case 't': // Draws a quadratic Bézier curve(reflective control point)(relative) + reflectiveCtrlPointX = 0; + reflectiveCtrlPointY = 0; + if (previousCmd == 'q' || previousCmd == 't' || previousCmd == 'Q' || + previousCmd == 'T') { + reflectiveCtrlPointX = currentX - ctrlPointX; + reflectiveCtrlPointY = currentY - ctrlPointY; + } + outPath->rQuadTo(reflectiveCtrlPointX, reflectiveCtrlPointY, points->at(k + 0), + points->at(k + 1)); + ctrlPointX = currentX + reflectiveCtrlPointX; + ctrlPointY = currentY + reflectiveCtrlPointY; + currentX += points->at(k + 0); + currentY += points->at(k + 1); + break; + case 'T': // Draws a quadratic Bézier curve (reflective control point) + reflectiveCtrlPointX = currentX; + reflectiveCtrlPointY = currentY; + if (previousCmd == 'q' || previousCmd == 't' || previousCmd == 'Q' || + previousCmd == 'T') { + reflectiveCtrlPointX = 2 * currentX - ctrlPointX; + reflectiveCtrlPointY = 2 * currentY - ctrlPointY; + } + outPath->quadTo(reflectiveCtrlPointX, reflectiveCtrlPointY, points->at(k + 0), + points->at(k + 1)); + ctrlPointX = reflectiveCtrlPointX; + ctrlPointY = reflectiveCtrlPointY; + currentX = points->at(k + 0); + currentY = points->at(k + 1); + break; + case 'a': // Draws an elliptical arc + // (rx ry x-axis-rotation large-arc-flag sweep-flag x y) + outPath->arcTo(points->at(k + 0), points->at(k + 1), points->at(k + 2), + (SkPath::ArcSize) (points->at(k + 3) != 0), + (SkPath::Direction) (points->at(k + 4) == 0), + points->at(k + 5) + currentX, points->at(k + 6) + currentY); + currentX += points->at(k + 5); + currentY += points->at(k + 6); + ctrlPointX = currentX; + ctrlPointY = currentY; + break; + case 'A': // Draws an elliptical arc + outPath->arcTo(points->at(k + 0), points->at(k + 1), points->at(k + 2), + (SkPath::ArcSize) (points->at(k + 3) != 0), + (SkPath::Direction) (points->at(k + 4) == 0), + points->at(k + 5), points->at(k + 6)); + currentX = points->at(k + 5); + currentY = points->at(k + 6); + ctrlPointX = currentX; + ctrlPointY = currentY; + break; + default: + LOG_ALWAYS_FATAL("Unsupported command: %c", cmd); + break; } previousCmd = cmd; } } -} // namespace uirenderer -} // namespace android +} // namespace uirenderer +} // namespace android diff --git a/libs/hwui/utils/VectorDrawableUtils.h b/libs/hwui/utils/VectorDrawableUtils.h index b5ef5102d219..4be48fb942fc 100644 --- a/libs/hwui/utils/VectorDrawableUtils.h +++ b/libs/hwui/utils/VectorDrawableUtils.h @@ -20,8 +20,8 @@ #include "VectorDrawable.h" #include <cutils/compiler.h> -#include "SkPath.h" #include <vector> +#include "SkPath.h" namespace android { namespace uirenderer { @@ -30,11 +30,11 @@ class VectorDrawableUtils { public: ANDROID_API static bool canMorph(const PathData& morphFrom, const PathData& morphTo); ANDROID_API static bool interpolatePathData(PathData* outData, const PathData& morphFrom, - const PathData& morphTo, float fraction); + const PathData& morphTo, float fraction); ANDROID_API static void verbsToPath(SkPath* outPath, const PathData& data); static void interpolatePaths(PathData* outPathData, const PathData& from, const PathData& to, - float fraction); + float fraction); }; -} // namespace uirenderer -} // namespace android +} // namespace uirenderer +} // namespace android #endif /* ANDROID_HWUI_VECTORDRAWABLE_UTILS_H*/ diff --git a/libs/incident/Android.mk b/libs/incident/Android.mk index 439e86deba5d..b63400f14609 100644 --- a/libs/incident/Android.mk +++ b/libs/incident/Android.mk @@ -31,11 +31,12 @@ LOCAL_C_INCLUDES := \ LOCAL_SRC_FILES := \ ../../core/java/android/os/IIncidentManager.aidl \ - ../../core/java/android/os/IIncidentReportCompletedListener.aidl \ ../../core/java/android/os/IIncidentReportStatusListener.aidl \ + proto/android/os/header.proto \ src/IncidentReportArgs.cpp LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include +LOCAL_PROTO_OPTIMIZE_TYPE := lite include $(BUILD_SHARED_LIBRARY) diff --git a/libs/incident/include/android/os/IncidentReportArgs.h b/libs/incident/include/android/os/IncidentReportArgs.h index 956ef6c39b99..c56f689b7419 100644 --- a/libs/incident/include/android/os/IncidentReportArgs.h +++ b/libs/incident/include/android/os/IncidentReportArgs.h @@ -24,11 +24,19 @@ #include <set> #include <vector> +#include "frameworks/base/libs/incident/proto/android/os/header.pb.h" + namespace android { namespace os { using namespace std; +// DESTINATION enum value, sync with proto/android/privacy.proto +const uint8_t DEST_LOCAL = 0; +const uint8_t DEST_EXPLICIT = 100; +const uint8_t DEST_AUTOMATIC = 200; + + class IncidentReportArgs : public Parcelable { public: IncidentReportArgs(); @@ -39,21 +47,23 @@ public: virtual status_t readFromParcel(const Parcel* in); void setAll(bool all); + void setDest(int dest); void addSection(int section); - void addHeader(const vector<int8_t>& header); + void addHeader(const IncidentHeaderProto& headerProto); - inline bool all() const { return mAll; }; + inline bool all() const { return mAll; } bool containsSection(int section) const; - + inline int dest() const { return mDest; } inline const set<int>& sections() const { return mSections; } - inline const vector<vector<int8_t>>& headers() const { return mHeaders; } + inline const vector<vector<uint8_t>>& headers() const { return mHeaders; } void merge(const IncidentReportArgs& that); private: set<int> mSections; - vector<vector<int8_t>> mHeaders; + vector<vector<uint8_t>> mHeaders; bool mAll; + int mDest; }; } diff --git a/libs/incident/proto/android/os/header.proto b/libs/incident/proto/android/os/header.proto new file mode 100644 index 000000000000..a84dc48b8b34 --- /dev/null +++ b/libs/incident/proto/android/os/header.proto @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +syntax = "proto2"; +option java_multiple_files = true; + +package android.os; + +// IncidentHeaderProto contains extra information the caller of incidentd wants +// to attach in an incident report, the data should just be informative. +message IncidentHeaderProto { + // From statsd config, the id of the anomaly alert, unique among alerts. + optional int64 alert_id = 1; + + // Format a human readable reason why an incident report is requested. + // It's optional and may directly come from a user input clicking the + // bug-report button. + optional string reason = 2; + + // Defines which stats config used to fire the request, incident report will + // only be uploaded if this value is given. + message StatsdConfigKey { + optional int32 uid = 1; // The uid pushes the config to statsd. + optional int64 id = 2; // The unique id of the statsd config. + } + optional StatsdConfigKey config_key = 3; +} diff --git a/libs/incident/proto/android/privacy.proto b/libs/incident/proto/android/privacy.proto index ae5af0e4afa7..7590b22486c5 100644 --- a/libs/incident/proto/android/privacy.proto +++ b/libs/incident/proto/android/privacy.proto @@ -34,24 +34,34 @@ enum Destination { // Fields or messages annotated with DEST_EXPLICIT can be sent // off the device with an explicit user action. - DEST_EXPLICIT = 1; + DEST_EXPLICIT = 100; - // Fields or messages annotated with DEST_LOCAL can be sent by + // Fields or messages annotated with DEST_AUTOMATIC can be sent by // automatic means, without per-sending user consent. The user // still must have previously accepted a consent to share this // information. - DEST_AUTOMATIC = 2; + DEST_AUTOMATIC = 200; - // There is no more permissive option than DEST_AUTOMATIC. + // This is the default value, which could be overridden by other values. + // The reason to pick 255 is it fits into one byte. + DEST_UNSET = 255; + + // Currently use 0, 100, 200 and 255, values in between are reserved for futures. } message PrivacyFlags { - optional Destination dest = 1 [ - default = DEST_LOCAL - ]; + optional Destination dest = 1 [ default = DEST_UNSET ]; + + // regex to filter pii sensitive info from a string field type + repeated string patterns = 2; } extend google.protobuf.FieldOptions { // Flags for automatically filtering statistics optional PrivacyFlags privacy = 102672883; } + +extend google.protobuf.MessageOptions { + // Flags used to annotate a message which all its unset primitive types inhert this tag. + optional PrivacyFlags msg_privacy = 102672883; +} diff --git a/libs/incident/proto/android/section.proto b/libs/incident/proto/android/section.proto new file mode 100644 index 000000000000..49bfe1e8a598 --- /dev/null +++ b/libs/incident/proto/android/section.proto @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +syntax = "proto2"; + +option java_package = "android"; +option java_multiple_files = true; + +import "google/protobuf/descriptor.proto"; + +package android; + +// SectionType defines how incidentd gonna get the field's data +enum SectionType { + + // Default fields, not available in incidentd + SECTION_NONE = 0; + + // incidentd reads a file to get the data for annotated field + SECTION_FILE = 1; + + // incidentd executes the given command for annotated field + SECTION_COMMAND = 2; + + // incidentd calls dumpsys for annotated field + SECTION_DUMPSYS = 3; + + // incidentd calls logs for annotated field + SECTION_LOG = 4; +} + +message SectionFlags { + optional SectionType type = 1 [default = SECTION_NONE]; + optional string args = 2; +} + +extend google.protobuf.FieldOptions { + // Flags for automatically section list generation + optional SectionFlags section = 155792027; +} diff --git a/libs/incident/src/IncidentReportArgs.cpp b/libs/incident/src/IncidentReportArgs.cpp index f60490911aed..fbc21e558806 100644 --- a/libs/incident/src/IncidentReportArgs.cpp +++ b/libs/incident/src/IncidentReportArgs.cpp @@ -25,14 +25,16 @@ namespace os { IncidentReportArgs::IncidentReportArgs() :mSections(), - mAll(false) + mAll(false), + mDest(-1) { } IncidentReportArgs::IncidentReportArgs(const IncidentReportArgs& that) :mSections(that.mSections), mHeaders(that.mHeaders), - mAll(that.mAll) + mAll(that.mAll), + mDest(that.mDest) { } @@ -67,13 +69,18 @@ IncidentReportArgs::writeToParcel(Parcel* out) const return err; } - for (vector<vector<int8_t>>::const_iterator it = mHeaders.begin(); it != mHeaders.end(); it++) { + for (vector<vector<uint8_t>>::const_iterator it = mHeaders.begin(); it != mHeaders.end(); it++) { err = out->writeByteVector(*it); if (err != NO_ERROR) { return err; } } + err = out->writeInt32(mDest); + if (err != NO_ERROR) { + return err; + } + return NO_ERROR; } @@ -120,6 +127,13 @@ IncidentReportArgs::readFromParcel(const Parcel* in) } } + int32_t dest; + err = in->readInt32(&dest); + if (err != NO_ERROR) { + return err; + } + mDest = dest; + return OK; } @@ -133,6 +147,12 @@ IncidentReportArgs::setAll(bool all) } void +IncidentReportArgs::setDest(int dest) +{ + mDest = dest; +} + +void IncidentReportArgs::addSection(int section) { if (!mAll) { @@ -141,8 +161,14 @@ IncidentReportArgs::addSection(int section) } void -IncidentReportArgs::addHeader(const vector<int8_t>& header) +IncidentReportArgs::addHeader(const IncidentHeaderProto& headerProto) { + vector<uint8_t> header; + auto serialized = headerProto.SerializeAsString(); + if (serialized.empty()) return; + for (auto it = serialized.begin(); it != serialized.end(); it++) { + header.push_back((uint8_t)*it); + } mHeaders.push_back(header); } diff --git a/libs/input/Android.bp b/libs/input/Android.bp index 43107064332e..67682a06cb6a 100644 --- a/libs/input/Android.bp +++ b/libs/input/Android.bp @@ -24,7 +24,7 @@ cc_library_shared { "libcutils", "liblog", "libutils", - "libskia", + "libhwui", "libgui", "libui", "libinput", diff --git a/libs/input/SpriteController.cpp b/libs/input/SpriteController.cpp index ed31b1202863..173cd507d943 100644 --- a/libs/input/SpriteController.cpp +++ b/libs/input/SpriteController.cpp @@ -148,8 +148,9 @@ void SpriteController::doUpdateSprites() { } } - // Resize sprites if needed, inside a global transaction. - bool haveGlobalTransaction = false; + // Resize sprites if needed. + SurfaceComposerClient::Transaction t; + bool needApplyTransaction = false; for (size_t i = 0; i < numSprites; i++) { SpriteUpdate& update = updates.editItemAt(i); @@ -158,36 +159,24 @@ void SpriteController::doUpdateSprites() { int32_t desiredHeight = update.state.icon.bitmap.height(); if (update.state.surfaceWidth < desiredWidth || update.state.surfaceHeight < desiredHeight) { - if (!haveGlobalTransaction) { - SurfaceComposerClient::openGlobalTransaction(); - haveGlobalTransaction = true; - } + needApplyTransaction = true; - status_t status = update.state.surfaceControl->setSize(desiredWidth, desiredHeight); - if (status) { - ALOGE("Error %d resizing sprite surface from %dx%d to %dx%d", - status, update.state.surfaceWidth, update.state.surfaceHeight, - desiredWidth, desiredHeight); - } else { - update.state.surfaceWidth = desiredWidth; - update.state.surfaceHeight = desiredHeight; - update.state.surfaceDrawn = false; - update.surfaceChanged = surfaceChanged = true; + t.setSize(update.state.surfaceControl, + desiredWidth, desiredHeight); + update.state.surfaceWidth = desiredWidth; + update.state.surfaceHeight = desiredHeight; + update.state.surfaceDrawn = false; + update.surfaceChanged = surfaceChanged = true; - if (update.state.surfaceVisible) { - status = update.state.surfaceControl->hide(); - if (status) { - ALOGE("Error %d hiding sprite surface after resize.", status); - } else { - update.state.surfaceVisible = false; - } - } + if (update.state.surfaceVisible) { + t.hide(update.state.surfaceControl); + update.state.surfaceVisible = false; } } } } - if (haveGlobalTransaction) { - SurfaceComposerClient::closeGlobalTransaction(); + if (needApplyTransaction) { + t.apply(); } // Redraw sprites if needed. @@ -240,8 +229,7 @@ void SpriteController::doUpdateSprites() { } } - // Set sprite surface properties and make them visible. - bool haveTransaction = false; + needApplyTransaction = false; for (size_t i = 0; i < numSprites; i++) { SpriteUpdate& update = updates.editItemAt(i); @@ -253,75 +241,59 @@ void SpriteController::doUpdateSprites() { || (wantSurfaceVisibleAndDrawn && (update.state.dirty & (DIRTY_ALPHA | DIRTY_POSITION | DIRTY_TRANSFORMATION_MATRIX | DIRTY_LAYER | DIRTY_VISIBILITY | DIRTY_HOTSPOT))))) { - status_t status; - if (!haveTransaction) { - SurfaceComposerClient::openGlobalTransaction(); - haveTransaction = true; - } + needApplyTransaction = true; if (wantSurfaceVisibleAndDrawn && (becomingVisible || (update.state.dirty & DIRTY_ALPHA))) { - status = update.state.surfaceControl->setAlpha(update.state.alpha); - if (status) { - ALOGE("Error %d setting sprite surface alpha.", status); - } + t.setAlpha(update.state.surfaceControl, + update.state.alpha); } if (wantSurfaceVisibleAndDrawn && (becomingVisible || (update.state.dirty & (DIRTY_POSITION | DIRTY_HOTSPOT)))) { - status = update.state.surfaceControl->setPosition( + t.setPosition( + update.state.surfaceControl, update.state.positionX - update.state.icon.hotSpotX, update.state.positionY - update.state.icon.hotSpotY); - if (status) { - ALOGE("Error %d setting sprite surface position.", status); - } } if (wantSurfaceVisibleAndDrawn && (becomingVisible || (update.state.dirty & DIRTY_TRANSFORMATION_MATRIX))) { - status = update.state.surfaceControl->setMatrix( + t.setMatrix( + update.state.surfaceControl, update.state.transformationMatrix.dsdx, update.state.transformationMatrix.dtdx, update.state.transformationMatrix.dsdy, update.state.transformationMatrix.dtdy); - if (status) { - ALOGE("Error %d setting sprite surface transformation matrix.", status); - } } int32_t surfaceLayer = mOverlayLayer + update.state.layer; if (wantSurfaceVisibleAndDrawn && (becomingVisible || (update.state.dirty & DIRTY_LAYER))) { - status = update.state.surfaceControl->setLayer(surfaceLayer); - if (status) { - ALOGE("Error %d setting sprite surface layer.", status); - } + t.setLayer(update.state.surfaceControl, surfaceLayer); } if (becomingVisible) { - status = update.state.surfaceControl->show(); - if (status) { - ALOGE("Error %d showing sprite surface.", status); - } else { - update.state.surfaceVisible = true; - update.surfaceChanged = surfaceChanged = true; - } + t.show(update.state.surfaceControl); + + update.state.surfaceVisible = true; + update.surfaceChanged = surfaceChanged = true; } else if (becomingHidden) { - status = update.state.surfaceControl->hide(); - if (status) { - ALOGE("Error %d hiding sprite surface.", status); - } else { - update.state.surfaceVisible = false; - update.surfaceChanged = surfaceChanged = true; - } + t.hide(update.state.surfaceControl); + + update.state.surfaceVisible = false; + update.surfaceChanged = surfaceChanged = true; } } } - if (haveTransaction) { - SurfaceComposerClient::closeGlobalTransaction(); + if (needApplyTransaction) { + status_t status = t.apply(); + if (status) { + ALOGE("Error applying Surface transaction"); + } } // If any surfaces were changed, write back the new surface properties to the sprites. diff --git a/libs/protoutil/Android.bp b/libs/protoutil/Android.bp new file mode 100644 index 000000000000..4f1d2d5a4fe5 --- /dev/null +++ b/libs/protoutil/Android.bp @@ -0,0 +1,39 @@ +// +// Copyright (C) 2017 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +cc_library { + name: "libprotoutil", + + cflags: [ + "-Wall", + "-Werror", + "-Wno-missing-field-initializers", + "-Wno-unused-variable", + "-Wunused-parameter", + ], + + srcs: [ + "src/EncodedBuffer.cpp", + "src/ProtoOutputStream.cpp", + "src/protobuf.cpp", + ], + + export_include_dirs: ["include"], + + shared_libs: [ + "libcutils", + "liblog", + ], +} diff --git a/libs/protoutil/include/android/util/EncodedBuffer.h b/libs/protoutil/include/android/util/EncodedBuffer.h new file mode 100644 index 000000000000..e568e4cf02fc --- /dev/null +++ b/libs/protoutil/include/android/util/EncodedBuffer.h @@ -0,0 +1,227 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_UTIL_ENCODED_BUFFER_H +#define ANDROID_UTIL_ENCODED_BUFFER_H + +#include <stdint.h> +#include <vector> + +namespace android { +namespace util { + +using namespace std; + +/** + * A stream of bytes containing a read pointer and a write pointer, + * backed by a set of fixed-size buffers. There are write functions for the + * primitive types stored by protocol buffers, but none of the logic + * for tags, inner objects, or any of that. + * + * Terminology: + * *Pos: Position in the whole data set (as if it were a single buffer). + * *Index: Index of a buffer within the mBuffers list. + * *Offset: Position within a buffer. + */ +class EncodedBuffer +{ +public: + EncodedBuffer(); + EncodedBuffer(size_t chunkSize); + ~EncodedBuffer(); + + class Pointer { + public: + Pointer(); + Pointer(size_t chunkSize); + + size_t pos() const; + size_t index() const; + size_t offset() const; + + Pointer* move(size_t amt); + inline Pointer* move() { return move(1); }; + Pointer* rewind(); + + Pointer copy() const; + + private: + size_t mChunkSize; + size_t mIndex; + size_t mOffset; + }; + + /******************************** Write APIs ************************************************/ + + /** + * Returns the number of bytes written in the buffer + */ + size_t size() const; + + /** + * Returns the write pointer. + */ + Pointer* wp(); + + /** + * Returns the current position of write pointer, if the write buffer is full, it will automatically + * rotate to a new buffer with given chunkSize. If NULL is returned, it means NO_MEMORY + */ + uint8_t* writeBuffer(); + + /** + * Returns the writeable size in the current write buffer . + */ + size_t currentToWrite(); + + /** + * Write a single byte to the buffer. + */ + void writeRawByte(uint8_t val); + + /** + * Write a varint32 into the buffer. Return the size of the varint. + */ + size_t writeRawVarint32(uint32_t val); + + /** + * Write a varint64 into the buffer. Return the size of the varint. + */ + size_t writeRawVarint64(uint64_t val); + + /** + * Write Fixed32 into the buffer. + */ + void writeRawFixed32(uint32_t val); + + /** + * Write Fixed64 into the buffer. + */ + void writeRawFixed64(uint64_t val); + + /** + * Write a protobuf header. Return the size of the header. + */ + size_t writeHeader(uint32_t fieldId, uint8_t wireType); + + /********************************* Edit APIs ************************************************/ + /** + * Returns the edit pointer. + */ + Pointer* ep(); + + /** + * Read a single byte at ep, and move ep to next byte; + */ + uint8_t readRawByte(); + + /** + * Read varint starting at ep, ep will move to pos of next byte. + */ + uint64_t readRawVarint(); + + /** + * Read 4 bytes starting at ep, ep will move to pos of next byte. + */ + uint32_t readRawFixed32(); + + /** + * Read 8 bytes starting at ep, ep will move to pos of next byte. + */ + uint64_t readRawFixed64(); + + /** + * Edit 4 bytes starting at pos. + */ + void editRawFixed32(size_t pos, uint32_t val); + + /** + * Copy _size_ bytes of data starting at __srcPos__ to wp. + */ + void copy(size_t srcPos, size_t size); + + /********************************* Read APIs ************************************************/ + class iterator; + friend class iterator; + class iterator { + public: + iterator(const EncodedBuffer& buffer); + + /** + * Returns the number of bytes written in the buffer + */ + size_t size() const; + + /** + * Returns the size of total bytes read. + */ + size_t bytesRead() const; + + /** + * Returns the read pointer. + */ + Pointer* rp(); + + /** + * Returns the current position of read pointer, if NULL is returned, it reaches end of buffer. + */ + uint8_t const* readBuffer(); + + /** + * Returns the readable size in the current read buffer. + */ + size_t currentToRead(); + + /** + * Returns true if next bytes is available for read. + */ + bool hasNext(); + + /** + * Reads the current byte and moves pointer 1 bit. + */ + uint8_t next(); + + /** + * Read varint from iterator, the iterator will point to next available byte. + */ + uint64_t readRawVarint(); + + private: + const EncodedBuffer& mData; + Pointer mRp; + }; + + /** + * Returns the iterator of EncodedBuffer so it guarantees consumers won't be able to modified the buffer. + */ + iterator begin() const; + +private: + size_t mChunkSize; + vector<uint8_t*> mBuffers; + + Pointer mWp; + Pointer mEp; + + inline uint8_t* at(const Pointer& p) const; // helper function to get value +}; + +} // util +} // android + +#endif // ANDROID_UTIL_ENCODED_BUFFER_H + diff --git a/libs/protoutil/include/android/util/ProtoOutputStream.h b/libs/protoutil/include/android/util/ProtoOutputStream.h new file mode 100644 index 000000000000..faea9b21311d --- /dev/null +++ b/libs/protoutil/include/android/util/ProtoOutputStream.h @@ -0,0 +1,164 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_UTIL_PROTOOUTPUT_STREAM_H +#define ANDROID_UTIL_PROTOOUTPUT_STREAM_H + +#include <android/util/EncodedBuffer.h> + +#include <string> + +namespace android { +namespace util { + +/** + * Position of the field type in a (long long) fieldId. + */ +const uint64_t FIELD_TYPE_SHIFT = 32; + +/** + * Mask for the field types stored in a fieldId. Leaves a whole + * byte for future expansion, even though there are currently only 17 types. + */ +const uint64_t FIELD_TYPE_MASK = 0x0ffULL << FIELD_TYPE_SHIFT; + +/** + * The types are copied from external/protobuf/src/google/protobuf/descriptor.h directly, + * so no extra mapping needs to be maintained in this case. + */ +const uint64_t FIELD_TYPE_UNKNOWN = 0; +const uint64_t FIELD_TYPE_DOUBLE = 1ULL << FIELD_TYPE_SHIFT; // double, exactly eight bytes on the wire. +const uint64_t FIELD_TYPE_FLOAT = 2ULL << FIELD_TYPE_SHIFT; // float, exactly four bytes on the wire. +const uint64_t FIELD_TYPE_INT64 = 3ULL << FIELD_TYPE_SHIFT; // int64, varint on the wire. Negative numbers + // take 10 bytes. Use TYPE_SINT64 if negative + // values are likely. +const uint64_t FIELD_TYPE_UINT64 = 4ULL << FIELD_TYPE_SHIFT; // uint64, varint on the wire. +const uint64_t FIELD_TYPE_INT32 = 5ULL << FIELD_TYPE_SHIFT; // int32, varint on the wire. Negative numbers + // take 10 bytes. Use TYPE_SINT32 if negative + // values are likely. +const uint64_t FIELD_TYPE_FIXED64 = 6ULL << FIELD_TYPE_SHIFT; // uint64, exactly eight bytes on the wire. +const uint64_t FIELD_TYPE_FIXED32 = 7ULL << FIELD_TYPE_SHIFT; // uint32, exactly four bytes on the wire. +const uint64_t FIELD_TYPE_BOOL = 8ULL << FIELD_TYPE_SHIFT; // bool, varint on the wire. +const uint64_t FIELD_TYPE_STRING = 9ULL << FIELD_TYPE_SHIFT; // UTF-8 text. +// const uint64_t FIELD_TYPE_GROUP = 10ULL << FIELD_TYPE_SHIFT; // Tag-delimited message. Deprecated. +const uint64_t FIELD_TYPE_MESSAGE = 11ULL << FIELD_TYPE_SHIFT; // Length-delimited message. + +const uint64_t FIELD_TYPE_BYTES = 12ULL << FIELD_TYPE_SHIFT; // Arbitrary byte array. +const uint64_t FIELD_TYPE_UINT32 = 13ULL << FIELD_TYPE_SHIFT; // uint32, varint on the wire +const uint64_t FIELD_TYPE_ENUM = 14ULL << FIELD_TYPE_SHIFT; // Enum, varint on the wire +const uint64_t FIELD_TYPE_SFIXED32 = 15ULL << FIELD_TYPE_SHIFT; // int32, exactly four bytes on the wire +const uint64_t FIELD_TYPE_SFIXED64 = 16ULL << FIELD_TYPE_SHIFT; // int64, exactly eight bytes on the wire +const uint64_t FIELD_TYPE_SINT32 = 17ULL << FIELD_TYPE_SHIFT; // int32, ZigZag-encoded varint on the wire +const uint64_t FIELD_TYPE_SINT64 = 18ULL << FIELD_TYPE_SHIFT; // int64, ZigZag-encoded varint on the wire + +// +// FieldId flags for whether the field is single, repeated or packed. +// TODO: packed is not supported yet. +// +const uint64_t FIELD_COUNT_SHIFT = 40; +const uint64_t FIELD_COUNT_MASK = 0x0fULL << FIELD_COUNT_SHIFT; +const uint64_t FIELD_COUNT_UNKNOWN = 0; +const uint64_t FIELD_COUNT_SINGLE = 1ULL << FIELD_COUNT_SHIFT; +const uint64_t FIELD_COUNT_REPEATED = 2ULL << FIELD_COUNT_SHIFT; +const uint64_t FIELD_COUNT_PACKED = 5ULL << FIELD_COUNT_SHIFT; + +/** + * Class to write to a protobuf stream. + * + * Each write method takes an ID code from the protoc generated classes + * and the value to write. To make a nested object, call start + * and then end when you are done. + * + * See the java version implementation (ProtoOutputStream.java) for more infos. + */ +class ProtoOutputStream +{ +public: + ProtoOutputStream(); + ~ProtoOutputStream(); + + /** + * Write APIs for dumping protobuf data. Returns true if the write succeeds. + */ + bool write(uint64_t fieldId, double val); + bool write(uint64_t fieldId, float val); + bool write(uint64_t fieldId, int val); + bool write(uint64_t fieldId, long long val); + bool write(uint64_t fieldId, bool val); + bool write(uint64_t fieldId, std::string val); + bool write(uint64_t fieldId, const char* val, size_t size); + + /** + * Starts a sub-message write session. + * Returns a token of this write session. + * Must call end(token) when finish write this sub-message. + */ + long long start(uint64_t fieldId); + void end(long long token); + + /** + * Returns how many bytes are buffered in ProtoOutputStream. + * Notice, this is not the actual(compact) size of the output data. + */ + size_t bytesWritten(); + + /** + * Flushes the protobuf data out to given fd. When the following functions are called, + * it is not able to write to ProtoOutputStream any more since the data is compact. + */ + size_t size(); // Get the size of the serialized protobuf. + EncodedBuffer::iterator data(); // Get the reader apis of the data. + bool flush(int fd); // Flush data directly to a file descriptor. + + // Please don't use the following functions to dump protos unless you are familiar with protobuf encoding. + void writeRawVarint(uint64_t varint); + void writeLengthDelimitedHeader(uint32_t id, size_t size); + void writeRawByte(uint8_t byte); + +private: + EncodedBuffer mBuffer; + size_t mCopyBegin; + bool mCompact; + int mDepth; + int mObjectId; + long long mExpectedObjectToken; + + inline void writeDoubleImpl(uint32_t id, double val); + inline void writeFloatImpl(uint32_t id, float val); + inline void writeInt64Impl(uint32_t id, long long val); + inline void writeInt32Impl(uint32_t id, int val); + inline void writeUint64Impl(uint32_t id, uint64_t val); + inline void writeUint32Impl(uint32_t id, uint32_t val); + inline void writeFixed64Impl(uint32_t id, uint64_t val); + inline void writeFixed32Impl(uint32_t id, uint32_t val); + inline void writeSFixed64Impl(uint32_t id, long long val); + inline void writeSFixed32Impl(uint32_t id, int val); + inline void writeZigzagInt64Impl(uint32_t id, long long val); + inline void writeZigzagInt32Impl(uint32_t id, int val); + inline void writeEnumImpl(uint32_t id, int val); + inline void writeBoolImpl(uint32_t id, bool val); + inline void writeUtf8StringImpl(uint32_t id, const char* val, size_t size); + inline void writeMessageBytesImpl(uint32_t id, const char* val, size_t size); + + bool compact(); + size_t editEncodedSize(size_t rawSize); + bool compactSize(size_t rawSize); +}; + +} +} + +#endif // ANDROID_UTIL_PROTOOUTPUT_STREAM_H diff --git a/libs/protoutil/include/android/util/protobuf.h b/libs/protoutil/include/android/util/protobuf.h new file mode 100644 index 000000000000..ca45e263b20e --- /dev/null +++ b/libs/protoutil/include/android/util/protobuf.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_UTIL_PROTOBUF_H +#define ANDROID_UTIL_PROTOBUF_H + +#include <stdint.h> + +namespace android { +namespace util { + +using namespace std; + +const int FIELD_ID_SHIFT = 3; +const uint8_t WIRE_TYPE_MASK = (1 << FIELD_ID_SHIFT) - 1; + +const uint8_t WIRE_TYPE_VARINT = 0; +const uint8_t WIRE_TYPE_FIXED64 = 1; +const uint8_t WIRE_TYPE_LENGTH_DELIMITED = 2; +const uint8_t WIRE_TYPE_FIXED32 = 5; + +/** + * Read the wire type from varint, it is the smallest 3 bits. + */ +uint8_t read_wire_type(uint32_t varint); + +/** + * Read field id from varint, it is varint >> 3; + */ +uint32_t read_field_id(uint32_t varint); + +/** + * Get the size of a varint. + */ +size_t get_varint_size(uint64_t varint); + +/** + * Write a varint into the buffer. Return the next position to write at. + * There must be 10 bytes in the buffer. + */ +uint8_t* write_raw_varint(uint8_t* buf, uint64_t val); + +/** + * Write a protobuf WIRE_TYPE_LENGTH_DELIMITED header. Return the next position + * to write at. There must be 20 bytes in the buffer. + */ +uint8_t* write_length_delimited_tag_header(uint8_t* buf, uint32_t fieldId, size_t size); + +} // util +} // android + +#endif // ANDROID_UTIL_PROTOUBUF_H diff --git a/libs/protoutil/src/EncodedBuffer.cpp b/libs/protoutil/src/EncodedBuffer.cpp new file mode 100644 index 000000000000..435ae8836217 --- /dev/null +++ b/libs/protoutil/src/EncodedBuffer.cpp @@ -0,0 +1,349 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <android/util/EncodedBuffer.h> +#include <android/util/protobuf.h> + +#include <stdlib.h> + +namespace android { +namespace util { + +const size_t BUFFER_SIZE = 8 * 1024; // 8 KB + +EncodedBuffer::Pointer::Pointer() : Pointer(BUFFER_SIZE) +{ +} + +EncodedBuffer::Pointer::Pointer(size_t chunkSize) + :mIndex(0), + mOffset(0) +{ + mChunkSize = chunkSize == 0 ? BUFFER_SIZE : chunkSize; +} + +size_t +EncodedBuffer::Pointer::pos() const +{ + return mIndex * mChunkSize + mOffset; +} + +size_t +EncodedBuffer::Pointer::index() const +{ + return mIndex; +} + +size_t +EncodedBuffer::Pointer::offset() const +{ + return mOffset; +} + +EncodedBuffer::Pointer* +EncodedBuffer::Pointer::move(size_t amt) +{ + size_t newOffset = mOffset + amt; + mIndex += newOffset / mChunkSize; + mOffset = newOffset % mChunkSize; + return this; +} + +EncodedBuffer::Pointer* +EncodedBuffer::Pointer::rewind() +{ + mIndex = 0; + mOffset = 0; + return this; +} + +EncodedBuffer::Pointer +EncodedBuffer::Pointer::copy() const +{ + Pointer p = Pointer(mChunkSize); + p.mIndex = mIndex; + p.mOffset = mOffset; + return p; +} + +// =========================================================== +EncodedBuffer::EncodedBuffer() : EncodedBuffer(0) +{ +} + +EncodedBuffer::EncodedBuffer(size_t chunkSize) + :mBuffers() +{ + mChunkSize = chunkSize == 0 ? BUFFER_SIZE : chunkSize; + mWp = Pointer(mChunkSize); + mEp = Pointer(mChunkSize); +} + +EncodedBuffer::~EncodedBuffer() +{ + for (size_t i=0; i<mBuffers.size(); i++) { + uint8_t* buf = mBuffers[i]; + free(buf); + } +} + +inline uint8_t* +EncodedBuffer::at(const Pointer& p) const +{ + return mBuffers[p.index()] + p.offset(); +} + +/******************************** Write APIs ************************************************/ +size_t +EncodedBuffer::size() const +{ + return mWp.pos(); +} + +EncodedBuffer::Pointer* +EncodedBuffer::wp() +{ + return &mWp; +} + +uint8_t* +EncodedBuffer::writeBuffer() +{ + // This prevents write pointer move too fast than allocating the buffer. + if (mWp.index() > mBuffers.size()) return NULL; + uint8_t* buf = NULL; + if (mWp.index() == mBuffers.size()) { + buf = (uint8_t*)malloc(mChunkSize); + + if (buf == NULL) return NULL; // This indicates NO_MEMORY + + mBuffers.push_back(buf); + } + return at(mWp); +} + +size_t +EncodedBuffer::currentToWrite() +{ + return mChunkSize - mWp.offset(); +} + +void +EncodedBuffer::writeRawByte(uint8_t val) +{ + *writeBuffer() = val; + mWp.move(); +} + +size_t +EncodedBuffer::writeRawVarint64(uint64_t val) +{ + size_t size = 0; + while (true) { + size++; + if ((val & ~0x7F) == 0) { + writeRawByte((uint8_t) val); + return size; + } else { + writeRawByte((uint8_t)((val & 0x7F) | 0x80)); + val >>= 7; + } + } +} + +size_t +EncodedBuffer::writeRawVarint32(uint32_t val) +{ + uint64_t v =(uint64_t)val; + return writeRawVarint64(v); +} + +void +EncodedBuffer::writeRawFixed32(uint32_t val) +{ + writeRawByte((uint8_t) val); + writeRawByte((uint8_t) (val>>8)); + writeRawByte((uint8_t) (val>>16)); + writeRawByte((uint8_t) (val>>24)); +} + +void +EncodedBuffer::writeRawFixed64(uint64_t val) +{ + writeRawByte((uint8_t) val); + writeRawByte((uint8_t) (val>>8)); + writeRawByte((uint8_t) (val>>16)); + writeRawByte((uint8_t) (val>>24)); + writeRawByte((uint8_t) (val>>32)); + writeRawByte((uint8_t) (val>>40)); + writeRawByte((uint8_t) (val>>48)); + writeRawByte((uint8_t) (val>>56)); +} + +size_t +EncodedBuffer::writeHeader(uint32_t fieldId, uint8_t wireType) +{ + return writeRawVarint32((fieldId << FIELD_ID_SHIFT) | wireType); +} + +/******************************** Edit APIs ************************************************/ +EncodedBuffer::Pointer* +EncodedBuffer::ep() +{ + return &mEp; +} + +uint8_t +EncodedBuffer::readRawByte() +{ + uint8_t val = *at(mEp); + mEp.move(); + return val; +} + +uint64_t +EncodedBuffer::readRawVarint() +{ + uint64_t val = 0, shift = 0; + size_t start = mEp.pos(); + while (true) { + uint8_t byte = readRawByte(); + val += (byte & 0x7F) << shift; + if ((byte & 0x80) == 0) break; + shift += 7; + } + return val; +} + +uint32_t +EncodedBuffer::readRawFixed32() +{ + uint32_t val = 0; + for (auto i=0; i<32; i+=8) { + val += (uint32_t)readRawByte() << i; + } + return val; +} + +uint64_t +EncodedBuffer::readRawFixed64() +{ + uint64_t val = 0; + for (auto i=0; i<64; i+=8) { + val += (uint64_t)readRawByte() << i; + } + return val; +} + +void +EncodedBuffer::editRawFixed32(size_t pos, uint32_t val) +{ + size_t oldPos = mEp.pos(); + mEp.rewind()->move(pos); + for (auto i=0; i<32; i+=8) { + *at(mEp) = (uint8_t) (val >> i); + mEp.move(); + } + mEp.rewind()->move(oldPos); +} + +void +EncodedBuffer::copy(size_t srcPos, size_t size) +{ + if (size == 0) return; + Pointer cp(mChunkSize); + cp.move(srcPos); + + while (cp.pos() < srcPos + size) { + writeRawByte(*at(cp)); + cp.move(); + } +} + +/********************************* Read APIs ************************************************/ +EncodedBuffer::iterator +EncodedBuffer::begin() const +{ + return EncodedBuffer::iterator(*this); +} + +EncodedBuffer::iterator::iterator(const EncodedBuffer& buffer) + :mData(buffer), + mRp(buffer.mChunkSize) +{ +} + +size_t +EncodedBuffer::iterator::size() const +{ + return mData.size(); +} + +size_t +EncodedBuffer::iterator::bytesRead() const +{ + return mRp.pos(); +} + +EncodedBuffer::Pointer* +EncodedBuffer::iterator::rp() +{ + return &mRp; +} + +uint8_t const* +EncodedBuffer::iterator::readBuffer() +{ + return hasNext() ? const_cast<uint8_t const*>(mData.at(mRp)) : NULL; +} + +size_t +EncodedBuffer::iterator::currentToRead() +{ + return (mData.mWp.index() > mRp.index()) ? + mData.mChunkSize - mRp.offset() : + mData.mWp.offset() - mRp.offset(); +} + +bool +EncodedBuffer::iterator::hasNext() +{ + return mRp.pos() < mData.mWp.pos(); +} + +uint8_t +EncodedBuffer::iterator::next() +{ + uint8_t res = *(mData.at(mRp)); + mRp.move(); + return res; +} + +uint64_t +EncodedBuffer::iterator::readRawVarint() +{ + uint64_t val = 0, shift = 0; + while (true) { + uint8_t byte = next(); + val += (byte & 0x7F) << shift; + if ((byte & 0x80) == 0) break; + shift += 7; + } + return val; +} + +} // util +} // android diff --git a/libs/protoutil/src/ProtoOutputStream.cpp b/libs/protoutil/src/ProtoOutputStream.cpp new file mode 100644 index 000000000000..1904d40ea67a --- /dev/null +++ b/libs/protoutil/src/ProtoOutputStream.cpp @@ -0,0 +1,642 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#define LOG_TAG "libprotoutil" + +#include <android/util/protobuf.h> +#include <android/util/ProtoOutputStream.h> +#include <cutils/log.h> + +namespace android { +namespace util { + +ProtoOutputStream::ProtoOutputStream() + :mBuffer(), + mCopyBegin(0), + mCompact(false), + mDepth(0), + mObjectId(0), + mExpectedObjectToken(0LL) +{ +} + +ProtoOutputStream::~ProtoOutputStream() +{ +} + +bool +ProtoOutputStream::write(uint64_t fieldId, double val) +{ + if (mCompact) return false; + const uint32_t id = (uint32_t)fieldId; + switch (fieldId & FIELD_TYPE_MASK) { + case FIELD_TYPE_DOUBLE: writeDoubleImpl(id, (double)val); break; + case FIELD_TYPE_FLOAT: writeFloatImpl(id, (float)val); break; + case FIELD_TYPE_INT64: writeInt64Impl(id, (long long)val); break; + case FIELD_TYPE_UINT64: writeUint64Impl(id, (uint64_t)val); break; + case FIELD_TYPE_INT32: writeInt32Impl(id, (int)val); break; + case FIELD_TYPE_FIXED64: writeFixed64Impl(id, (uint64_t)val); break; + case FIELD_TYPE_FIXED32: writeFixed32Impl(id, (uint32_t)val); break; + case FIELD_TYPE_UINT32: writeUint32Impl(id, (uint32_t)val); break; + case FIELD_TYPE_SFIXED32: writeSFixed32Impl(id, (int)val); break; + case FIELD_TYPE_SFIXED64: writeSFixed64Impl(id, (long long)val); break; + case FIELD_TYPE_SINT32: writeZigzagInt32Impl(id, (int)val); break; + case FIELD_TYPE_SINT64: writeZigzagInt64Impl(id, (long long)val); break; + default: + ALOGW("Field type %d is not supported when writing double val.", + (int)((fieldId & FIELD_TYPE_MASK) >> FIELD_TYPE_SHIFT)); + return false; + } + return true; +} + +bool +ProtoOutputStream::write(uint64_t fieldId, float val) +{ + if (mCompact) return false; + const uint32_t id = (uint32_t)fieldId; + switch (fieldId & FIELD_TYPE_MASK) { + case FIELD_TYPE_DOUBLE: writeDoubleImpl(id, (double)val); break; + case FIELD_TYPE_FLOAT: writeFloatImpl(id, (float)val); break; + case FIELD_TYPE_INT64: writeInt64Impl(id, (long long)val); break; + case FIELD_TYPE_UINT64: writeUint64Impl(id, (uint64_t)val); break; + case FIELD_TYPE_INT32: writeInt32Impl(id, (int)val); break; + case FIELD_TYPE_FIXED64: writeFixed64Impl(id, (uint64_t)val); break; + case FIELD_TYPE_FIXED32: writeFixed32Impl(id, (uint32_t)val); break; + case FIELD_TYPE_UINT32: writeUint32Impl(id, (uint32_t)val); break; + case FIELD_TYPE_SFIXED32: writeSFixed32Impl(id, (int)val); break; + case FIELD_TYPE_SFIXED64: writeSFixed64Impl(id, (long long)val); break; + case FIELD_TYPE_SINT32: writeZigzagInt32Impl(id, (int)val); break; + case FIELD_TYPE_SINT64: writeZigzagInt64Impl(id, (long long)val); break; + default: + ALOGW("Field type %d is not supported when writing float val.", + (int)((fieldId & FIELD_TYPE_MASK) >> FIELD_TYPE_SHIFT)); + return false; + } + return true; +} + +bool +ProtoOutputStream::write(uint64_t fieldId, int val) +{ + if (mCompact) return false; + const uint32_t id = (uint32_t)fieldId; + switch (fieldId & FIELD_TYPE_MASK) { + case FIELD_TYPE_DOUBLE: writeDoubleImpl(id, (double)val); break; + case FIELD_TYPE_FLOAT: writeFloatImpl(id, (float)val); break; + case FIELD_TYPE_INT64: writeInt64Impl(id, (long long)val); break; + case FIELD_TYPE_UINT64: writeUint64Impl(id, (uint64_t)val); break; + case FIELD_TYPE_INT32: writeInt32Impl(id, (int)val); break; + case FIELD_TYPE_FIXED64: writeFixed64Impl(id, (uint64_t)val); break; + case FIELD_TYPE_FIXED32: writeFixed32Impl(id, (uint32_t)val); break; + case FIELD_TYPE_UINT32: writeUint32Impl(id, (uint32_t)val); break; + case FIELD_TYPE_SFIXED32: writeSFixed32Impl(id, (int)val); break; + case FIELD_TYPE_SFIXED64: writeSFixed64Impl(id, (long long)val); break; + case FIELD_TYPE_SINT32: writeZigzagInt32Impl(id, (int)val); break; + case FIELD_TYPE_SINT64: writeZigzagInt64Impl(id, (long long)val); break; + case FIELD_TYPE_ENUM: writeEnumImpl(id, (int)val); break; + case FIELD_TYPE_BOOL: writeBoolImpl(id, val != 0); break; + default: + ALOGW("Field type %d is not supported when writing int val.", + (int)((fieldId & FIELD_TYPE_MASK) >> FIELD_TYPE_SHIFT)); + return false; + } + return true; +} + +bool +ProtoOutputStream::write(uint64_t fieldId, long long val) +{ + if (mCompact) return false; + const uint32_t id = (uint32_t)fieldId; + switch (fieldId & FIELD_TYPE_MASK) { + case FIELD_TYPE_DOUBLE: writeDoubleImpl(id, (double)val); break; + case FIELD_TYPE_FLOAT: writeFloatImpl(id, (float)val); break; + case FIELD_TYPE_INT64: writeInt64Impl(id, (long long)val); break; + case FIELD_TYPE_UINT64: writeUint64Impl(id, (uint64_t)val); break; + case FIELD_TYPE_INT32: writeInt32Impl(id, (int)val); break; + case FIELD_TYPE_FIXED64: writeFixed64Impl(id, (uint64_t)val); break; + case FIELD_TYPE_FIXED32: writeFixed32Impl(id, (uint32_t)val); break; + case FIELD_TYPE_UINT32: writeUint32Impl(id, (uint32_t)val); break; + case FIELD_TYPE_SFIXED32: writeSFixed32Impl(id, (int)val); break; + case FIELD_TYPE_SFIXED64: writeSFixed64Impl(id, (long long)val); break; + case FIELD_TYPE_SINT32: writeZigzagInt32Impl(id, (int)val); break; + case FIELD_TYPE_SINT64: writeZigzagInt64Impl(id, (long long)val); break; + case FIELD_TYPE_ENUM: writeEnumImpl(id, (int)val); break; + case FIELD_TYPE_BOOL: writeBoolImpl(id, val != 0); break; + default: + ALOGW("Field type %d is not supported when writing long long val.", + (int)((fieldId & FIELD_TYPE_MASK) >> FIELD_TYPE_SHIFT)); + return false; + } + return true; +} + +bool +ProtoOutputStream::write(uint64_t fieldId, bool val) +{ + if (mCompact) return false; + const uint32_t id = (uint32_t)fieldId; + switch (fieldId & FIELD_TYPE_MASK) { + case FIELD_TYPE_BOOL: + writeBoolImpl(id, val); + return true; + default: + ALOGW("Field type %d is not supported when writing bool val.", + (int)((fieldId & FIELD_TYPE_MASK) >> FIELD_TYPE_SHIFT)); + return false; + } +} + +bool +ProtoOutputStream::write(uint64_t fieldId, string val) +{ + if (mCompact) return false; + const uint32_t id = (uint32_t)fieldId; + switch (fieldId & FIELD_TYPE_MASK) { + case FIELD_TYPE_STRING: + writeUtf8StringImpl(id, val.c_str(), val.size()); + return true; + default: + ALOGW("Field type %d is not supported when writing string val.", + (int)((fieldId & FIELD_TYPE_MASK) >> FIELD_TYPE_SHIFT)); + return false; + } +} + +bool +ProtoOutputStream::write(uint64_t fieldId, const char* val, size_t size) +{ + if (mCompact) return false; + const uint32_t id = (uint32_t)fieldId; + switch (fieldId & FIELD_TYPE_MASK) { + case FIELD_TYPE_STRING: + case FIELD_TYPE_BYTES: + writeUtf8StringImpl(id, val, size); + return true; + case FIELD_TYPE_MESSAGE: + // can directly write valid format of message bytes into ProtoOutputStream without calling start/end + writeMessageBytesImpl(id, val, size); + return true; + default: + ALOGW("Field type %d is not supported when writing char[] val.", + (int)((fieldId & FIELD_TYPE_MASK) >> FIELD_TYPE_SHIFT)); + return false; + } +} + +/** + * Make a token. + * Bits 61-63 - tag size (So we can go backwards later if the object had not data) + * - 3 bits, max value 7, max value needed 5 + * Bit 60 - true if the object is repeated + * Bits 59-51 - depth (For error checking) + * - 9 bits, max value 512, when checking, value is masked (if we really + * are more than 512 levels deep) + * Bits 32-50 - objectId (For error checking) + * - 19 bits, max value 524,288. that's a lot of objects. IDs will wrap + * because of the overflow, and only the tokens are compared. + * Bits 0-31 - offset of the first size field in the buffer. + */ +long long +makeToken(int tagSize, bool repeated, int depth, int objectId, int sizePos) { + return ((0x07L & (long long)tagSize) << 61) + | (repeated ? (1LL << 60) : 0) + | (0x01ffL & (long long)depth) << 51 + | (0x07ffffL & (long long)objectId) << 32 + | (0x0ffffffffL & (long long)sizePos); +} + +/** + * Get the encoded tag size from the token. + */ +static int getTagSizeFromToken(long long token) { + return (int)(0x7 & (token >> 61)); +} + +/** + * Get the nesting depth of startObject calls from the token. + */ +static int getDepthFromToken(long long token) { + return (int)(0x01ff & (token >> 51)); +} + +/** + * Get the location of the childRawSize (the first 32 bit size field) in this object. + */ +static int getSizePosFromToken(long long token) { + return (int)token; +} + +long long +ProtoOutputStream::start(uint64_t fieldId) +{ + if ((fieldId & FIELD_TYPE_MASK) != FIELD_TYPE_MESSAGE) { + ALOGE("Can't call start for non-message type field: 0x%llx", (long long)fieldId); + return 0; + } + + uint32_t id = (uint32_t)fieldId; + mBuffer.writeHeader(id, WIRE_TYPE_LENGTH_DELIMITED); + + size_t sizePos = mBuffer.wp()->pos(); + + mDepth++; + mObjectId++; + mBuffer.writeRawFixed64(mExpectedObjectToken); // push previous token into stack. + + mExpectedObjectToken = makeToken(get_varint_size(id), + (bool)(fieldId & FIELD_COUNT_REPEATED), mDepth, mObjectId, sizePos); + return mExpectedObjectToken; +} + +void +ProtoOutputStream::end(long long token) +{ + if (token != mExpectedObjectToken) { + ALOGE("Unexpected token: 0x%llx, should be 0x%llx", token, mExpectedObjectToken); + return; + } + + int depth = getDepthFromToken(token); + if (depth != (mDepth & 0x01ff)) { + ALOGE("Unexpected depth: %d, should be %d", depth, mDepth); + return; + } + mDepth--; + + int sizePos = getSizePosFromToken(token); + // number of bytes written in this start-end session. + int childRawSize = mBuffer.wp()->pos() - sizePos - 8; + + // retrieve the old token from stack. + mBuffer.ep()->rewind()->move(sizePos); + mExpectedObjectToken = mBuffer.readRawFixed64(); + + // If raw size is larger than 0, write the negative value here to indicate a compact is needed. + if (childRawSize > 0) { + mBuffer.editRawFixed32(sizePos, -childRawSize); + mBuffer.editRawFixed32(sizePos+4, -1); + } else { + // reset wp which erase the header tag of the message when its size is 0. + mBuffer.wp()->rewind()->move(sizePos - getTagSizeFromToken(token)); + } +} + +size_t +ProtoOutputStream::bytesWritten() +{ + return mBuffer.size(); +} + +bool +ProtoOutputStream::compact() { + if (mCompact) return true; + if (mDepth != 0) { + ALOGE("Can't compact when depth(%d) is not zero. Missing calls to end.", mDepth); + return false; + } + // record the size of the original buffer. + size_t rawBufferSize = mBuffer.size(); + if (rawBufferSize == 0) return true; // nothing to do if the buffer is empty; + + // reset edit pointer and recursively compute encoded size of messages. + mBuffer.ep()->rewind(); + if (editEncodedSize(rawBufferSize) == 0) { + ALOGE("Failed to editEncodedSize."); + return false; + } + + // reset both edit pointer and write pointer, and compact recursively. + mBuffer.ep()->rewind(); + mBuffer.wp()->rewind(); + if (!compactSize(rawBufferSize)) { + ALOGE("Failed to compactSize."); + return false; + } + // copy the reset to the buffer. + if (mCopyBegin < rawBufferSize) { + mBuffer.copy(mCopyBegin, rawBufferSize - mCopyBegin); + } + + // mark true means it is not legal to write to this ProtoOutputStream anymore + mCompact = true; + return true; +} + +/** + * First compaction pass. Iterate through the data, and fill in the + * nested object sizes so the next pass can compact them. + */ +size_t +ProtoOutputStream::editEncodedSize(size_t rawSize) +{ + size_t objectStart = mBuffer.ep()->pos(); + size_t objectEnd = objectStart + rawSize; + size_t encodedSize = 0; + int childRawSize, childEncodedSize; + size_t childEncodedSizePos; + + while (mBuffer.ep()->pos() < objectEnd) { + uint32_t tag = (uint32_t)mBuffer.readRawVarint(); + encodedSize += get_varint_size(tag); + switch (read_wire_type(tag)) { + case WIRE_TYPE_VARINT: + do { + encodedSize++; + } while ((mBuffer.readRawByte() & 0x80) != 0); + break; + case WIRE_TYPE_FIXED64: + encodedSize += 8; + mBuffer.ep()->move(8); + break; + case WIRE_TYPE_LENGTH_DELIMITED: + childRawSize = (int)mBuffer.readRawFixed32(); + childEncodedSizePos = mBuffer.ep()->pos(); + childEncodedSize = (int)mBuffer.readRawFixed32(); + if (childRawSize >= 0 && childRawSize == childEncodedSize) { + mBuffer.ep()->move(childRawSize); + } else if (childRawSize < 0 && childEncodedSize == -1){ + childEncodedSize = editEncodedSize(-childRawSize); + mBuffer.editRawFixed32(childEncodedSizePos, childEncodedSize); + } else { + ALOGE("Bad raw or encoded values: raw=%d, encoded=%d at %zu", + childRawSize, childEncodedSize, childEncodedSizePos); + return 0; + } + encodedSize += get_varint_size(childEncodedSize) + childEncodedSize; + break; + case WIRE_TYPE_FIXED32: + encodedSize += 4; + mBuffer.ep()->move(4); + break; + default: + ALOGE("Unexpected wire type %d in editEncodedSize at [%zu, %zu]", + read_wire_type(tag), objectStart, objectEnd); + return 0; + } + } + return encodedSize; +} + +/** + * Second compaction pass. Iterate through the data, and copy the data + * forward in the buffer, converting the pairs of uint32s into a single + * unsigned varint of the size. + */ +bool +ProtoOutputStream::compactSize(size_t rawSize) +{ + size_t objectStart = mBuffer.ep()->pos(); + size_t objectEnd = objectStart + rawSize; + int childRawSize, childEncodedSize; + + while (mBuffer.ep()->pos() < objectEnd) { + uint32_t tag = (uint32_t)mBuffer.readRawVarint(); + switch (read_wire_type(tag)) { + case WIRE_TYPE_VARINT: + while ((mBuffer.readRawByte() & 0x80) != 0) {} + break; + case WIRE_TYPE_FIXED64: + mBuffer.ep()->move(8); + break; + case WIRE_TYPE_LENGTH_DELIMITED: + mBuffer.copy(mCopyBegin, mBuffer.ep()->pos() - mCopyBegin); + + childRawSize = (int)mBuffer.readRawFixed32(); + childEncodedSize = (int)mBuffer.readRawFixed32(); + mCopyBegin = mBuffer.ep()->pos(); + + // write encoded size to buffer. + mBuffer.writeRawVarint32(childEncodedSize); + if (childRawSize >= 0 && childRawSize == childEncodedSize) { + mBuffer.ep()->move(childEncodedSize); + } else if (childRawSize < 0){ + if (!compactSize(-childRawSize)) return false; + } else { + ALOGE("Bad raw or encoded values: raw=%d, encoded=%d", + childRawSize, childEncodedSize); + return false; + } + break; + case WIRE_TYPE_FIXED32: + mBuffer.ep()->move(4); + break; + default: + ALOGE("Unexpected wire type %d in compactSize at [%zu, %zu]", + read_wire_type(tag), objectStart, objectEnd); + return false; + } + } + return true; +} + +size_t +ProtoOutputStream::size() +{ + compact(); + return mBuffer.size(); +} + +static bool write_all(int fd, uint8_t const* buf, size_t size) +{ + while (size > 0) { + ssize_t amt = ::write(fd, buf, size); + if (amt < 0) { + return false; + } + size -= amt; + buf += amt; + } + return true; +} + +bool +ProtoOutputStream::flush(int fd) +{ + if (fd < 0) return false; + if (!compact()) return false; + + EncodedBuffer::iterator it = mBuffer.begin(); + while (it.readBuffer() != NULL) { + if (!write_all(fd, it.readBuffer(), it.currentToRead())) return false; + it.rp()->move(it.currentToRead()); + } + return true; +} + +EncodedBuffer::iterator +ProtoOutputStream::data() +{ + compact(); + return mBuffer.begin(); +} + +void +ProtoOutputStream::writeRawVarint(uint64_t varint) +{ + mBuffer.writeRawVarint64(varint); +} + +void +ProtoOutputStream::writeLengthDelimitedHeader(uint32_t id, size_t size) +{ + mBuffer.writeHeader(id, WIRE_TYPE_LENGTH_DELIMITED); + // reserves 64 bits for length delimited fields, if first field is negative, compact it. + mBuffer.writeRawFixed32(size); + mBuffer.writeRawFixed32(size); +} + +void +ProtoOutputStream::writeRawByte(uint8_t byte) +{ + mBuffer.writeRawByte(byte); +} + + +// ========================================================================= +// Private functions + +/** + * bit_cast + */ +template <class From, class To> +inline To bit_cast(From const &from) { + To to; + memcpy(&to, &from, sizeof(to)); + return to; +} + +inline void +ProtoOutputStream::writeDoubleImpl(uint32_t id, double val) +{ + mBuffer.writeHeader(id, WIRE_TYPE_FIXED64); + mBuffer.writeRawFixed64(bit_cast<double, uint64_t>(val)); +} + +inline void +ProtoOutputStream::writeFloatImpl(uint32_t id, float val) +{ + mBuffer.writeHeader(id, WIRE_TYPE_FIXED32); + mBuffer.writeRawFixed32(bit_cast<float, uint32_t>(val)); +} + +inline void +ProtoOutputStream::writeInt64Impl(uint32_t id, long long val) +{ + mBuffer.writeHeader(id, WIRE_TYPE_VARINT); + mBuffer.writeRawVarint64((uint64_t)val); +} + +inline void +ProtoOutputStream::writeInt32Impl(uint32_t id, int val) +{ + mBuffer.writeHeader(id, WIRE_TYPE_VARINT); + mBuffer.writeRawVarint32((uint32_t)val); +} + +inline void +ProtoOutputStream::writeUint64Impl(uint32_t id, uint64_t val) +{ + mBuffer.writeHeader(id, WIRE_TYPE_VARINT); + mBuffer.writeRawVarint64(val); +} + +inline void +ProtoOutputStream::writeUint32Impl(uint32_t id, uint32_t val) +{ + mBuffer.writeHeader(id, WIRE_TYPE_VARINT); + mBuffer.writeRawVarint32(val); +} + +inline void +ProtoOutputStream::writeFixed64Impl(uint32_t id, uint64_t val) +{ + mBuffer.writeHeader(id, WIRE_TYPE_FIXED64); + mBuffer.writeRawFixed64(val); +} + +inline void +ProtoOutputStream::writeFixed32Impl(uint32_t id, uint32_t val) +{ + mBuffer.writeHeader(id, WIRE_TYPE_FIXED32); + mBuffer.writeRawFixed32(val); +} + +inline void +ProtoOutputStream::writeSFixed64Impl(uint32_t id, long long val) +{ + mBuffer.writeHeader(id, WIRE_TYPE_FIXED64); + mBuffer.writeRawFixed64((uint64_t)val); +} + +inline void +ProtoOutputStream::writeSFixed32Impl(uint32_t id, int val) +{ + mBuffer.writeHeader(id, WIRE_TYPE_FIXED32); + mBuffer.writeRawFixed32((uint32_t)val); +} + +inline void +ProtoOutputStream::writeZigzagInt64Impl(uint32_t id, long long val) +{ + mBuffer.writeHeader(id, WIRE_TYPE_VARINT); + mBuffer.writeRawVarint64((val << 1) ^ (val >> 63)); +} + +inline void +ProtoOutputStream::writeZigzagInt32Impl(uint32_t id, int val) +{ + mBuffer.writeHeader(id, WIRE_TYPE_VARINT); + mBuffer.writeRawVarint32((val << 1) ^ (val >> 31)); +} + +inline void +ProtoOutputStream::writeEnumImpl(uint32_t id, int val) +{ + mBuffer.writeHeader(id, WIRE_TYPE_VARINT); + mBuffer.writeRawVarint32((uint32_t) val); +} + +inline void +ProtoOutputStream::writeBoolImpl(uint32_t id, bool val) +{ + mBuffer.writeHeader(id, WIRE_TYPE_VARINT); + mBuffer.writeRawVarint32(val ? 1 : 0); +} + +inline void +ProtoOutputStream::writeUtf8StringImpl(uint32_t id, const char* val, size_t size) +{ + if (val == NULL) return; + writeLengthDelimitedHeader(id, size); + for (size_t i=0; i<size; i++) { + mBuffer.writeRawByte((uint8_t)val[i]); + } +} + +inline void +ProtoOutputStream::writeMessageBytesImpl(uint32_t id, const char* val, size_t size) +{ + if (val == NULL) return; + writeLengthDelimitedHeader(id, size); + for (size_t i=0; i<size; i++) { + mBuffer.writeRawByte(val[i]); + } +} + +} // util +} // android + diff --git a/libs/protoutil/src/protobuf.cpp b/libs/protoutil/src/protobuf.cpp new file mode 100644 index 000000000000..1c7eef922895 --- /dev/null +++ b/libs/protoutil/src/protobuf.cpp @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <android/util/protobuf.h> + +namespace android { +namespace util { + +uint8_t +read_wire_type(uint32_t varint) +{ + return (uint8_t) (varint & WIRE_TYPE_MASK); +} + +uint32_t +read_field_id(uint32_t varint) +{ + return varint >> FIELD_ID_SHIFT; +} + +size_t +get_varint_size(uint64_t varint) +{ + size_t size = 1; + while ((varint & ~0x7F)) { + size++; + varint >>= 7; + } + return size; +} + +uint8_t* +write_raw_varint(uint8_t* buf, uint64_t val) +{ + uint8_t* p = buf; + while (true) { + if ((val & ~0x7F) == 0) { + *p++ = (uint8_t)val; + return p; + } else { + *p++ = (uint8_t)((val & 0x7F) | 0x80); + val >>= 7; + } + } +} + +uint8_t* +write_length_delimited_tag_header(uint8_t* buf, uint32_t fieldId, size_t size) +{ + buf = write_raw_varint(buf, (fieldId << FIELD_ID_SHIFT) | WIRE_TYPE_LENGTH_DELIMITED); + buf = write_raw_varint(buf, size); + return buf; +} + +} // util +} // android diff --git a/libs/services/Android.bp b/libs/services/Android.bp index 53e620138079..3d57fbdd0dcd 100644 --- a/libs/services/Android.bp +++ b/libs/services/Android.bp @@ -19,6 +19,8 @@ cc_library_shared { srcs: [ ":IDropBoxManagerService.aidl", "src/os/DropBoxManager.cpp", + "src/os/StatsDimensionsValue.cpp", + "src/os/StatsLogEventWrapper.cpp", ], shared_libs: [ @@ -28,7 +30,7 @@ cc_library_shared { "libutils", ], header_libs: [ - "libbase_headers", + "libbase_headers", ], aidl: { include_dirs: ["frameworks/base/core/java/"], diff --git a/libs/services/include/android/os/DropBoxManager.h b/libs/services/include/android/os/DropBoxManager.h index 8717178bb7d6..75b26c626d14 100644 --- a/libs/services/include/android/os/DropBoxManager.h +++ b/libs/services/include/android/os/DropBoxManager.h @@ -57,7 +57,11 @@ public: // and a handle will be passed to the system process, so no additional permissions // are required from the system process. Returns NULL if the file can't be opened. Status addFile(const String16& tag, const string& filename, int flags); - + + // Create a new Entry from an already opened file. Takes ownership of the + // file descriptor. + Status addFile(const String16& tag, int fd, int flags); + class Entry : public virtual RefBase, public Parcelable { public: Entry(); @@ -65,7 +69,12 @@ public: virtual status_t writeToParcel(Parcel* out) const; virtual status_t readFromParcel(const Parcel* in); - + + const vector<uint8_t>& getData() const; + const unique_fd& getFd() const; + int32_t getFlags() const; + int64_t getTimestamp() const; + private: Entry(const String16& tag, int32_t flags); Entry(const String16& tag, int32_t flags, int fd); @@ -80,6 +89,9 @@ public: friend class DropBoxManager; }; + // Get the next entry from the drop box after the specified time. + Status getNextEntry(const String16& tag, long msec, Entry* entry); + private: enum { HAS_BYTE_ARRAY = 8 diff --git a/libs/services/include/android/os/StatsDimensionsValue.h b/libs/services/include/android/os/StatsDimensionsValue.h new file mode 100644 index 000000000000..cc0b05644f2c --- /dev/null +++ b/libs/services/include/android/os/StatsDimensionsValue.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef STATS_DIMENSIONS_VALUE_H +#define STATS_DIMENSIONS_VALUE_H + +#include <binder/Parcel.h> +#include <binder/Parcelable.h> +#include <binder/Status.h> +#include <utils/String16.h> +#include <vector> + +namespace android { +namespace os { + +// Represents a parcelable object. Used to send data from statsd to StatsCompanionService.java. +class StatsDimensionsValue : public android::Parcelable { +public: + StatsDimensionsValue(); + + StatsDimensionsValue(int32_t field, String16 value); + StatsDimensionsValue(int32_t field, int32_t value); + StatsDimensionsValue(int32_t field, int64_t value); + StatsDimensionsValue(int32_t field, bool value); + StatsDimensionsValue(int32_t field, float value); + StatsDimensionsValue(int32_t field, std::vector<StatsDimensionsValue> value); + + virtual ~StatsDimensionsValue(); + + virtual android::status_t writeToParcel(android::Parcel* out) const override; + virtual android::status_t readFromParcel(const android::Parcel* in) override; + +private: + // Keep constants in sync with android/os/StatsDimensionsValue.java + // and stats_log.proto's DimensionValue. + static const int kStrValueType = 2; + static const int kIntValueType = 3; + static const int kLongValueType = 4; + static const int kBoolValueType = 5; + static const int kFloatValueType = 6; + static const int kTupleValueType = 7; + + int32_t mField; + int32_t mValueType; + + // This isn't very clever, but it isn't used for long-term storage, so it'll do. + String16 mStrValue; + int32_t mIntValue; + int64_t mLongValue; + bool mBoolValue; + float mFloatValue; + std::vector<StatsDimensionsValue> mTupleValue; +}; + +} // namespace os +} // namespace android + +#endif // STATS_DIMENSIONS_VALUE_H diff --git a/libs/services/include/android/os/StatsLogEventWrapper.h b/libs/services/include/android/os/StatsLogEventWrapper.h new file mode 100644 index 000000000000..255619c6226c --- /dev/null +++ b/libs/services/include/android/os/StatsLogEventWrapper.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef STATS_LOG_EVENT_WRAPPER_H +#define STATS_LOG_EVENT_WRAPPER_H + +#include <binder/Parcel.h> +#include <binder/Parcelable.h> +#include <binder/Status.h> +#include <utils/RefBase.h> +#include <vector> + +namespace android { +namespace os { + +// Represents a parcelable object. Only used to send data from Android OS to statsd. +class StatsLogEventWrapper : public android::Parcelable { + public: + StatsLogEventWrapper(); + + StatsLogEventWrapper(StatsLogEventWrapper&& in) = default; + + android::status_t writeToParcel(android::Parcel* out) const; + + android::status_t readFromParcel(const android::Parcel* in); + + // These are public for ease of conversion. + std::vector<uint8_t> bytes; +}; +} // Namespace os +} // Namespace android + + +#endif // STATS_LOG_EVENT_WRAPPER_H + diff --git a/libs/services/src/os/DropBoxManager.cpp b/libs/services/src/os/DropBoxManager.cpp index bbb45f022a87..f5685d9ca753 100644 --- a/libs/services/src/os/DropBoxManager.cpp +++ b/libs/services/src/os/DropBoxManager.cpp @@ -143,6 +143,29 @@ DropBoxManager::Entry::readFromParcel(const Parcel* in) return NO_ERROR; } +const vector<uint8_t>& +DropBoxManager::Entry::getData() const +{ + return mData; +} + +const unique_fd& +DropBoxManager::Entry::getFd() const +{ + return mFd; +} + +int32_t +DropBoxManager::Entry::getFlags() const +{ + return mFlags; +} + +int64_t +DropBoxManager::Entry::getTimestamp() const +{ + return mTimeMillis; +} DropBoxManager::DropBoxManager() { @@ -179,7 +202,12 @@ DropBoxManager::addFile(const String16& tag, const string& filename, int flags) ALOGW("DropboxManager: %s", message.c_str()); return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE, message.c_str()); } + return addFile(tag, fd, flags); +} +Status +DropBoxManager::addFile(const String16& tag, int fd, int flags) +{ Entry entry(tag, flags, fd); return add(entry); } @@ -195,5 +223,16 @@ DropBoxManager::add(const Entry& entry) return service->add(entry); } +Status +DropBoxManager::getNextEntry(const String16& tag, long msec, Entry* entry) +{ + sp<IDropBoxManagerService> service = interface_cast<IDropBoxManagerService>( + defaultServiceManager()->getService(android::String16("dropbox"))); + if (service == NULL) { + return Status::fromExceptionCode(Status::EX_NULL_POINTER, "can't find dropbox service"); + } + return service->getNextEntry(tag, msec, entry); +} + }} // namespace android::os diff --git a/libs/services/src/os/StatsDimensionsValue.cpp b/libs/services/src/os/StatsDimensionsValue.cpp new file mode 100644 index 000000000000..0052e0baa905 --- /dev/null +++ b/libs/services/src/os/StatsDimensionsValue.cpp @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "StatsDimensionsValue" + +#include "android/os/StatsDimensionsValue.h" + +#include <cutils/log.h> + +using android::Parcel; +using android::Parcelable; +using android::status_t; +using std::vector; + +namespace android { +namespace os { + +StatsDimensionsValue::StatsDimensionsValue() {}; + +StatsDimensionsValue::StatsDimensionsValue(int32_t field, String16 value) : + mField(field), + mValueType(kStrValueType), + mStrValue(value) { +} +StatsDimensionsValue::StatsDimensionsValue(int32_t field, int32_t value) : + mField(field), + mValueType(kIntValueType), + mIntValue(value) { +} +StatsDimensionsValue::StatsDimensionsValue(int32_t field, int64_t value) : + mField(field), + mValueType(kLongValueType), + mLongValue(value) { +} +StatsDimensionsValue::StatsDimensionsValue(int32_t field, bool value) : + mField(field), + mValueType(kBoolValueType), + mBoolValue(value) { +} +StatsDimensionsValue::StatsDimensionsValue(int32_t field, float value) : + mField(field), + mValueType(kFloatValueType), + mFloatValue(value) { +} +StatsDimensionsValue::StatsDimensionsValue(int32_t field, vector<StatsDimensionsValue> value) : + mField(field), + mValueType(kTupleValueType), + mTupleValue(value) { +} + +StatsDimensionsValue::~StatsDimensionsValue() {} + +status_t +StatsDimensionsValue::writeToParcel(Parcel* out) const { + status_t err ; + + err = out->writeInt32(mField); + if (err != NO_ERROR) { + return err; + } + err = out->writeInt32(mValueType); + if (err != NO_ERROR) { + return err; + } + switch (mValueType) { + case kStrValueType: + err = out->writeString16(mStrValue); + break; + case kIntValueType: + err = out->writeInt32(mIntValue); + break; + case kLongValueType: + err = out->writeInt64(mLongValue); + break; + case kBoolValueType: + err = out->writeBool(mBoolValue); + break; + case kFloatValueType: + err = out->writeFloat(mFloatValue); + break; + case kTupleValueType: + { + int sz = mTupleValue.size(); + err = out->writeInt32(sz); + if (err != NO_ERROR) { + return err; + } + for (int i = 0; i < sz; ++i) { + err = mTupleValue[i].writeToParcel(out); + if (err != NO_ERROR) { + return err; + } + } + } + break; + default: + err = UNKNOWN_ERROR; + break; + } + return err; +} + +status_t +StatsDimensionsValue::readFromParcel(const Parcel* in) +{ + // Implement me if desired. We don't currently use this. + ALOGE("Cannot do c++ StatsDimensionsValue.readFromParcel(); it is not implemented."); + (void)in; // To prevent compile error of unused parameter 'in' + return UNKNOWN_ERROR; +} + +} // namespace os +} // namespace android diff --git a/libs/services/src/os/StatsLogEventWrapper.cpp b/libs/services/src/os/StatsLogEventWrapper.cpp new file mode 100644 index 000000000000..8b3aa9ab4257 --- /dev/null +++ b/libs/services/src/os/StatsLogEventWrapper.cpp @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include <android/os/StatsLogEventWrapper.h> + +#include <binder/Parcel.h> +#include <binder/Parcelable.h> +#include <binder/Status.h> +#include <utils/RefBase.h> +#include <vector> + +using android::Parcel; +using android::Parcelable; +using android::status_t; +using std::vector; + +namespace android { +namespace os { + +StatsLogEventWrapper::StatsLogEventWrapper(){}; + +status_t StatsLogEventWrapper::writeToParcel(Parcel* out) const { + out->writeByteVector(bytes); + return ::android::NO_ERROR; +}; + +status_t StatsLogEventWrapper::readFromParcel(const Parcel* in) { + in->readByteVector(&bytes); + return ::android::NO_ERROR; +}; + +} // Namespace os +} // Namespace android |