diff options
Diffstat (limited to 'libs')
380 files changed, 25126 insertions, 17765 deletions
diff --git a/libs/androidfw/.clang-format b/libs/androidfw/.clang-format new file mode 100644 index 000000000000..ee1bee2bc644 --- /dev/null +++ b/libs/androidfw/.clang-format @@ -0,0 +1,2 @@ +BasedOnStyle: Google +ColumnLimit: 100 diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp index 5d12d9507c22..fb898355db92 100644 --- a/libs/androidfw/Android.bp +++ b/libs/androidfw/Android.bp @@ -24,9 +24,14 @@ cc_library { "-Wunreachable-code", ], srcs: [ + "ApkAssets.cpp", "Asset.cpp", "AssetDir.cpp", "AssetManager.cpp", + "AssetManager2.cpp", + "AttributeResolution.cpp", + "ChunkIterator.cpp", + "LoadedArsc.cpp", "LocaleData.cpp", "misc.cpp", "ObbFile.cpp", @@ -64,7 +69,16 @@ cc_library { shared: { enabled: false, }, - shared_libs: ["libz-host"], + static_libs: [ + "libziparchive", + "libbase", + "liblog", + "libcutils", + "libutils", + ], + shared_libs: [ + "libz-host", + ], }, windows: { enabled: true, diff --git a/libs/androidfw/Android.mk b/libs/androidfw/Android.mk new file mode 100644 index 000000000000..68c51effd79d --- /dev/null +++ b/libs/androidfw/Android.mk @@ -0,0 +1,24 @@ +# 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 new file mode 100644 index 000000000000..55f4c3ce6e76 --- /dev/null +++ b/libs/androidfw/ApkAssets.cpp @@ -0,0 +1,104 @@ +/* + * 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. + */ + +#define ATRACE_TAG ATRACE_TAG_RESOURCES + +#include "androidfw/ApkAssets.h" + +#include "android-base/logging.h" +#include "utils/Trace.h" +#include "ziparchive/zip_archive.h" + +#include "androidfw/Asset.h" +#include "androidfw/Util.h" + +namespace android { + +std::unique_ptr<ApkAssets> ApkAssets::Load(const std::string& path) { + ATRACE_NAME("ApkAssets::Load"); + ::ZipArchiveHandle unmanaged_handle; + int32_t result = ::OpenArchive(path.c_str(), &unmanaged_handle); + if (result != 0) { + LOG(ERROR) << ::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); + + ::ZipString entry_name("resources.arsc"); + ::ZipEntry entry; + result = ::FindEntry(loaded_apk->zip_handle_.get(), entry_name, &entry); + if (result != 0) { + LOG(ERROR) << ::ErrorCodeString(result); + return {}; + } + + if (entry.method == kCompressDeflated) { + LOG(WARNING) << "resources.arsc is compressed."; + } + + loaded_apk->resources_asset_ = + loaded_apk->Open("resources.arsc", Asset::AccessMode::ACCESS_BUFFER); + if (loaded_apk->resources_asset_ == nullptr) { + return {}; + } + + loaded_apk->path_ = path; + loaded_apk->loaded_arsc_ = + LoadedArsc::Load(loaded_apk->resources_asset_->getBuffer(true /*wordAligned*/), + loaded_apk->resources_asset_->getLength()); + if (loaded_apk->loaded_arsc_ == nullptr) { + return {}; + } + return loaded_apk; +} + +std::unique_ptr<Asset> ApkAssets::Open(const std::string& path, Asset::AccessMode /*mode*/) const { + ATRACE_NAME("ApkAssets::Open"); + CHECK(zip_handle_ != nullptr); + + ::ZipString name(path.c_str()); + ::ZipEntry entry; + int32_t result = ::FindEntry(zip_handle_.get(), name, &entry); + if (result != 0) { + LOG(ERROR) << "No entry '" << path << "' found in APK."; + return {}; + } + + if (entry.method == kCompressDeflated) { + auto compressed_asset = util::make_unique<_CompressedAsset>(); + if (compressed_asset->openChunk(::GetFileDescriptor(zip_handle_.get()), entry.offset, + entry.method, entry.uncompressed_length, + entry.compressed_length) != NO_ERROR) { + LOG(ERROR) << "Failed to decompress '" << path << "'."; + return {}; + } + return std::move(compressed_asset); + } else { + auto uncompressed_asset = util::make_unique<_FileAsset>(); + if (uncompressed_asset->openChunk(path.c_str(), ::GetFileDescriptor(zip_handle_.get()), + entry.offset, entry.uncompressed_length) != NO_ERROR) { + LOG(ERROR) << "Failed to mmap '" << path << "'."; + return {}; + } + return std::move(uncompressed_asset); + } + return {}; +} + +} // namespace android diff --git a/libs/androidfw/AssetManager.cpp b/libs/androidfw/AssetManager.cpp index 80968e51d48c..e0689006d5dd 100644 --- a/libs/androidfw/AssetManager.cpp +++ b/libs/androidfw/AssetManager.cpp @@ -59,12 +59,6 @@ using namespace android; static const bool kIsDebug = false; -/* - * Names for default app, locale, and vendor. We might want to change - * these to be an actual locale, e.g. always use en-US as the default. - */ -static const char* kDefaultLocale = "default"; -static const char* kDefaultVendor = "default"; static const char* kAssetsRoot = "assets"; static const char* kAppZipName = NULL; //"classes.jar"; static const char* kSystemAssets = "framework/framework-res.apk"; @@ -80,73 +74,70 @@ const char* AssetManager::RESOURCES_FILENAME = "resources.arsc"; const char* AssetManager::IDMAP_BIN = "/system/bin/idmap"; const char* AssetManager::OVERLAY_DIR = "/vendor/overlay"; const char* AssetManager::OVERLAY_THEME_DIR_PROPERTY = "ro.boot.vendor.overlay.theme"; +const char* AssetManager::OVERLAY_THEME_DIR_PERSIST_PROPERTY = "persist.vendor.overlay.theme"; const char* AssetManager::TARGET_PACKAGE_NAME = "android"; const char* AssetManager::TARGET_APK_PATH = "/system/framework/framework-res.apk"; const char* AssetManager::IDMAP_DIR = "/data/resource-cache"; namespace { - String8 idmapPathForPackagePath(const String8& pkgPath) - { - const char* root = getenv("ANDROID_DATA"); - LOG_ALWAYS_FATAL_IF(root == NULL, "ANDROID_DATA not set"); - String8 path(root); - path.appendPath(kResourceCache); - - char buf[256]; // 256 chars should be enough for anyone... - strncpy(buf, pkgPath.string(), 255); - buf[255] = '\0'; - char* filename = buf; - while (*filename && *filename == '/') { - ++filename; - } - char* p = filename; - while (*p) { - if (*p == '/') { - *p = '@'; - } - ++p; - } - path.appendPath(filename); - path.append("@idmap"); - return path; +String8 idmapPathForPackagePath(const String8& pkgPath) { + const char* root = getenv("ANDROID_DATA"); + LOG_ALWAYS_FATAL_IF(root == NULL, "ANDROID_DATA not set"); + String8 path(root); + path.appendPath(kResourceCache); + + char buf[256]; // 256 chars should be enough for anyone... + strncpy(buf, pkgPath.string(), 255); + buf[255] = '\0'; + char* filename = buf; + while (*filename && *filename == '/') { + ++filename; + } + char* p = filename; + while (*p) { + if (*p == '/') { + *p = '@'; + } + ++p; } + path.appendPath(filename); + path.append("@idmap"); - /* - * Like strdup(), but uses C++ "new" operator instead of malloc. - */ - static char* strdupNew(const char* str) - { - char* newStr; - int len; + return path; +} + +/* + * Like strdup(), but uses C++ "new" operator instead of malloc. + */ +static char* strdupNew(const char* str) { + char* newStr; + int len; - if (str == NULL) - return NULL; + if (str == NULL) + return NULL; - len = strlen(str); - newStr = new char[len+1]; - memcpy(newStr, str, len+1); + len = strlen(str); + newStr = new char[len+1]; + memcpy(newStr, str, len+1); - return newStr; - } + return newStr; } +} // namespace + /* * =========================================================================== * AssetManager * =========================================================================== */ -int32_t AssetManager::getGlobalCount() -{ +int32_t AssetManager::getGlobalCount() { return gCount; } -AssetManager::AssetManager(CacheMode cacheMode) - : mLocale(NULL), mVendor(NULL), - mResources(NULL), mConfig(new ResTable_config), - mCacheMode(cacheMode), mCacheValid(false) -{ +AssetManager::AssetManager() : + mLocale(NULL), mResources(NULL), mConfig(new ResTable_config) { int count = android_atomic_inc(&gCount) + 1; if (kIsDebug) { ALOGI("Creating AssetManager %p #%d\n", this, count); @@ -154,8 +145,7 @@ AssetManager::AssetManager(CacheMode cacheMode) memset(mConfig, 0, sizeof(ResTable_config)); } -AssetManager::~AssetManager(void) -{ +AssetManager::~AssetManager() { int count = android_atomic_dec(&gCount); if (kIsDebug) { ALOGI("Destroying AssetManager in %p #%d\n", this, count); @@ -166,12 +156,10 @@ AssetManager::~AssetManager(void) // don't have a String class yet, so make sure we clean up delete[] mLocale; - delete[] mVendor; } bool AssetManager::addAssetPath( - const String8& path, int32_t* cookie, bool appAsLib, bool isSystemAsset) -{ + const String8& path, int32_t* cookie, bool appAsLib, bool isSystemAsset) { AutoMutex _l(mLock); asset_path ap; @@ -346,97 +334,16 @@ String8 AssetManager::getAssetPath(const int32_t cookie) const return String8(); } -/* - * Set the current locale. Use NULL to indicate no locale. - * - * Close and reopen Zip archives as appropriate, and reset cached - * information in the locale-specific sections of the tree. - */ -void AssetManager::setLocale(const char* locale) -{ - AutoMutex _l(mLock); - setLocaleLocked(locale); -} - - -static const char kFilPrefix[] = "fil"; -static const char kTlPrefix[] = "tl"; - -// The sizes of the prefixes, excluding the 0 suffix. -// char. -static const int kFilPrefixLen = sizeof(kFilPrefix) - 1; -static const int kTlPrefixLen = sizeof(kTlPrefix) - 1; - void AssetManager::setLocaleLocked(const char* locale) { if (mLocale != NULL) { - /* previously set, purge cached data */ - purgeFileNameCacheLocked(); - //mZipSet.purgeLocale(); delete[] mLocale; } - // If we're attempting to set a locale that starts with "fil", - // we should convert it to "tl" for backwards compatibility since - // we've been using "tl" instead of "fil" prior to L. - // - // If the resource table already has entries for "fil", we use that - // instead of attempting a fallback. - if (strncmp(locale, kFilPrefix, kFilPrefixLen) == 0) { - Vector<String8> locales; - ResTable* res = mResources; - if (res != NULL) { - res->getLocales(&locales); - } - const size_t localesSize = locales.size(); - bool hasFil = false; - for (size_t i = 0; i < localesSize; ++i) { - if (locales[i].find(kFilPrefix) == 0) { - hasFil = true; - break; - } - } - - - if (!hasFil) { - const size_t newLocaleLen = strlen(locale); - // This isn't a bug. We really do want mLocale to be 1 byte - // shorter than locale, because we're replacing "fil-" with - // "tl-". - mLocale = new char[newLocaleLen]; - // Copy over "tl". - memcpy(mLocale, kTlPrefix, kTlPrefixLen); - // Copy the rest of |locale|, including the terminating '\0'. - memcpy(mLocale + kTlPrefixLen, locale + kFilPrefixLen, - newLocaleLen - kFilPrefixLen + 1); - updateResourceParamsLocked(); - return; - } - } - mLocale = strdupNew(locale); updateResourceParamsLocked(); } -/* - * Set the current vendor. Use NULL to indicate no vendor. - * - * Close and reopen Zip archives as appropriate, and reset cached - * information in the vendor-specific sections of the tree. - */ -void AssetManager::setVendor(const char* vendor) -{ - AutoMutex _l(mLock); - - if (mVendor != NULL) { - /* previously set, purge cached data */ - purgeFileNameCacheLocked(); - //mZipSet.purgeVendor(); - delete[] mVendor; - } - mVendor = strdupNew(vendor); -} - void AssetManager::setConfiguration(const ResTable_config& config, const char* locale) { AutoMutex _l(mLock); @@ -461,23 +368,11 @@ void AssetManager::getConfiguration(ResTable_config* outConfig) const /* * Open an asset. * - * The data could be; - * - In a file on disk (assetBase + fileName). - * - In a compressed file on disk (assetBase + fileName.gz). - * - In a Zip archive, uncompressed or compressed. + * The data could be in any asset path. Each asset path could be: + * - A directory on disk. + * - A Zip archive, uncompressed or compressed. * - * It can be in a number of different directories and Zip archives. - * The search order is: - * - [appname] - * - locale + vendor - * - "default" + vendor - * - locale + "default" - * - "default + "default" - * - "common" - * - (same as above) - * - * To find a particular file, we have to try up to eight paths with - * all three forms of data. + * If the file is in a directory, it could have a .gz suffix, meaning it is compressed. * * We should probably reject requests for "illegal" filenames, e.g. those * with illegal characters or "../" backward relative paths. @@ -488,10 +383,6 @@ Asset* AssetManager::open(const char* fileName, AccessMode mode) LOG_FATAL_IF(mAssetPaths.size() == 0, "No assets added to AssetManager"); - - if (mCacheMode != CACHE_OFF && !mCacheValid) - loadFileNameCacheLocked(); - String8 assetName(kAssetsRoot); assetName.appendPath(fileName); @@ -516,8 +407,7 @@ Asset* AssetManager::open(const char* fileName, AccessMode mode) /* * Open a non-asset file as if it were an asset. * - * The "fileName" is the partial path starting from the application - * name. + * The "fileName" is the partial path starting from the application name. */ Asset* AssetManager::openNonAsset(const char* fileName, AccessMode mode, int32_t* outCookie) { @@ -525,10 +415,6 @@ Asset* AssetManager::openNonAsset(const char* fileName, AccessMode mode, int32_t LOG_FATAL_IF(mAssetPaths.size() == 0, "No assets added to AssetManager"); - - if (mCacheMode != CACHE_OFF && !mCacheValid) - loadFileNameCacheLocked(); - /* * For each top-level asset path, search for the asset. */ @@ -556,9 +442,6 @@ Asset* AssetManager::openNonAsset(const int32_t cookie, const char* fileName, Ac LOG_FATAL_IF(mAssetPaths.size() == 0, "No assets added to AssetManager"); - if (mCacheMode != CACHE_OFF && !mCacheValid) - loadFileNameCacheLocked(); - if (which < mAssetPaths.size()) { ALOGV("Looking for non-asset '%s' in '%s'\n", fileName, mAssetPaths.itemAt(which).path.string()); @@ -590,10 +473,11 @@ FileType AssetManager::getFileType(const char* fileName) pAsset = open(fileName, Asset::ACCESS_STREAMING); delete pAsset; - if (pAsset == NULL) + if (pAsset == NULL) { return kFileTypeNonexistent; - else + } else { return kFileTypeRegular; + } } bool AssetManager::appendPathToResTable(const asset_path& ap, bool appAsLib) const { @@ -708,10 +592,6 @@ const ResTable* AssetManager::getResTable(bool required) const LOG_FATAL_IF(mAssetPaths.size() == 0, "No assets added to AssetManager"); } - if (mCacheMode != CACHE_OFF && !mCacheValid) { - const_cast<AssetManager*>(this)->loadFileNameCacheLocked(); - } - mResources = new ResTable(); updateResourceParamsLocked(); @@ -831,17 +711,7 @@ void AssetManager::getLocales(Vector<String8>* locales, bool includeSystemLocale { ResTable* res = mResources; if (res != NULL) { - res->getLocales(locales, includeSystemLocales); - } - - const size_t numLocales = locales->size(); - for (size_t i = 0; i < numLocales; ++i) { - const String8& localeStr = locales->itemAt(i); - if (localeStr.find(kTlPrefix) == 0) { - String8 replaced("fil"); - replaced += (localeStr.string() + kTlPrefixLen); - locales->editItemAt(i) = replaced; - } + res->getLocales(locales, includeSystemLocales, true /* mergeEquivalentLangs */); } } @@ -903,158 +773,6 @@ Asset* AssetManager::openNonAssetInPathLocked(const char* fileName, AccessMode m } /* - * Open an asset, searching for it in the directory hierarchy for the - * specified app. - * - * Pass in a NULL values for "appName" if the common app directory should - * be used. - */ -Asset* AssetManager::openInPathLocked(const char* fileName, AccessMode mode, - const asset_path& ap) -{ - Asset* pAsset = NULL; - - /* - * Try various combinations of locale and vendor. - */ - if (mLocale != NULL && mVendor != NULL) - pAsset = openInLocaleVendorLocked(fileName, mode, ap, mLocale, mVendor); - if (pAsset == NULL && mVendor != NULL) - pAsset = openInLocaleVendorLocked(fileName, mode, ap, NULL, mVendor); - if (pAsset == NULL && mLocale != NULL) - pAsset = openInLocaleVendorLocked(fileName, mode, ap, mLocale, NULL); - if (pAsset == NULL) - pAsset = openInLocaleVendorLocked(fileName, mode, ap, NULL, NULL); - - return pAsset; -} - -/* - * Open an asset, searching for it in the directory hierarchy for the - * specified locale and vendor. - * - * We also search in "app.jar". - * - * Pass in NULL values for "appName", "locale", and "vendor" if the - * defaults should be used. - */ -Asset* AssetManager::openInLocaleVendorLocked(const char* fileName, AccessMode mode, - const asset_path& ap, const char* locale, const char* vendor) -{ - Asset* pAsset = NULL; - - if (ap.type == kFileTypeDirectory) { - if (mCacheMode == CACHE_OFF) { - /* look at the filesystem on disk */ - String8 path(createPathNameLocked(ap, locale, vendor)); - path.appendPath(fileName); - - String8 excludeName(path); - excludeName.append(kExcludeExtension); - if (::getFileType(excludeName.string()) != kFileTypeNonexistent) { - /* say no more */ - //printf("+++ excluding '%s'\n", (const char*) excludeName); - return kExcludedAsset; - } - - pAsset = openAssetFromFileLocked(path, mode); - - if (pAsset == NULL) { - /* try again, this time with ".gz" */ - path.append(".gz"); - pAsset = openAssetFromFileLocked(path, mode); - } - - if (pAsset != NULL) - pAsset->setAssetSource(path); - } else { - /* find in cache */ - String8 path(createPathNameLocked(ap, locale, vendor)); - path.appendPath(fileName); - - AssetDir::FileInfo tmpInfo; - bool found = false; - - String8 excludeName(path); - excludeName.append(kExcludeExtension); - - if (mCache.indexOf(excludeName) != NAME_NOT_FOUND) { - /* go no farther */ - //printf("+++ Excluding '%s'\n", (const char*) excludeName); - return kExcludedAsset; - } - - /* - * File compression extensions (".gz") don't get stored in the - * name cache, so we have to try both here. - */ - if (mCache.indexOf(path) != NAME_NOT_FOUND) { - found = true; - pAsset = openAssetFromFileLocked(path, mode); - if (pAsset == NULL) { - /* try again, this time with ".gz" */ - path.append(".gz"); - pAsset = openAssetFromFileLocked(path, mode); - } - } - - if (pAsset != NULL) - pAsset->setAssetSource(path); - - /* - * Don't continue the search into the Zip files. Our cached info - * said it was a file on disk; to be consistent with openDir() - * we want to return the loose asset. If the cached file gets - * removed, we fail. - * - * The alternative is to update our cache when files get deleted, - * or make some sort of "best effort" promise, but for now I'm - * taking the hard line. - */ - if (found) { - if (pAsset == NULL) - ALOGD("Expected file not found: '%s'\n", path.string()); - return pAsset; - } - } - } - - /* - * Either it wasn't found on disk or on the cached view of the disk. - * Dig through the currently-opened set of Zip files. If caching - * is disabled, the Zip file may get reopened. - */ - if (pAsset == NULL && ap.type == kFileTypeRegular) { - String8 path; - - path.appendPath((locale != NULL) ? locale : kDefaultLocale); - path.appendPath((vendor != NULL) ? vendor : kDefaultVendor); - path.appendPath(fileName); - - /* check the appropriate Zip file */ - ZipFileRO* pZip = getZipFileLocked(ap); - if (pZip != NULL) { - //printf("GOT zip, checking '%s'\n", (const char*) path); - ZipEntryRO entry = pZip->findEntryByName(path.string()); - if (entry != NULL) { - //printf("FOUND in Zip file for %s/%s-%s\n", - // appName, locale, vendor); - pAsset = openAssetFromZipLocked(pZip, entry, mode, path); - pZip->releaseEntry(entry); - } - } - - if (pAsset != NULL) { - /* create a "source" name, for debug/display */ - pAsset->setAssetSource(createZipSourceNameLocked(ZipSet::getPathName(ap.path.string()), - String8(""), String8(fileName))); - } - } - - return pAsset; -} - -/* * Create a "source name" for a file from a Zip archive. */ String8 AssetManager::createZipSourceNameLocked(const String8& zipFileName, @@ -1071,18 +789,6 @@ String8 AssetManager::createZipSourceNameLocked(const String8& zipFileName, } /* - * Create a path to a loose asset (asset-base/app/locale/vendor). - */ -String8 AssetManager::createPathNameLocked(const asset_path& ap, const char* locale, - const char* vendor) -{ - String8 path(ap.path); - path.appendPath((locale != NULL) ? locale : kDefaultLocale); - path.appendPath((vendor != NULL) ? vendor : kDefaultVendor); - return path; -} - -/* * Create a path to a loose asset (asset-base/app/rootDir). */ String8 AssetManager::createPathNameLocked(const asset_path& ap, const char* rootDir) @@ -1095,15 +801,6 @@ 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. - * - * Right now we have 2 possible Zip files (1 each in app/"common"). - * - * If caching is set to CACHE_OFF, to get the expected behavior we - * need to reopen the Zip file on every request. That would be silly - * and expensive, so instead we just check the file modification date. - * - * Pass in NULL values for "appName", "locale", and "vendor" if the - * generics should be used. */ ZipFileRO* AssetManager::getZipFileLocked(const asset_path& ap) { @@ -1188,14 +885,10 @@ Asset* AssetManager::openAssetFromZipLocked(const ZipFileRO* pZipFile, return pAsset; } - - /* * Open a directory in the asset namespace. * - * An "asset directory" is simply the combination of all files in all - * locations, with ".gz" stripped for loose files. With app, locale, and - * vendor defined, we have 8 directories and 2 Zip archives to scan. + * An "asset directory" is simply the combination of all asset paths' "assets/" directories. * * Pass in "" for the root dir. */ @@ -1211,9 +904,6 @@ AssetDir* AssetManager::openDir(const char* dirName) //printf("+++ openDir(%s) in '%s'\n", dirName, (const char*) mAssetBase); - if (mCacheMode != CACHE_OFF && !mCacheValid) - loadFileNameCacheLocked(); - pDir = new AssetDir; /* @@ -1256,9 +946,7 @@ AssetDir* AssetManager::openDir(const char* dirName) /* * Open a directory in the non-asset namespace. * - * An "asset directory" is simply the combination of all files in all - * locations, with ".gz" stripped for loose files. With app, locale, and - * vendor defined, we have 8 directories and 2 Zip archives to scan. + * An "asset directory" is simply the combination of all asset paths' "assets/" directories. * * Pass in "" for the root dir. */ @@ -1274,9 +962,6 @@ AssetDir* AssetManager::openNonAssetDir(const int32_t cookie, const char* dirNam //printf("+++ openDir(%s) in '%s'\n", dirName, (const char*) mAssetBase); - if (mCacheMode != CACHE_OFF && !mCacheValid) - loadFileNameCacheLocked(); - pDir = new AssetDir; pMergedInfo = new SortedVector<AssetDir::FileInfo>; @@ -1317,74 +1002,17 @@ AssetDir* AssetManager::openNonAssetDir(const int32_t cookie, const char* dirNam bool AssetManager::scanAndMergeDirLocked(SortedVector<AssetDir::FileInfo>* pMergedInfo, const asset_path& ap, const char* rootDir, const char* dirName) { - SortedVector<AssetDir::FileInfo>* pContents; - String8 path; - assert(pMergedInfo != NULL); - //printf("scanAndMergeDir: %s %s %s %s\n", appName, locale, vendor,dirName); + //printf("scanAndMergeDir: %s %s %s\n", ap.path.string(), rootDir, dirName); - if (mCacheValid) { - int i, start, count; + String8 path = createPathNameLocked(ap, rootDir); + if (dirName[0] != '\0') + path.appendPath(dirName); - pContents = new SortedVector<AssetDir::FileInfo>; - - /* - * Get the basic partial path and find it in the cache. That's - * the start point for the search. - */ - path = createPathNameLocked(ap, rootDir); - if (dirName[0] != '\0') - path.appendPath(dirName); - - start = mCache.indexOf(path); - if (start == NAME_NOT_FOUND) { - //printf("+++ not found in cache: dir '%s'\n", (const char*) path); - delete pContents; - return false; - } - - /* - * The match string looks like "common/default/default/foo/bar/". - * The '/' on the end ensures that we don't match on the directory - * itself or on ".../foo/barfy/". - */ - path.append("/"); - - count = mCache.size(); - - /* - * Pick out the stuff in the current dir by examining the pathname. - * It needs to match the partial pathname prefix, and not have a '/' - * (fssep) anywhere after the prefix. - */ - for (i = start+1; i < count; i++) { - if (mCache[i].getFileName().length() > path.length() && - strncmp(mCache[i].getFileName().string(), path.string(), path.length()) == 0) - { - const char* name = mCache[i].getFileName().string(); - // XXX THIS IS BROKEN! Looks like we need to store the full - // path prefix separately from the file path. - if (strchr(name + path.length(), '/') == NULL) { - /* grab it, reducing path to just the filename component */ - AssetDir::FileInfo tmp = mCache[i]; - tmp.setFileName(tmp.getFileName().getPathLeaf()); - pContents->add(tmp); - } - } else { - /* no longer in the dir or its subdirs */ - break; - } - - } - } else { - path = createPathNameLocked(ap, rootDir); - if (dirName[0] != '\0') - path.appendPath(dirName); - pContents = scanDirLocked(path); - if (pContents == NULL) - return false; - } + SortedVector<AssetDir::FileInfo>* pContents = scanDirLocked(path); + if (pContents == NULL) + return false; // if we wanted to do an incremental cache fill, we would do it here @@ -1711,153 +1339,6 @@ void AssetManager::mergeInfoLocked(SortedVector<AssetDir::FileInfo>* pMergedInfo #endif } - -/* - * Load all files into the file name cache. We want to do this across - * all combinations of { appname, locale, vendor }, performing a recursive - * directory traversal. - * - * This is not the most efficient data structure. Also, gathering the - * information as we needed it (file-by-file or directory-by-directory) - * would be faster. However, on the actual device, 99% of the files will - * live in Zip archives, so this list will be very small. The trouble - * is that we have to check the "loose" files first, so it's important - * that we don't beat the filesystem silly looking for files that aren't - * there. - * - * Note on thread safety: this is the only function that causes updates - * to mCache, and anybody who tries to use it will call here if !mCacheValid, - * so we need to employ a mutex here. - */ -void AssetManager::loadFileNameCacheLocked(void) -{ - assert(!mCacheValid); - assert(mCache.size() == 0); - -#ifdef DO_TIMINGS // need to link against -lrt for this now - DurationTimer timer; - timer.start(); -#endif - - fncScanLocked(&mCache, ""); - -#ifdef DO_TIMINGS - timer.stop(); - ALOGD("Cache scan took %.3fms\n", - timer.durationUsecs() / 1000.0); -#endif - -#if 0 - int i; - printf("CACHED FILE LIST (%d entries):\n", mCache.size()); - for (i = 0; i < (int) mCache.size(); i++) { - printf(" %d: (%d) '%s'\n", i, - mCache.itemAt(i).getFileType(), - (const char*) mCache.itemAt(i).getFileName()); - } -#endif - - mCacheValid = true; -} - -/* - * Scan up to 8 versions of the specified directory. - */ -void AssetManager::fncScanLocked(SortedVector<AssetDir::FileInfo>* pMergedInfo, - const char* dirName) -{ - size_t i = mAssetPaths.size(); - while (i > 0) { - i--; - const asset_path& ap = mAssetPaths.itemAt(i); - fncScanAndMergeDirLocked(pMergedInfo, ap, NULL, NULL, dirName); - if (mLocale != NULL) - fncScanAndMergeDirLocked(pMergedInfo, ap, mLocale, NULL, dirName); - if (mVendor != NULL) - fncScanAndMergeDirLocked(pMergedInfo, ap, NULL, mVendor, dirName); - if (mLocale != NULL && mVendor != NULL) - fncScanAndMergeDirLocked(pMergedInfo, ap, mLocale, mVendor, dirName); - } -} - -/* - * Recursively scan this directory and all subdirs. - * - * This is similar to scanAndMergeDir, but we don't remove the .EXCLUDE - * files, and we prepend the extended partial path to the filenames. - */ -bool AssetManager::fncScanAndMergeDirLocked( - SortedVector<AssetDir::FileInfo>* pMergedInfo, - const asset_path& ap, const char* locale, const char* vendor, - const char* dirName) -{ - SortedVector<AssetDir::FileInfo>* pContents; - String8 partialPath; - String8 fullPath; - - // XXX This is broken -- the filename cache needs to hold the base - // asset path separately from its filename. - - partialPath = createPathNameLocked(ap, locale, vendor); - if (dirName[0] != '\0') { - partialPath.appendPath(dirName); - } - - fullPath = partialPath; - pContents = scanDirLocked(fullPath); - if (pContents == NULL) { - return false; // directory did not exist - } - - /* - * Scan all subdirectories of the current dir, merging what we find - * into "pMergedInfo". - */ - for (int i = 0; i < (int) pContents->size(); i++) { - if (pContents->itemAt(i).getFileType() == kFileTypeDirectory) { - String8 subdir(dirName); - subdir.appendPath(pContents->itemAt(i).getFileName()); - - fncScanAndMergeDirLocked(pMergedInfo, ap, locale, vendor, subdir.string()); - } - } - - /* - * To be consistent, we want entries for the root directory. If - * we're the root, add one now. - */ - if (dirName[0] == '\0') { - AssetDir::FileInfo tmpInfo; - - tmpInfo.set(String8(""), kFileTypeDirectory); - tmpInfo.setSourceName(createPathNameLocked(ap, locale, vendor)); - pContents->add(tmpInfo); - } - - /* - * We want to prepend the extended partial path to every entry in - * "pContents". It's the same value for each entry, so this will - * not change the sorting order of the vector contents. - */ - for (int i = 0; i < (int) pContents->size(); i++) { - const AssetDir::FileInfo& info = pContents->itemAt(i); - pContents->editItemAt(i).setFileName(partialPath.appendPathCopy(info.getFileName())); - } - - mergeInfoLocked(pMergedInfo, pContents); - delete pContents; - return true; -} - -/* - * Trash the cache. - */ -void AssetManager::purgeFileNameCacheLocked(void) -{ - mCacheValid = false; - mCache.clear(); -} - /* * =========================================================================== * AssetManager::SharedZip @@ -1991,13 +1472,6 @@ AssetManager::SharedZip::~SharedZip() */ /* - * Constructor. - */ -AssetManager::ZipSet::ZipSet(void) -{ -} - -/* * Destructor. Close any open archives. */ AssetManager::ZipSet::~ZipSet(void) diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp new file mode 100644 index 000000000000..8d65925a218e --- /dev/null +++ b/libs/androidfw/AssetManager2.cpp @@ -0,0 +1,576 @@ +/* + * 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. + */ + +#define ATRACE_TAG ATRACE_TAG_RESOURCES + +#include "androidfw/AssetManager2.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 + +namespace android { + +AssetManager2::AssetManager2() { memset(&configuration_, 0, sizeof(configuration_)); } + +bool AssetManager2::SetApkAssets(const std::vector<const ApkAssets*>& apk_assets, + bool invalidate_caches) { + apk_assets_ = apk_assets; + if (invalidate_caches) { + InvalidateCaches(static_cast<uint32_t>(-1)); + } + return true; +} + +const std::vector<const ApkAssets*> AssetManager2::GetApkAssets() const { return apk_assets_; } + +const ResStringPool* AssetManager2::GetStringPoolForCookie(ApkAssetsCookie cookie) const { + if (cookie < 0 || static_cast<size_t>(cookie) >= apk_assets_.size()) { + return nullptr; + } + return apk_assets_[cookie]->GetLoadedArsc()->GetStringPool(); +} + +void AssetManager2::SetConfiguration(const ResTable_config& configuration) { + const int diff = configuration_.diff(configuration); + configuration_ = configuration; + + if (diff) { + InvalidateCaches(static_cast<uint32_t>(diff)); + } +} + +const ResTable_config& AssetManager2::GetConfiguration() const { return configuration_; } + +std::unique_ptr<Asset> AssetManager2::Open(const std::string& filename, Asset::AccessMode mode) { + 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) { + const std::string new_path = "assets/" + filename; + return OpenNonAsset(new_path, cookie, mode); +} + +// Search in reverse because that's how we used to do it and we need to preserve behaviour. +// This is unfortunate, because ClassLoaders delegate to the parent first, so the order +// is inconsistent for split APKs. +std::unique_ptr<Asset> AssetManager2::OpenNonAsset(const std::string& filename, + Asset::AccessMode mode, + ApkAssetsCookie* out_cookie) { + ATRACE_CALL(); + for (int32_t i = apk_assets_.size() - 1; i >= 0; i--) { + std::unique_ptr<Asset> asset = apk_assets_[i]->Open(filename, mode); + if (asset) { + if (out_cookie != nullptr) { + *out_cookie = i; + } + return asset; + } + } + + if (out_cookie != nullptr) { + *out_cookie = kInvalidCookie; + } + return {}; +} + +std::unique_ptr<Asset> AssetManager2::OpenNonAsset(const std::string& filename, + ApkAssetsCookie cookie, Asset::AccessMode mode) { + ATRACE_CALL(); + if (cookie < 0 || static_cast<size_t>(cookie) >= apk_assets_.size()) { + return {}; + } + return apk_assets_[cookie]->Open(filename, mode); +} + +ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_override, + bool stop_at_first_match, LoadedArsc::Entry* out_entry, + ResTable_config* out_selected_config, + uint32_t* out_flags) { + ATRACE_CALL(); + + // 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_; + if (density_override != 0 && density_override != configuration_.density) { + density_override_config = configuration_; + density_override_config.density = density_override; + desired_config = &density_override_config; + } + + LoadedArsc::Entry best_entry; + ResTable_config best_config; + int32_t best_index = -1; + uint32_t cumulated_flags = 0; + + const size_t apk_asset_count = apk_assets_.size(); + for (size_t i = 0; i < apk_asset_count; i++) { + const LoadedArsc* loaded_arsc = apk_assets_[i]->GetLoadedArsc(); + + LoadedArsc::Entry current_entry; + ResTable_config current_config; + uint32_t flags = 0; + if (!loaded_arsc->FindEntry(resid, *desired_config, ¤t_entry, ¤t_config, &flags)) { + continue; + } + + cumulated_flags |= flags; + + if (best_index == -1 || current_config.isBetterThan(best_config, desired_config)) { + best_entry = current_entry; + best_config = current_config; + best_index = static_cast<int32_t>(i); + if (stop_at_first_match) { + break; + } + } + } + + if (best_index == -1) { + return kInvalidCookie; + } + + *out_entry = best_entry; + *out_selected_config = best_config; + *out_flags = cumulated_flags; + return best_index; +} + +bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) { + ATRACE_CALL(); + + LoadedArsc::Entry entry; + ResTable_config config; + uint32_t flags = 0u; + ApkAssetsCookie cookie = FindEntry(resid, 0u /* density_override */, + true /* stop_at_first_match */, &entry, &config, &flags); + if (cookie == kInvalidCookie) { + return false; + } + + const std::string* package_name = + apk_assets_[cookie]->GetLoadedArsc()->GetPackageNameForId(resid); + if (package_name == nullptr) { + return false; + } + + out_name->package = package_name->data(); + out_name->package_len = package_name->size(); + + out_name->type = entry.type_string_ref.string8(&out_name->type_len); + out_name->type16 = nullptr; + if (out_name->type == nullptr) { + out_name->type16 = entry.type_string_ref.string16(&out_name->type_len); + if (out_name->type16 == nullptr) { + return false; + } + } + + out_name->entry = entry.entry_string_ref.string8(&out_name->entry_len); + out_name->entry16 = nullptr; + if (out_name->entry == nullptr) { + out_name->entry16 = entry.entry_string_ref.string16(&out_name->entry_len); + if (out_name->entry16 == nullptr) { + return false; + } + } + return true; +} + +bool AssetManager2::GetResourceFlags(uint32_t resid, uint32_t* out_flags) { + LoadedArsc::Entry entry; + ResTable_config config; + ApkAssetsCookie cookie = FindEntry(resid, 0u /* density_override */, + false /* stop_at_first_match */, &entry, &config, out_flags); + return cookie != 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) { + ATRACE_CALL(); + + LoadedArsc::Entry entry; + ResTable_config config; + uint32_t flags = 0u; + ApkAssetsCookie cookie = + FindEntry(resid, density_override, false /* stop_at_first_match */, &entry, &config, &flags); + if (cookie == kInvalidCookie) { + return kInvalidCookie; + } + + if (dtohl(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; + } + + const Res_value* device_value = reinterpret_cast<const Res_value*>( + reinterpret_cast<const uint8_t*>(entry.entry) + dtohs(entry.entry->size)); + out_value->copyFrom_dtoh(*device_value); + *out_selected_config = config; + *out_flags = flags; + return cookie; +} + +const ResolvedBag* AssetManager2::GetBag(uint32_t resid) { + ATRACE_CALL(); + + auto cached_iter = cached_bags_.find(resid); + if (cached_iter != cached_bags_.end()) { + return cached_iter->second.get(); + } + + LoadedArsc::Entry entry; + ResTable_config config; + uint32_t flags = 0u; + ApkAssetsCookie cookie = FindEntry(resid, 0u /* density_override */, + false /* stop_at_first_match */, &entry, &config, &flags); + if (cookie == kInvalidCookie) { + return nullptr; + } + + // Check that the size of the entry header is at least as big as + // the desired ResTable_map_entry. Also verify that the entry + // was intended to be a map. + if (dtohs(entry.entry->size) < sizeof(ResTable_map_entry) || + (dtohs(entry.entry->flags) & ResTable_entry::FLAG_COMPLEX) == 0) { + // Not a bag, nothing to do. + return nullptr; + } + + const ResTable_map_entry* map = reinterpret_cast<const ResTable_map_entry*>(entry.entry); + const ResTable_map* map_entry = + reinterpret_cast<const ResTable_map*>(reinterpret_cast<const uint8_t*>(map) + map->size); + const ResTable_map* const map_entry_end = map_entry + dtohl(map->count); + + const uint32_t parent = dtohl(map->parent.ident); + if (parent == 0) { + // There is no parent, meaning there is nothing to inherit and we can do a simple + // copy of the entries in the map. + const size_t entry_count = map_entry_end - map_entry; + util::unique_cptr<ResolvedBag> new_bag{reinterpret_cast<ResolvedBag*>( + malloc(sizeof(ResolvedBag) + (entry_count * sizeof(ResolvedBag::Entry))))}; + ResolvedBag::Entry* new_entry = new_bag->entries; + for (; map_entry != map_entry_end; ++map_entry) { + new_entry->cookie = cookie; + new_entry->value.copyFrom_dtoh(map_entry->value); + new_entry->key = dtohl(map_entry->name.ident); + new_entry->key_pool = nullptr; + new_entry->type_pool = nullptr; + ++new_entry; + } + new_bag->type_spec_flags = flags; + new_bag->entry_count = static_cast<uint32_t>(entry_count); + ResolvedBag* result = new_bag.get(); + cached_bags_[resid] = std::move(new_bag); + return result; + } + + // Get the parent and do a merge of the keys. + const ResolvedBag* parent_bag = GetBag(parent); + if (parent_bag == nullptr) { + // Failed to get the parent that should exist. + 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)))); + ResolvedBag::Entry* new_entry = new_bag->entries; + + const ResolvedBag::Entry* parent_entry = parent_bag->entries; + const ResolvedBag::Entry* const parent_entry_end = parent_entry + parent_bag->entry_count; + + // The keys are expected to be in sorted order. Merge the two bags. + while (map_entry != map_entry_end && parent_entry != parent_entry_end) { + const uint32_t child_key = dtohl(map_entry->name.ident); + if (child_key <= parent_entry->key) { + // 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; + ++map_entry; + } else { + // Take the parent entry as-is. + memcpy(new_entry, parent_entry, sizeof(*new_entry)); + } + + if (child_key >= parent_entry->key) { + // Move to the next parent entry if we used it or it was overridden. + ++parent_entry; + } + // Increment to the next entry to fill. + ++new_entry; + } + + // Finish the child entries if they exist. + while (map_entry != map_entry_end) { + new_entry->cookie = cookie; + new_entry->value.copyFrom_dtoh(map_entry->value); + new_entry->key = dtohl(map_entry->name.ident); + new_entry->key_pool = nullptr; + new_entry->type_pool = nullptr; + ++map_entry; + ++new_entry; + } + + // Finish the parent entries if they exist. + if (parent_entry != parent_entry_end) { + // Take the rest of the parent entries as-is. + const size_t num_entries_to_copy = parent_entry_end - parent_entry; + memcpy(new_entry, parent_entry, num_entries_to_copy * sizeof(*new_entry)); + new_entry += num_entries_to_copy; + } + + // 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)))); + } + + 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); + return result; +} + +void AssetManager2::InvalidateCaches(uint32_t diff) { + if (diff == 0xffffffffu) { + // Everything must go. + cached_bags_.clear(); + return; + } + + // Be more conservative with what gets purged. Only if the bag has other possible + // variations with respect to what changed (diff) should we remove it. + for (auto iter = cached_bags_.cbegin(); iter != cached_bags_.cend();) { + if (diff & iter->second->type_spec_flags) { + iter = cached_bags_.erase(iter); + } else { + ++iter; + } + } +} + +std::unique_ptr<Theme> AssetManager2::NewTheme() { return std::unique_ptr<Theme>(new Theme(this)); } + +bool Theme::ApplyStyle(uint32_t resid, bool force) { + ATRACE_CALL(); + + const ResolvedBag* bag = asset_manager_->GetBag(resid); + if (bag == nullptr) { + return false; + } + + // 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) { + 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 (!util::is_valid_resid(attr_resid)) { + return false; + } + + const uint32_t package_idx = util::get_package_id(attr_resid); + + // The type ID is 1-based, so subtract 1 to get an index. + const uint32_t type_idx = util::get_type_id(attr_resid) - 1; + const uint32_t entry_idx = util::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; + } + } + + // 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 = util::get_package_id(attr_resid); + const uint32_t type_idx = util::get_type_id(attr_resid) - 1; + const uint32_t entry_idx = util::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)); + } + type->entry_capacity = type->entry_count; + } + Entry& entry = type->entries[entry_idx]; + if (force || entry.value.dataType == Res_value::TYPE_NULL) { + entry.cookie = bag_iter->cookie; + entry.type_spec_flags |= bag->type_spec_flags; + entry.value = bag_iter->value; + } + } + return true; +} + +ApkAssetsCookie Theme::GetAttribute(uint32_t resid, Res_value* out_value, + uint32_t* out_flags) const { + constexpr const int kMaxIterations = 20; + + uint32_t type_spec_flags = 0u; + + for (int iterations_left = kMaxIterations; iterations_left > 0; iterations_left--) { + if (!util::is_valid_resid(resid)) { + return kInvalidCookie; + } + + const uint32_t package_idx = util::get_package_id(resid); + + // Type ID is 1-based, subtract 1 to get the index. + const uint32_t type_idx = util::get_type_id(resid) - 1; + const uint32_t entry_idx = util::get_entry_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_ATTRIBUTE: + resid = entry.value.data; + break; + + case Res_value::TYPE_NULL: + return kInvalidCookie; + + 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); + return kInvalidCookie; +} + +void Theme::Clear() { + type_spec_flags_ = 0u; + for (std::unique_ptr<Package>& package : packages_) { + package.reset(); + } +} + +bool Theme::SetTo(const Theme& o) { + if (this == &o) { + return true; + } + + if (asset_manager_ != o.asset_manager_) { + return false; + } + + type_spec_flags_ = o.type_spec_flags_; + + for (size_t p = 0; p < arraysize(packages_); p++) { + const Package* package = o.packages_[p].get(); + if (package == nullptr) { + packages_[p].reset(); + continue; + } + + for (size_t t = 0; t < arraysize(package->types); t++) { + const Type* type = package->types[t].get(); + if (type == nullptr) { + packages_[p]->types[t].reset(); + continue; + } + + const size_t type_alloc_size = sizeof(Type) + (type->entry_capacity * sizeof(Entry)); + void* copied_data = malloc(type_alloc_size); + memcpy(copied_data, type, type_alloc_size); + packages_[p]->types[t].reset(reinterpret_cast<Type*>(copied_data)); + } + } + return true; +} + +} // namespace android diff --git a/libs/androidfw/AttributeResolution.cpp b/libs/androidfw/AttributeResolution.cpp new file mode 100644 index 000000000000..2771ade1dd12 --- /dev/null +++ b/libs/androidfw/AttributeResolution.cpp @@ -0,0 +1,521 @@ +/* + * 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 "androidfw/AttributeResolution.h" + +#include <cstdint> + +#include <log/log.h> + +#include "androidfw/AttributeFinder.h" +#include "androidfw/ResourceTypes.h" + +constexpr bool kDebugStyles = false; + +namespace android { + +class XmlAttributeFinder + : public BackTrackingAttributeFinder<XmlAttributeFinder, size_t> { + public: + explicit XmlAttributeFinder(const ResXMLParser* parser) + : BackTrackingAttributeFinder( + 0, parser != nullptr ? parser->getAttributeCount() : 0), + parser_(parser) {} + + inline uint32_t GetAttribute(size_t index) const { + return parser_->getAttributeNameResID(index); + } + + private: + const ResXMLParser* parser_; +}; + +class BagAttributeFinder + : public BackTrackingAttributeFinder<BagAttributeFinder, + const ResTable::bag_entry*> { + public: + BagAttributeFinder(const ResTable::bag_entry* start, + const ResTable::bag_entry* end) + : BackTrackingAttributeFinder(start, end) {} + + inline uint32_t GetAttribute(const ResTable::bag_entry* entry) const { + return entry->map.name.ident; + } +}; + +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) { + 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(); + 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; + if (def_style_attr != 0) { + Res_value value; + if (theme->getAttribute(def_style_attr, &value, + &def_style_bag_type_set_flags) >= 0) { + 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); + + // 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]; + + if (kDebugStyles) { + ALOGI("RETRIEVING ATTR 0x%08x...", cur_ident); + } + + ssize_t block = -1; + uint32_t type_set_flags = 0; + + value.dataType = Res_value::TYPE_NULL; + value.data = Res_value::DATA_NULL_UNDEFINED; + config.density = 0; + + // Try to find a value for this attribute... we prioritize values + // coming from, first XML attributes, then XML style, then default + // style, and finally the theme. + + // Retrieve the current input value if available. + if (src_values_length > 0 && src_values[ii] != 0) { + 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); + } + } + + if (value.dataType == Res_value::TYPE_NULL) { + 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; + if (kDebugStyles) { + ALOGI("-> From def style: type=0x%x, data=0x%08x", value.dataType, + value.data); + } + } + } + + 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; + if (kDebugStyles) { + ALOGI("-> Resolved attr: type=0x%x, data=0x%08x", value.dataType, + value.data); + } + } else { + // 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 (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; + if (kDebugStyles) { + ALOGI("-> Resolved theme: type=0x%x, data=0x%08x", value.dataType, + value.data); + } + } + } + + // Deal with the special @null value -- it turns back to TYPE_NULL. + if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) { + if (kDebugStyles) { + ALOGI("-> Setting to @null!"); + } + value.dataType = Res_value::TYPE_NULL; + value.data = Res_value::DATA_NULL_UNDEFINED; + block = -1; + } + + if (kDebugStyles) { + ALOGI("Attribute 0x%08x: type=0x%x, data=0x%08x", cur_ident, + value.dataType, value.data); + } + + // 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_RESOURCE_ID] = resid; + out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags; + out_values[STYLE_DENSITY] = config.density; + + if (out_indices != nullptr && value.dataType != Res_value::TYPE_NULL) { + indices_idx++; + out_indices[indices_idx] = ii; + } + + 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, + 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); + } + + const ResTable& res = theme->getResTable(); + 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; + if (def_style_attr != 0) { + Res_value value; + if (theme->getAttribute(def_style_attr, &value, + &def_style_bag_type_set_flags) >= 0) { + if (value.dataType == Res_value::TYPE_REFERENCE) { + def_style_res = value.data; + } + } + } + + // Retrieve the style class associated with the current XML tag. + int style = 0; + uint32_t style_bag_type_set_flags = 0; + 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) { + value.dataType = Res_value::TYPE_NULL; + } + } + if (value.dataType == value.TYPE_REFERENCE) { + style = 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); + + // 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); + + // 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. + for (size_t ii = 0; ii < attrs_length; ii++) { + const uint32_t cur_ident = attrs[ii]; + + if (kDebugStyles) { + ALOGI("RETRIEVING ATTR 0x%08x...", cur_ident); + } + + ssize_t block = kXmlBlock; + uint32_t type_set_flags = 0; + + value.dataType = Res_value::TYPE_NULL; + value.data = Res_value::DATA_NULL_UNDEFINED; + config.density = 0; + + // Try to find a value for this attribute... we prioritize values + // coming from, first XML attributes, then XML style, then default + // style, and finally the theme. + + // 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) { + // We found the attribute we were looking for. + xml_parser->getAttributeValue(xml_attr_idx, &value); + if (kDebugStyles) { + ALOGI("-> From XML: type=0x%x, data=0x%08x", value.dataType, + value.data); + } + } + + if (value.dataType == Res_value::TYPE_NULL) { + // 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) { + // 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; + if (kDebugStyles) { + ALOGI("-> From style: type=0x%x, data=0x%08x", value.dataType, + value.data); + } + } + } + + if (value.dataType == Res_value::TYPE_NULL) { + // 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) { + // 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; + if (kDebugStyles) { + ALOGI("-> From def style: type=0x%x, data=0x%08x", value.dataType, + value.data); + } + } + } + + 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; + } + + if (kDebugStyles) { + ALOGI("-> Resolved attr: type=0x%x, data=0x%08x", value.dataType, + value.data); + } + } else { + // 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 (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; + } + + if (kDebugStyles) { + ALOGI("-> Resolved theme: type=0x%x, data=0x%08x", value.dataType, + value.data); + } + } + } + + // Deal with the special @null value -- it turns back to TYPE_NULL. + if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) { + if (kDebugStyles) { + ALOGI("-> Setting to @null!"); + } + value.dataType = Res_value::TYPE_NULL; + value.data = Res_value::DATA_NULL_UNDEFINED; + block = kXmlBlock; + } + + if (kDebugStyles) { + ALOGI("Attribute 0x%08x: type=0x%x, data=0x%08x", cur_ident, + value.dataType, value.data); + } + + // 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_RESOURCE_ID] = resid; + out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags; + out_values[STYLE_DENSITY] = config.density; + + if (value.dataType != Res_value::TYPE_NULL) { + indices_idx++; + + // out_indices must NOT be nullptr. + out_indices[indices_idx] = ii; + } + + 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) { + 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; + + value.dataType = Res_value::TYPE_NULL; + value.data = Res_value::DATA_NULL_UNDEFINED; + config.density = 0; + + // Try to find a value for this attribute... + // Skip through XML attributes until the end or the next possible match. + while (ix < xml_attr_count && cur_ident > cur_xml_attr) { + ix++; + cur_xml_attr = xml_parser->getAttributeNameResID(ix); + } + // Retrieve the current XML attribute if it matches, and step to next. + if (ix < xml_attr_count && cur_ident == cur_xml_attr) { + xml_parser->getAttributeValue(ix, &value); + ix++; + cur_xml_attr = xml_parser->getAttributeNameResID(ix); + } + + uint32_t resid = 0; + 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; + } + + // 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; + } + + // 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_RESOURCE_ID] = resid; + out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags; + out_values[STYLE_DENSITY] = config.density; + + if (out_indices != nullptr && value.dataType != Res_value::TYPE_NULL) { + indices_idx++; + out_indices[indices_idx] = ii; + } + + out_values += STYLE_NUM_ENTRIES; + } + + res->unlock(); + + if (out_indices != nullptr) { + out_indices[0] = indices_idx; + } + return true; +} + +} // namespace android diff --git a/libs/androidfw/Chunk.h b/libs/androidfw/Chunk.h new file mode 100644 index 000000000000..e87b94087450 --- /dev/null +++ b/libs/androidfw/Chunk.h @@ -0,0 +1,113 @@ +/* + * 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. + */ + +#ifndef CHUNK_H_ +#define CHUNK_H_ + +#include "android-base/logging.h" +#include "android-base/macros.h" +#include "utils/ByteOrder.h" + +#ifdef _WIN32 +#ifdef ERROR +#undef ERROR +#endif +#endif + +#include "androidfw/ResourceTypes.h" + +namespace android { + +// Helpful wrapper around a ResChunk_header that provides getter methods +// that handle endianness conversions and provide access to the data portion +// of the chunk. +class Chunk { + public: + explicit Chunk(const ResChunk_header* chunk) : device_chunk_(chunk) {} + + // Returns the type of the chunk. Caller need not worry about endianness. + inline int type() const { return dtohs(device_chunk_->type); } + + // Returns the size of the entire chunk. This can be useful for skipping + // over the entire chunk. Caller need not worry about endianness. + inline size_t size() const { return dtohl(device_chunk_->size); } + + // Returns the size of the header. Caller need not worry about endianness. + inline size_t header_size() const { return dtohs(device_chunk_->headerSize); } + + template <typename T> + inline const T* header() const { + if (header_size() >= sizeof(T)) { + return reinterpret_cast<const T*>(device_chunk_); + } + return nullptr; + } + + inline const void* data_ptr() const { + return reinterpret_cast<const uint8_t*>(device_chunk_) + header_size(); + } + + inline size_t data_size() const { return size() - header_size(); } + + private: + const ResChunk_header* device_chunk_; +}; + +// Provides a Java style iterator over an array of ResChunk_header's. +// Validation is performed while iterating. +// The caller should check if there was an error during chunk validation +// by calling HadError() and GetLastError() to get the reason for failure. +// Example: +// +// ChunkIterator iter(data_ptr, data_len); +// while (iter.HasNext()) { +// const Chunk chunk = iter.Next(); +// ... +// } +// +// if (iter.HadError()) { +// LOG(ERROR) << iter.GetLastError(); +// } +// +class ChunkIterator { + public: + ChunkIterator(const void* data, size_t len) + : next_chunk_(reinterpret_cast<const ResChunk_header*>(data)), + len_(len), + last_error_(nullptr) { + CHECK(next_chunk_ != nullptr) << "data can't be nullptr"; + VerifyNextChunk(); + } + + Chunk Next(); + inline bool HasNext() const { return !HadError() && len_ != 0; }; + inline bool HadError() const { return last_error_ != nullptr; } + inline std::string GetLastError() const { return last_error_; } + + private: + DISALLOW_COPY_AND_ASSIGN(ChunkIterator); + + // Returns false if there was an error. + bool VerifyNextChunk(); + + const ResChunk_header* next_chunk_; + size_t len_; + const char* last_error_; +}; + +} // namespace android + +#endif /* CHUNK_H_ */ diff --git a/libs/androidfw/ChunkIterator.cpp b/libs/androidfw/ChunkIterator.cpp new file mode 100644 index 000000000000..747aa4ad20e6 --- /dev/null +++ b/libs/androidfw/ChunkIterator.cpp @@ -0,0 +1,81 @@ +/* + * 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 "Chunk.h" + +#include "android-base/logging.h" + +namespace android { + +Chunk ChunkIterator::Next() { + CHECK(len_ != 0) << "called Next() after last chunk"; + + const ResChunk_header* this_chunk = next_chunk_; + + // We've already checked the values of this_chunk, so safely increment. + next_chunk_ = reinterpret_cast<const ResChunk_header*>( + reinterpret_cast<const uint8_t*>(this_chunk) + dtohl(this_chunk->size)); + len_ -= dtohl(this_chunk->size); + + if (len_ != 0) { + // Prepare the next chunk. + VerifyNextChunk(); + } + return Chunk(this_chunk); +} + +// Returns false if there was an error. +bool ChunkIterator::VerifyNextChunk() { + const uintptr_t header_start = reinterpret_cast<uintptr_t>(next_chunk_); + + // This data must be 4-byte aligned, since we directly + // access 32-bit words, which must be aligned on + // certain architectures. + if (header_start & 0x03) { + last_error_ = "header not aligned on 4-byte boundary"; + return false; + } + + if (len_ < sizeof(ResChunk_header)) { + last_error_ = "not enough space for header"; + return false; + } + + const size_t header_size = dtohs(next_chunk_->headerSize); + const size_t size = dtohl(next_chunk_->size); + if (header_size < sizeof(ResChunk_header)) { + last_error_ = "header size too small"; + return false; + } + + if (header_size > size) { + last_error_ = "header size is larger than entire chunk"; + return false; + } + + if (size > len_) { + last_error_ = "chunk size is bigger than given data"; + return false; + } + + if ((size | header_size) & 0x03) { + last_error_ = "header sizes are not aligned on 4-byte boundary"; + return false; + } + return true; +} + +} // namespace android diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp new file mode 100644 index 000000000000..94d0d4638ba8 --- /dev/null +++ b/libs/androidfw/LoadedArsc.cpp @@ -0,0 +1,572 @@ +/* + * 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. + */ + +#define ATRACE_TAG ATRACE_TAG_RESOURCES + +#include "androidfw/LoadedArsc.h" + +#include <cstddef> +#include <limits> + +#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 "Chunk.h" +#include "androidfw/ByteBucketArray.h" +#include "androidfw/Util.h" + +using android::base::StringPrintf; + +namespace android { + +namespace { + +// 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>; + +// Builder that helps accumulate Type structs and then create a single +// contiguous block of memory to store both the TypeSpec struct and +// the Type structs. +class TypeSpecPtrBuilder { + public: + TypeSpecPtrBuilder(const ResTable_typeSpec* header) : header_(header) {} + + void AddType(const ResTable_type* type) { + ResTable_config config; + config.copyFromDtoH(type->config); + types_.push_back(Type{config, type}); + } + + TypeSpecPtr Build() { + // Check for overflow. + if ((std::numeric_limits<size_t>::max() - sizeof(TypeSpec)) / sizeof(Type) < types_.size()) { + return {}; + } + TypeSpec* type_spec = (TypeSpec*)::malloc(sizeof(TypeSpec) + (types_.size() * sizeof(Type))); + type_spec->type_spec = header_; + type_spec->type_count = types_.size(); + memcpy(type_spec + 1, types_.data(), types_.size() * sizeof(Type)); + return TypeSpecPtr(type_spec); + } + + private: + DISALLOW_COPY_AND_ASSIGN(TypeSpecPtrBuilder); + + const ResTable_typeSpec* header_; + std::vector<Type> types_; +}; + +} // namespace + +class LoadedPackage { + public: + LoadedPackage() = default; + + bool FindEntry(uint8_t type_id, uint16_t entry_id, const ResTable_config& config, + LoadedArsc::Entry* out_entry, ResTable_config* out_selected_config, + uint32_t* out_flags) const; + + ResStringPool type_string_pool_; + ResStringPool key_string_pool_; + std::string package_name_; + int package_id_ = -1; + + ByteBucketArray<TypeSpecPtr> type_specs_; + + private: + DISALLOW_COPY_AND_ASSIGN(LoadedPackage); +}; + +bool LoadedPackage::FindEntry(uint8_t type_id, uint16_t entry_id, const ResTable_config& config, + LoadedArsc::Entry* out_entry, ResTable_config* out_selected_config, + uint32_t* out_flags) const { + ATRACE_NAME("LoadedPackage::FindEntry"); + const TypeSpecPtr& ptr = type_specs_[type_id]; + if (ptr == nullptr) { + return false; + } + + // Don't bother checking if the entry ID is larger than + // the number of entries. + if (entry_id >= dtohl(ptr->type_spec->entryCount)) { + 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_id < 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_id]); + 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); + } + } + } + } + + if (best_type == nullptr) { + return false; + } + + const uint32_t* flags = reinterpret_cast<const uint32_t*>(ptr->type_spec + 1); + *out_flags = dtohl(flags[entry_id]); + *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, Entry* out_entry, + ResTable_config* out_selected_config, uint32_t* out_flags) const { + ATRACE_NAME("LoadedArsc::FindEntry"); + const uint8_t package_id = util::get_package_id(resid); + const uint8_t type_id = util::get_type_id(resid); + const uint16_t entry_id = util::get_entry_id(resid); + + if (type_id == 0) { + LOG(ERROR) << "Invalid ID 0x" << std::hex << resid << std::dec << "."; + 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); + } + } + return false; +} + +const std::string* LoadedArsc::GetPackageNameForId(uint32_t resid) const { + const uint8_t package_id = util::get_package_id(resid); + for (const auto& loaded_package : packages_) { + if (loaded_package->package_id_ == package_id) { + return &loaded_package->package_name_; + } + } + return nullptr; +} + +static bool VerifyType(const Chunk& chunk) { + ATRACE_CALL(); + const ResTable_type* header = chunk.header<ResTable_type>(); + + 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."; + 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; + + if (offsets_offset + offsets_length > entries_offset) { + LOG(ERROR) << "Entry offsets overlap actual entry data."; + return false; + } + + if (entries_offset > chunk.size()) { + LOG(ERROR) << "Entry offsets extend beyond chunk."; + return false; + } + + if (entries_offset & 0x03) { + LOG(ERROR) << "Entries start at unaligned address."; + 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; + } + + 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 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; + } + + // 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; + } + + // 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); + + size_t map_entries_start = offset + entry_size; + if (map_entries_start & 0x03) { + LOG(ERROR) << "Map entries start at unaligned offset."; + 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."; + return false; + } + + // 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; + } + + 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; + } + + if (value_size > chunk.size() || offset + entry_size > chunk.size() - value_size) { + LOG(ERROR) << "Res_value size is too large."; + return false; + } + } + } + } + return true; +} + +static bool LoadPackage(const Chunk& chunk, LoadedPackage* loaded_package) { + ATRACE_CALL(); + const ResTable_package* header = chunk.header<ResTable_package>(); + if (header == nullptr) { + LOG(ERROR) << "Chunk RES_TABLE_PACKAGE_TYPE is too small."; + return false; + } + + loaded_package->package_id_ = dtohl(header->id); + + // A TypeSpec builder. We use this to accumulate the set of Types + // available for a TypeSpec, and later build a single, contiguous block + // of memory that holds all the Types together with the TypeSpec. + std::unique_ptr<TypeSpecPtrBuilder> types_builder; + + // Keep track of the last seen type index. Since type IDs are 1-based, + // this records their index, which is 0-based (type ID - 1). + uint8_t last_type_idx = 0; + + ChunkIterator iter(chunk.data_ptr(), chunk.data_size()); + while (iter.HasNext()) { + const Chunk child_chunk = iter.Next(); + switch (child_chunk.type()) { + case RES_STRING_POOL_TYPE: { + const uintptr_t pool_address = + reinterpret_cast<uintptr_t>(child_chunk.header<ResChunk_header>()); + const uintptr_t header_address = reinterpret_cast<uintptr_t>(header); + if (pool_address == header_address + dtohl(header->typeStrings)) { + // This string pool is the type string pool. + 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."; + return false; + } + } else if (pool_address == header_address + dtohl(header->keyStrings)) { + // This string pool is the key string pool. + 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."; + return false; + } + } else { + LOG(WARNING) << "Too many string pool chunks found in package."; + } + } break; + + case RES_TABLE_TYPE_SPEC_TYPE: { + ATRACE_NAME("LoadTableTypeSpec"); + + // Starting a new TypeSpec, so finish the old one if there was one. + if (types_builder) { + TypeSpecPtr type_spec_ptr = types_builder->Build(); + if (type_spec_ptr == nullptr) { + LOG(ERROR) << "Too many type configurations, overflow detected."; + return false; + } + + loaded_package->type_specs_.editItemAt(last_type_idx) = std::move(type_spec_ptr); + + types_builder = {}; + last_type_idx = 0; + } + + 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."; + return false; + } + + if (type_spec->id == 0) { + LOG(ERROR) << "Chunk RES_TABLE_TYPE_SPEC_TYPE has invalid ID 0."; + return false; + } + + // The data portion of this chunk contains entry_count 32bit entries, + // each one representing a set of flags. + // Here we only validate that the chunk is well formed. + const size_t entry_count = dtohl(type_spec->entryCount); + + // 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 << "."; + return false; + } + + if (entry_count * sizeof(uint32_t) > chunk.data_size()) { + LOG(ERROR) << "Chunk too small to hold entries in RES_TABLE_TYPE_SPEC_TYPE."; + return false; + } + + last_type_idx = type_spec->id - 1; + types_builder = util::make_unique<TypeSpecPtrBuilder>(type_spec); + } break; + + case RES_TABLE_TYPE_TYPE: { + const ResTable_type* type = child_chunk.header<ResTable_type>(); + if (type == nullptr) { + LOG(ERROR) << "Chunk RES_TABLE_TYPE_TYPE is too small."; + return false; + } + + if (type->id == 0) { + LOG(ERROR) << "Chunk RES_TABLE_TYPE_TYPE has invalid ID 0."; + return false; + } + + // 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 false; + } + + if (!VerifyType(child_chunk)) { + return false; + } + + types_builder->AddType(type); + } break; + + default: + LOG(WARNING) << base::StringPrintf("Unknown chunk type '%02x'.", chunk.type()); + break; + } + } + + // Finish the last TypeSpec. + if (types_builder) { + TypeSpecPtr type_spec_ptr = types_builder->Build(); + if (type_spec_ptr == nullptr) { + LOG(ERROR) << "Too many type configurations, overflow detected."; + return false; + } + loaded_package->type_specs_.editItemAt(last_type_idx) = std::move(type_spec_ptr); + } + + if (iter.HadError()) { + LOG(ERROR) << iter.GetLastError(); + return false; + } + return true; +} + +bool LoadedArsc::LoadTable(const Chunk& chunk) { + ATRACE_CALL(); + const ResTable_header* header = chunk.header<ResTable_header>(); + if (header == nullptr) { + LOG(ERROR) << "Chunk RES_TABLE_TYPE is too small."; + return false; + } + + const size_t package_count = dtohl(header->packageCount); + size_t packages_seen = 0; + + packages_.reserve(package_count); + + ChunkIterator iter(chunk.data_ptr(), chunk.data_size()); + while (iter.HasNext()) { + const Chunk child_chunk = iter.Next(); + switch (child_chunk.type()) { + case RES_STRING_POOL_TYPE: + // Only use the first string pool. Ignore others. + if (global_string_pool_.getError() == NO_INIT) { + status_t err = global_string_pool_.setTo(child_chunk.header<ResStringPool_header>(), + child_chunk.size()); + if (err != NO_ERROR) { + LOG(ERROR) << "Corrupt string pool."; + return false; + } + } else { + LOG(WARNING) << "Multiple string pool chunks found in resource table."; + } + 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."; + return false; + } + packages_seen++; + + std::unique_ptr<LoadedPackage> loaded_package = util::make_unique<LoadedPackage>(); + if (!LoadPackage(child_chunk, loaded_package.get())) { + return false; + } + packages_.push_back(std::move(loaded_package)); + } break; + + default: + LOG(WARNING) << base::StringPrintf("Unknown chunk type '%02x'.", chunk.type()); + break; + } + } + + if (iter.HadError()) { + LOG(ERROR) << iter.GetLastError(); + return false; + } + return true; +} + +std::unique_ptr<LoadedArsc> LoadedArsc::Load(const void* data, size_t len) { + ATRACE_CALL(); + + // Not using make_unique because the constructor is private. + std::unique_ptr<LoadedArsc> loaded_arsc(new LoadedArsc()); + + ChunkIterator iter(data, len); + while (iter.HasNext()) { + const Chunk chunk = iter.Next(); + switch (chunk.type()) { + case RES_TABLE_TYPE: + if (!loaded_arsc->LoadTable(chunk)) { + return {}; + } + break; + + default: + LOG(WARNING) << base::StringPrintf("Unknown chunk type '%02x'.", chunk.type()); + break; + } + } + + if (iter.HadError()) { + LOG(ERROR) << iter.GetLastError(); + return {}; + } + return loaded_arsc; +} + +} // namespace android diff --git a/libs/androidfw/LocaleDataTables.cpp b/libs/androidfw/LocaleDataTables.cpp index 1ac508525061..7c381efec7ec 100644 --- a/libs/androidfw/LocaleDataTables.cpp +++ b/libs/androidfw/LocaleDataTables.cpp @@ -1,4 +1,4 @@ -// Auto-generated by frameworks/base/tools/localedata/extract_icu_data.py +// Auto-generated by ./tools/localedata/extract_icu_data.py const char SCRIPT_CODES[][4] = { /* 0 */ {'A', 'h', 'o', 'm'}, @@ -39,27 +39,27 @@ const char SCRIPT_CODES[][4] = { /* 35 */ {'K', 'h', 'm', 'r'}, /* 36 */ {'K', 'n', 'd', 'a'}, /* 37 */ {'K', 'o', 'r', 'e'}, - /* 38 */ {'K', 't', 'h', 'i'}, - /* 39 */ {'L', 'a', 'n', 'a'}, - /* 40 */ {'L', 'a', 'o', 'o'}, - /* 41 */ {'L', 'a', 't', 'n'}, - /* 42 */ {'L', 'e', 'p', 'c'}, - /* 43 */ {'L', 'i', 'n', 'a'}, - /* 44 */ {'L', 'i', 's', 'u'}, - /* 45 */ {'L', 'y', 'c', 'i'}, - /* 46 */ {'L', 'y', 'd', 'i'}, - /* 47 */ {'M', 'a', 'n', 'd'}, - /* 48 */ {'M', 'a', 'n', 'i'}, - /* 49 */ {'M', 'e', 'r', 'c'}, - /* 50 */ {'M', 'l', 'y', 'm'}, - /* 51 */ {'M', 'o', 'n', 'g'}, - /* 52 */ {'M', 'r', 'o', 'o'}, - /* 53 */ {'M', 'y', 'm', 'r'}, - /* 54 */ {'N', 'a', 'r', 'b'}, - /* 55 */ {'N', 'k', 'o', 'o'}, - /* 56 */ {'O', 'g', 'a', 'm'}, - /* 57 */ {'O', 'r', 'k', 'h'}, - /* 58 */ {'O', 'r', 'y', 'a'}, + /* 38 */ {'L', 'a', 'n', 'a'}, + /* 39 */ {'L', 'a', 'o', 'o'}, + /* 40 */ {'L', 'a', 't', 'n'}, + /* 41 */ {'L', 'e', 'p', 'c'}, + /* 42 */ {'L', 'i', 'n', 'a'}, + /* 43 */ {'L', 'i', 's', 'u'}, + /* 44 */ {'L', 'y', 'c', 'i'}, + /* 45 */ {'L', 'y', 'd', 'i'}, + /* 46 */ {'M', 'a', 'n', 'd'}, + /* 47 */ {'M', 'a', 'n', 'i'}, + /* 48 */ {'M', 'e', 'r', 'c'}, + /* 49 */ {'M', 'l', 'y', 'm'}, + /* 50 */ {'M', 'o', 'n', 'g'}, + /* 51 */ {'M', 'r', 'o', 'o'}, + /* 52 */ {'M', 'y', 'm', 'r'}, + /* 53 */ {'N', 'a', 'r', 'b'}, + /* 54 */ {'N', 'k', 'o', 'o'}, + /* 55 */ {'O', 'g', 'a', 'm'}, + /* 56 */ {'O', 'r', 'k', 'h'}, + /* 57 */ {'O', 'r', 'y', 'a'}, + /* 58 */ {'O', 's', 'g', 'e'}, /* 59 */ {'P', 'a', 'u', 'c'}, /* 60 */ {'P', 'h', 'l', 'i'}, /* 61 */ {'P', 'h', 'n', 'x'}, @@ -76,78 +76,147 @@ const char SCRIPT_CODES[][4] = { /* 72 */ {'T', 'a', 'l', 'e'}, /* 73 */ {'T', 'a', 'l', 'u'}, /* 74 */ {'T', 'a', 'm', 'l'}, - /* 75 */ {'T', 'a', 'v', 't'}, - /* 76 */ {'T', 'e', 'l', 'u'}, - /* 77 */ {'T', 'f', 'n', 'g'}, - /* 78 */ {'T', 'h', 'a', 'a'}, - /* 79 */ {'T', 'h', 'a', 'i'}, - /* 80 */ {'T', 'i', 'b', 't'}, - /* 81 */ {'U', 'g', 'a', 'r'}, - /* 82 */ {'V', 'a', 'i', 'i'}, - /* 83 */ {'X', 'p', 'e', 'o'}, - /* 84 */ {'X', 's', 'u', 'x'}, - /* 85 */ {'Y', 'i', 'i', 'i'}, - /* 86 */ {'~', '~', '~', 'A'}, - /* 87 */ {'~', '~', '~', 'B'}, + /* 75 */ {'T', 'a', 'n', 'g'}, + /* 76 */ {'T', 'a', 'v', 't'}, + /* 77 */ {'T', 'e', 'l', 'u'}, + /* 78 */ {'T', 'f', 'n', 'g'}, + /* 79 */ {'T', 'h', 'a', 'a'}, + /* 80 */ {'T', 'h', 'a', 'i'}, + /* 81 */ {'T', 'i', 'b', 't'}, + /* 82 */ {'U', 'g', 'a', 'r'}, + /* 83 */ {'V', 'a', 'i', 'i'}, + /* 84 */ {'X', 'p', 'e', 'o'}, + /* 85 */ {'X', 's', 'u', 'x'}, + /* 86 */ {'Y', 'i', 'i', 'i'}, + /* 87 */ {'~', '~', '~', 'A'}, + /* 88 */ {'~', '~', '~', 'B'}, }; const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ - {0x61610000u, 41u}, // aa -> Latn + {0x61610000u, 40u}, // aa -> Latn + {0xA0000000u, 40u}, // aai -> Latn + {0xA8000000u, 40u}, // aak -> Latn + {0xD0000000u, 40u}, // aau -> Latn {0x61620000u, 15u}, // ab -> Cyrl - {0xC4200000u, 41u}, // abr -> Latn - {0x90400000u, 41u}, // ace -> Latn - {0x9C400000u, 41u}, // ach -> Latn - {0x80600000u, 41u}, // ada -> Latn + {0xA0200000u, 40u}, // abi -> Latn + {0xC4200000u, 40u}, // abr -> Latn + {0xCC200000u, 40u}, // abt -> Latn + {0xE0200000u, 40u}, // aby -> Latn + {0x8C400000u, 40u}, // acd -> Latn + {0x90400000u, 40u}, // ace -> Latn + {0x9C400000u, 40u}, // ach -> Latn + {0x80600000u, 40u}, // ada -> Latn + {0x90600000u, 40u}, // ade -> Latn + {0xA4600000u, 40u}, // adj -> Latn {0xE0600000u, 15u}, // ady -> Cyrl + {0xE4600000u, 40u}, // adz -> Latn {0x61650000u, 4u}, // ae -> Avst {0x84800000u, 1u}, // aeb -> Arab - {0x61660000u, 41u}, // af -> Latn - {0xC0C00000u, 41u}, // agq -> Latn + {0xE0800000u, 40u}, // aey -> Latn + {0x61660000u, 40u}, // af -> Latn + {0x88C00000u, 40u}, // agc -> Latn + {0x8CC00000u, 40u}, // agd -> Latn + {0x98C00000u, 40u}, // agg -> Latn + {0xB0C00000u, 40u}, // agm -> Latn + {0xB8C00000u, 40u}, // ago -> Latn + {0xC0C00000u, 40u}, // agq -> Latn + {0x80E00000u, 40u}, // aha -> Latn + {0xACE00000u, 40u}, // ahl -> Latn {0xB8E00000u, 0u}, // aho -> Ahom - {0x616B0000u, 41u}, // ak -> Latn - {0xA9400000u, 84u}, // akk -> Xsux - {0xB5600000u, 41u}, // aln -> Latn + {0x99200000u, 40u}, // ajg -> Latn + {0x616B0000u, 40u}, // ak -> Latn + {0xA9400000u, 85u}, // akk -> Xsux + {0x81600000u, 40u}, // ala -> Latn + {0xA1600000u, 40u}, // ali -> Latn + {0xB5600000u, 40u}, // aln -> Latn {0xCD600000u, 15u}, // alt -> Cyrl {0x616D0000u, 18u}, // am -> Ethi - {0xB9800000u, 41u}, // amo -> Latn - {0xE5C00000u, 41u}, // aoz -> Latn + {0xB1800000u, 40u}, // amm -> Latn + {0xB5800000u, 40u}, // amn -> Latn + {0xB9800000u, 40u}, // amo -> Latn + {0xBD800000u, 40u}, // amp -> Latn + {0x89A00000u, 40u}, // anc -> Latn + {0xA9A00000u, 40u}, // ank -> Latn + {0xB5A00000u, 40u}, // ann -> Latn + {0xE1A00000u, 40u}, // any -> Latn + {0xA5C00000u, 40u}, // aoj -> Latn + {0xB1C00000u, 40u}, // aom -> Latn + {0xE5C00000u, 40u}, // aoz -> Latn + {0x89E00000u, 1u}, // apc -> Arab + {0x8DE00000u, 1u}, // apd -> Arab + {0x91E00000u, 40u}, // ape -> Latn + {0xC5E00000u, 40u}, // apr -> Latn + {0xC9E00000u, 40u}, // aps -> Latn + {0xE5E00000u, 40u}, // apz -> Latn {0x61720000u, 1u}, // ar -> Arab - {0x61725842u, 87u}, // ar-XB -> ~~~B + {0x61725842u, 88u}, // ar-XB -> ~~~B {0x8A200000u, 2u}, // arc -> Armi - {0xB6200000u, 41u}, // arn -> Latn - {0xBA200000u, 41u}, // aro -> Latn + {0x9E200000u, 40u}, // arh -> Latn + {0xB6200000u, 40u}, // arn -> Latn + {0xBA200000u, 40u}, // aro -> Latn {0xC2200000u, 1u}, // arq -> Arab {0xE2200000u, 1u}, // ary -> Arab {0xE6200000u, 1u}, // arz -> Arab {0x61730000u, 7u}, // as -> Beng - {0x82400000u, 41u}, // asa -> Latn + {0x82400000u, 40u}, // asa -> Latn {0x92400000u, 68u}, // ase -> Sgnw - {0xCE400000u, 41u}, // ast -> Latn - {0xA6600000u, 41u}, // atj -> Latn + {0x9A400000u, 40u}, // asg -> Latn + {0xBA400000u, 40u}, // aso -> Latn + {0xCE400000u, 40u}, // ast -> Latn + {0x82600000u, 40u}, // ata -> Latn + {0x9A600000u, 40u}, // atg -> Latn + {0xA6600000u, 40u}, // atj -> Latn + {0xE2800000u, 40u}, // auy -> Latn {0x61760000u, 15u}, // av -> Cyrl + {0xAEA00000u, 1u}, // avl -> Arab + {0xB6A00000u, 40u}, // avn -> Latn + {0xCEA00000u, 40u}, // avt -> Latn + {0xD2A00000u, 40u}, // avu -> Latn {0x82C00000u, 16u}, // awa -> Deva - {0x61790000u, 41u}, // ay -> Latn - {0x617A0000u, 41u}, // az -> Latn + {0x86C00000u, 40u}, // awb -> Latn + {0xBAC00000u, 40u}, // awo -> Latn + {0xDEC00000u, 40u}, // awx -> Latn + {0x61790000u, 40u}, // ay -> Latn + {0x87000000u, 40u}, // ayb -> Latn + {0x617A0000u, 40u}, // az -> Latn {0x617A4951u, 1u}, // az-IQ -> Arab {0x617A4952u, 1u}, // az-IR -> Arab {0x617A5255u, 15u}, // az-RU -> Cyrl {0x62610000u, 15u}, // ba -> Cyrl {0xAC010000u, 1u}, // bal -> Arab - {0xB4010000u, 41u}, // ban -> Latn + {0xB4010000u, 40u}, // ban -> Latn {0xBC010000u, 16u}, // bap -> Deva - {0xC4010000u, 41u}, // bar -> Latn - {0xC8010000u, 41u}, // bas -> Latn + {0xC4010000u, 40u}, // bar -> Latn + {0xC8010000u, 40u}, // bas -> Latn + {0xD4010000u, 40u}, // bav -> Latn {0xDC010000u, 5u}, // bax -> Bamu - {0x88210000u, 41u}, // bbc -> Latn - {0xA4210000u, 41u}, // bbj -> Latn - {0xA0410000u, 41u}, // bci -> Latn + {0x80210000u, 40u}, // bba -> Latn + {0x84210000u, 40u}, // bbb -> Latn + {0x88210000u, 40u}, // bbc -> Latn + {0x8C210000u, 40u}, // bbd -> Latn + {0xA4210000u, 40u}, // bbj -> Latn + {0xBC210000u, 40u}, // bbp -> Latn + {0xC4210000u, 40u}, // bbr -> Latn + {0x94410000u, 40u}, // bcf -> Latn + {0x9C410000u, 40u}, // bch -> Latn + {0xA0410000u, 40u}, // bci -> Latn + {0xB0410000u, 40u}, // bcm -> Latn + {0xB4410000u, 40u}, // bcn -> Latn + {0xB8410000u, 40u}, // bco -> Latn + {0xC0410000u, 18u}, // bcq -> Ethi + {0xD0410000u, 40u}, // bcu -> Latn + {0x8C610000u, 40u}, // bdd -> Latn {0x62650000u, 15u}, // be -> Cyrl + {0x94810000u, 40u}, // bef -> Latn + {0x9C810000u, 40u}, // beh -> Latn {0xA4810000u, 1u}, // bej -> Arab - {0xB0810000u, 41u}, // bem -> Latn - {0xD8810000u, 41u}, // bew -> Latn - {0xE4810000u, 41u}, // bez -> Latn - {0x8CA10000u, 41u}, // bfd -> Latn + {0xB0810000u, 40u}, // bem -> Latn + {0xCC810000u, 40u}, // bet -> Latn + {0xD8810000u, 40u}, // bew -> Latn + {0xDC810000u, 40u}, // bex -> Latn + {0xE4810000u, 40u}, // bez -> Latn + {0x8CA10000u, 40u}, // bfd -> Latn {0xC0A10000u, 74u}, // bfq -> Taml {0xCCA10000u, 1u}, // bft -> Arab {0xE0A10000u, 16u}, // bfy -> Deva @@ -155,663 +224,1202 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ {0x88C10000u, 16u}, // bgc -> Deva {0xB4C10000u, 1u}, // bgn -> Arab {0xDCC10000u, 21u}, // bgx -> Grek - {0x62680000u, 38u}, // bh -> Kthi {0x84E10000u, 16u}, // bhb -> Deva + {0x98E10000u, 40u}, // bhg -> Latn {0xA0E10000u, 16u}, // bhi -> Deva - {0xA8E10000u, 41u}, // bhk -> Latn + {0xA8E10000u, 40u}, // bhk -> Latn + {0xACE10000u, 40u}, // bhl -> Latn {0xB8E10000u, 16u}, // bho -> Deva - {0x62690000u, 41u}, // bi -> Latn - {0xA9010000u, 41u}, // bik -> Latn - {0xB5010000u, 41u}, // bin -> Latn + {0xE0E10000u, 40u}, // bhy -> Latn + {0x62690000u, 40u}, // bi -> Latn + {0x85010000u, 40u}, // bib -> Latn + {0x99010000u, 40u}, // big -> Latn + {0xA9010000u, 40u}, // bik -> Latn + {0xB1010000u, 40u}, // bim -> Latn + {0xB5010000u, 40u}, // bin -> Latn + {0xB9010000u, 40u}, // bio -> Latn + {0xC1010000u, 40u}, // biq -> Latn + {0x9D210000u, 40u}, // bjh -> Latn + {0xA1210000u, 18u}, // bji -> Ethi {0xA5210000u, 16u}, // bjj -> Deva - {0xB5210000u, 41u}, // bjn -> Latn - {0xB1410000u, 41u}, // bkm -> Latn - {0xD1410000u, 41u}, // bku -> Latn - {0xCD610000u, 75u}, // blt -> Tavt - {0x626D0000u, 41u}, // bm -> Latn - {0xC1810000u, 41u}, // bmq -> Latn + {0xB5210000u, 40u}, // bjn -> Latn + {0xB9210000u, 40u}, // bjo -> Latn + {0xC5210000u, 40u}, // bjr -> Latn + {0xE5210000u, 40u}, // bjz -> Latn + {0x89410000u, 40u}, // bkc -> Latn + {0xB1410000u, 40u}, // bkm -> Latn + {0xC1410000u, 40u}, // bkq -> Latn + {0xD1410000u, 40u}, // bku -> Latn + {0xD5410000u, 40u}, // bkv -> Latn + {0xCD610000u, 76u}, // blt -> Tavt + {0x626D0000u, 40u}, // bm -> Latn + {0x9D810000u, 40u}, // bmh -> Latn + {0xA9810000u, 40u}, // bmk -> Latn + {0xC1810000u, 40u}, // bmq -> Latn + {0xD1810000u, 40u}, // bmu -> Latn {0x626E0000u, 7u}, // bn -> Beng - {0x626F0000u, 80u}, // bo -> Tibt + {0x99A10000u, 40u}, // bng -> Latn + {0xB1A10000u, 40u}, // bnm -> Latn + {0xBDA10000u, 40u}, // bnp -> Latn + {0x626F0000u, 81u}, // bo -> Tibt + {0xA5C10000u, 40u}, // boj -> Latn + {0xB1C10000u, 40u}, // bom -> Latn + {0xB5C10000u, 40u}, // bon -> Latn {0xE1E10000u, 7u}, // bpy -> Beng + {0x8A010000u, 40u}, // bqc -> Latn {0xA2010000u, 1u}, // bqi -> Arab - {0xD6010000u, 41u}, // bqv -> Latn - {0x62720000u, 41u}, // br -> Latn + {0xBE010000u, 40u}, // bqp -> Latn + {0xD6010000u, 40u}, // bqv -> Latn + {0x62720000u, 40u}, // br -> Latn {0x82210000u, 16u}, // bra -> Deva {0x9E210000u, 1u}, // brh -> Arab {0xDE210000u, 16u}, // brx -> Deva - {0x62730000u, 41u}, // bs -> Latn + {0xE6210000u, 40u}, // brz -> Latn + {0x62730000u, 40u}, // bs -> Latn + {0xA6410000u, 40u}, // bsj -> Latn {0xC2410000u, 6u}, // bsq -> Bass - {0xCA410000u, 41u}, // bss -> Latn - {0xBA610000u, 41u}, // bto -> Latn + {0xCA410000u, 40u}, // bss -> Latn + {0xCE410000u, 18u}, // bst -> Ethi + {0xBA610000u, 40u}, // bto -> Latn + {0xCE610000u, 40u}, // btt -> Latn {0xD6610000u, 16u}, // btv -> Deva {0x82810000u, 15u}, // bua -> Cyrl - {0x8A810000u, 41u}, // buc -> Latn - {0x9A810000u, 41u}, // bug -> Latn - {0xB2810000u, 41u}, // bum -> Latn - {0x86A10000u, 41u}, // bvb -> Latn + {0x8A810000u, 40u}, // buc -> Latn + {0x8E810000u, 40u}, // bud -> Latn + {0x9A810000u, 40u}, // bug -> Latn + {0xAA810000u, 40u}, // buk -> Latn + {0xB2810000u, 40u}, // bum -> Latn + {0xBA810000u, 40u}, // buo -> Latn + {0xCA810000u, 40u}, // bus -> Latn + {0xD2810000u, 40u}, // buu -> Latn + {0x86A10000u, 40u}, // bvb -> Latn + {0x8EC10000u, 40u}, // bwd -> Latn + {0xC6C10000u, 40u}, // bwr -> Latn + {0x9EE10000u, 40u}, // bxh -> Latn + {0x93010000u, 40u}, // bye -> Latn {0xB7010000u, 18u}, // byn -> Ethi - {0xD7010000u, 41u}, // byv -> Latn - {0x93210000u, 41u}, // bze -> Latn - {0x63610000u, 41u}, // ca -> Latn - {0x9C420000u, 41u}, // cch -> Latn + {0xC7010000u, 40u}, // byr -> Latn + {0xCB010000u, 40u}, // bys -> Latn + {0xD7010000u, 40u}, // byv -> Latn + {0xDF010000u, 40u}, // byx -> Latn + {0x83210000u, 40u}, // bza -> Latn + {0x93210000u, 40u}, // bze -> Latn + {0x97210000u, 40u}, // bzf -> Latn + {0x9F210000u, 40u}, // bzh -> Latn + {0xDB210000u, 40u}, // bzw -> Latn + {0x63610000u, 40u}, // ca -> Latn + {0xB4020000u, 40u}, // can -> Latn + {0xA4220000u, 40u}, // cbj -> Latn + {0x9C420000u, 40u}, // cch -> Latn {0xBC420000u, 7u}, // ccp -> Beng {0x63650000u, 15u}, // ce -> Cyrl - {0x84820000u, 41u}, // ceb -> Latn - {0x98C20000u, 41u}, // cgg -> Latn - {0x63680000u, 41u}, // ch -> Latn - {0xA8E20000u, 41u}, // chk -> Latn + {0x84820000u, 40u}, // ceb -> Latn + {0x80A20000u, 40u}, // cfa -> Latn + {0x98C20000u, 40u}, // cgg -> Latn + {0x63680000u, 40u}, // ch -> Latn + {0xA8E20000u, 40u}, // chk -> Latn {0xB0E20000u, 15u}, // chm -> Cyrl - {0xB8E20000u, 41u}, // cho -> Latn - {0xBCE20000u, 41u}, // chp -> Latn + {0xB8E20000u, 40u}, // cho -> Latn + {0xBCE20000u, 40u}, // chp -> Latn {0xC4E20000u, 12u}, // chr -> Cher {0x81220000u, 1u}, // cja -> Arab {0xB1220000u, 11u}, // cjm -> Cham + {0xD5220000u, 40u}, // cjv -> Latn {0x85420000u, 1u}, // ckb -> Arab - {0x636F0000u, 41u}, // co -> Latn + {0xAD420000u, 40u}, // ckl -> Latn + {0xB9420000u, 40u}, // cko -> Latn + {0xE1420000u, 40u}, // cky -> Latn + {0x81620000u, 40u}, // cla -> Latn + {0x91820000u, 40u}, // cme -> Latn + {0x636F0000u, 40u}, // co -> Latn {0xBDC20000u, 13u}, // cop -> Copt - {0xC9E20000u, 41u}, // cps -> Latn + {0xC9E20000u, 40u}, // cps -> Latn {0x63720000u, 9u}, // cr -> Cans {0xA6220000u, 9u}, // crj -> Cans {0xAA220000u, 9u}, // crk -> Cans {0xAE220000u, 9u}, // crl -> Cans {0xB2220000u, 9u}, // crm -> Cans - {0xCA220000u, 41u}, // crs -> Latn - {0x63730000u, 41u}, // cs -> Latn - {0x86420000u, 41u}, // csb -> Latn + {0xCA220000u, 40u}, // crs -> Latn + {0x63730000u, 40u}, // cs -> Latn + {0x86420000u, 40u}, // csb -> Latn {0xDA420000u, 9u}, // csw -> Cans {0x8E620000u, 59u}, // ctd -> Pauc {0x63750000u, 15u}, // cu -> Cyrl {0x63760000u, 15u}, // cv -> Cyrl - {0x63790000u, 41u}, // cy -> Latn - {0x64610000u, 41u}, // da -> Latn - {0xA8030000u, 41u}, // dak -> Latn + {0x63790000u, 40u}, // cy -> Latn + {0x64610000u, 40u}, // da -> Latn + {0x8C030000u, 40u}, // dad -> Latn + {0x94030000u, 40u}, // daf -> Latn + {0x98030000u, 40u}, // dag -> Latn + {0x9C030000u, 40u}, // dah -> Latn + {0xA8030000u, 40u}, // dak -> Latn {0xC4030000u, 15u}, // dar -> Cyrl - {0xD4030000u, 41u}, // dav -> Latn + {0xD4030000u, 40u}, // dav -> Latn + {0x8C230000u, 40u}, // dbd -> Latn + {0xC0230000u, 40u}, // dbq -> Latn {0x88430000u, 1u}, // dcc -> Arab - {0x64650000u, 41u}, // de -> Latn - {0xB4830000u, 41u}, // den -> Latn - {0xC4C30000u, 41u}, // dgr -> Latn - {0x91230000u, 41u}, // dje -> Latn - {0xA5A30000u, 41u}, // dnj -> Latn + {0xB4630000u, 40u}, // ddn -> Latn + {0x64650000u, 40u}, // de -> Latn + {0x8C830000u, 40u}, // ded -> Latn + {0xB4830000u, 40u}, // den -> Latn + {0x80C30000u, 40u}, // dga -> Latn + {0x9CC30000u, 40u}, // dgh -> Latn + {0xA0C30000u, 40u}, // dgi -> Latn + {0xACC30000u, 1u}, // dgl -> Arab + {0xC4C30000u, 40u}, // dgr -> Latn + {0xE4C30000u, 40u}, // dgz -> Latn + {0x81030000u, 40u}, // dia -> Latn + {0x91230000u, 40u}, // dje -> Latn + {0xA5A30000u, 40u}, // dnj -> Latn + {0x85C30000u, 40u}, // dob -> Latn {0xA1C30000u, 1u}, // doi -> Arab - {0x86430000u, 41u}, // dsb -> Latn - {0xB2630000u, 41u}, // dtm -> Latn - {0xBE630000u, 41u}, // dtp -> Latn - {0x82830000u, 41u}, // dua -> Latn - {0x64760000u, 78u}, // dv -> Thaa - {0xBB030000u, 41u}, // dyo -> Latn - {0xD3030000u, 41u}, // dyu -> Latn - {0x647A0000u, 80u}, // dz -> Tibt - {0xD0240000u, 41u}, // ebu -> Latn - {0x65650000u, 41u}, // ee -> Latn - {0xA0A40000u, 41u}, // efi -> Latn - {0xACC40000u, 41u}, // egl -> Latn + {0xBDC30000u, 40u}, // dop -> Latn + {0xD9C30000u, 40u}, // dow -> Latn + {0xA2230000u, 40u}, // dri -> Latn + {0xCA230000u, 18u}, // drs -> Ethi + {0x86430000u, 40u}, // dsb -> Latn + {0xB2630000u, 40u}, // dtm -> Latn + {0xBE630000u, 40u}, // dtp -> Latn + {0xCA630000u, 40u}, // dts -> Latn + {0xE2630000u, 16u}, // dty -> Deva + {0x82830000u, 40u}, // dua -> Latn + {0x8A830000u, 40u}, // duc -> Latn + {0x8E830000u, 40u}, // dud -> Latn + {0x9A830000u, 40u}, // dug -> Latn + {0x64760000u, 79u}, // dv -> Thaa + {0x82A30000u, 40u}, // dva -> Latn + {0xDAC30000u, 40u}, // dww -> Latn + {0xBB030000u, 40u}, // dyo -> Latn + {0xD3030000u, 40u}, // dyu -> Latn + {0x647A0000u, 81u}, // dz -> Tibt + {0x9B230000u, 40u}, // dzg -> Latn + {0xD0240000u, 40u}, // ebu -> Latn + {0x65650000u, 40u}, // ee -> Latn + {0xA0A40000u, 40u}, // efi -> Latn + {0xACC40000u, 40u}, // egl -> Latn {0xE0C40000u, 17u}, // egy -> Egyp {0xE1440000u, 32u}, // eky -> Kali {0x656C0000u, 21u}, // el -> Grek - {0x656E0000u, 41u}, // en -> Latn - {0x656E5841u, 86u}, // en-XA -> ~~~A - {0x656F0000u, 41u}, // eo -> Latn - {0x65730000u, 41u}, // es -> Latn - {0xD2440000u, 41u}, // esu -> Latn - {0x65740000u, 41u}, // et -> Latn + {0x81840000u, 40u}, // ema -> Latn + {0xA1840000u, 40u}, // emi -> Latn + {0x656E0000u, 40u}, // en -> Latn + {0x656E5841u, 87u}, // en-XA -> ~~~A + {0xB5A40000u, 40u}, // enn -> Latn + {0xC1A40000u, 40u}, // enq -> Latn + {0x656F0000u, 40u}, // eo -> Latn + {0xA2240000u, 40u}, // eri -> Latn + {0x65730000u, 40u}, // es -> Latn + {0xD2440000u, 40u}, // esu -> Latn + {0x65740000u, 40u}, // et -> Latn + {0xC6640000u, 40u}, // etr -> Latn {0xCE640000u, 30u}, // ett -> Ital - {0x65750000u, 41u}, // eu -> Latn - {0xBAC40000u, 41u}, // ewo -> Latn - {0xCEE40000u, 41u}, // ext -> Latn + {0xD2640000u, 40u}, // etu -> Latn + {0xDE640000u, 40u}, // etx -> Latn + {0x65750000u, 40u}, // eu -> Latn + {0xBAC40000u, 40u}, // ewo -> Latn + {0xCEE40000u, 40u}, // ext -> Latn {0x66610000u, 1u}, // fa -> Arab - {0xB4050000u, 41u}, // fan -> Latn - {0x66660000u, 41u}, // ff -> Latn - {0xB0A50000u, 41u}, // ffm -> Latn - {0x66690000u, 41u}, // fi -> Latn + {0x80050000u, 40u}, // faa -> Latn + {0x84050000u, 40u}, // fab -> Latn + {0x98050000u, 40u}, // fag -> Latn + {0xA0050000u, 40u}, // fai -> Latn + {0xB4050000u, 40u}, // fan -> Latn + {0x66660000u, 40u}, // ff -> Latn + {0xA0A50000u, 40u}, // ffi -> Latn + {0xB0A50000u, 40u}, // ffm -> Latn + {0x66690000u, 40u}, // fi -> Latn {0x81050000u, 1u}, // fia -> Arab - {0xAD050000u, 41u}, // fil -> Latn - {0xCD050000u, 41u}, // fit -> Latn - {0x666A0000u, 41u}, // fj -> Latn - {0x666F0000u, 41u}, // fo -> Latn - {0xB5C50000u, 41u}, // fon -> Latn - {0x66720000u, 41u}, // fr -> Latn - {0x8A250000u, 41u}, // frc -> Latn - {0xBE250000u, 41u}, // frp -> Latn - {0xC6250000u, 41u}, // frr -> Latn - {0xCA250000u, 41u}, // frs -> Latn - {0x8E850000u, 41u}, // fud -> Latn - {0xC2850000u, 41u}, // fuq -> Latn - {0xC6850000u, 41u}, // fur -> Latn - {0xD6850000u, 41u}, // fuv -> Latn - {0xC6A50000u, 41u}, // fvr -> Latn - {0x66790000u, 41u}, // fy -> Latn - {0x67610000u, 41u}, // ga -> Latn - {0x80060000u, 41u}, // gaa -> Latn - {0x98060000u, 41u}, // gag -> Latn + {0xAD050000u, 40u}, // fil -> Latn + {0xCD050000u, 40u}, // fit -> Latn + {0x666A0000u, 40u}, // fj -> Latn + {0xC5650000u, 40u}, // flr -> Latn + {0xBD850000u, 40u}, // fmp -> Latn + {0x666F0000u, 40u}, // fo -> Latn + {0x8DC50000u, 40u}, // fod -> Latn + {0xB5C50000u, 40u}, // fon -> Latn + {0xC5C50000u, 40u}, // for -> Latn + {0x91E50000u, 40u}, // fpe -> Latn + {0xCA050000u, 40u}, // fqs -> Latn + {0x66720000u, 40u}, // fr -> Latn + {0x8A250000u, 40u}, // frc -> Latn + {0xBE250000u, 40u}, // frp -> Latn + {0xC6250000u, 40u}, // frr -> Latn + {0xCA250000u, 40u}, // frs -> Latn + {0x86850000u, 1u}, // fub -> Arab + {0x8E850000u, 40u}, // fud -> Latn + {0x92850000u, 40u}, // fue -> Latn + {0x96850000u, 40u}, // fuf -> Latn + {0x9E850000u, 40u}, // fuh -> Latn + {0xC2850000u, 40u}, // fuq -> Latn + {0xC6850000u, 40u}, // fur -> Latn + {0xD6850000u, 40u}, // fuv -> Latn + {0xE2850000u, 40u}, // fuy -> Latn + {0xC6A50000u, 40u}, // fvr -> Latn + {0x66790000u, 40u}, // fy -> Latn + {0x67610000u, 40u}, // ga -> Latn + {0x80060000u, 40u}, // gaa -> Latn + {0x94060000u, 40u}, // gaf -> Latn + {0x98060000u, 40u}, // gag -> Latn + {0x9C060000u, 40u}, // gah -> Latn + {0xA4060000u, 40u}, // gaj -> Latn + {0xB0060000u, 40u}, // gam -> Latn {0xB4060000u, 24u}, // gan -> Hans - {0xE0060000u, 41u}, // gay -> Latn + {0xD8060000u, 40u}, // gaw -> Latn + {0xE0060000u, 40u}, // gay -> Latn + {0x94260000u, 40u}, // gbf -> Latn {0xB0260000u, 16u}, // gbm -> Deva + {0xE0260000u, 40u}, // gby -> Latn {0xE4260000u, 1u}, // gbz -> Arab - {0xC4460000u, 41u}, // gcr -> Latn - {0x67640000u, 41u}, // gd -> Latn + {0xC4460000u, 40u}, // gcr -> Latn + {0x67640000u, 40u}, // gd -> Latn + {0x90660000u, 40u}, // gde -> Latn + {0xB4660000u, 40u}, // gdn -> Latn + {0xC4660000u, 40u}, // gdr -> Latn + {0x84860000u, 40u}, // geb -> Latn + {0xA4860000u, 40u}, // gej -> Latn + {0xAC860000u, 40u}, // gel -> Latn {0xE4860000u, 18u}, // gez -> Ethi + {0xA8A60000u, 40u}, // gfk -> Latn {0xB4C60000u, 16u}, // ggn -> Deva - {0xAD060000u, 41u}, // gil -> Latn + {0xC8E60000u, 40u}, // ghs -> Latn + {0xAD060000u, 40u}, // gil -> Latn + {0xB1060000u, 40u}, // gim -> Latn {0xA9260000u, 1u}, // gjk -> Arab + {0xB5260000u, 40u}, // gjn -> Latn {0xD1260000u, 1u}, // gju -> Arab - {0x676C0000u, 41u}, // gl -> Latn + {0xB5460000u, 40u}, // gkn -> Latn + {0xBD460000u, 40u}, // gkp -> Latn + {0x676C0000u, 40u}, // gl -> Latn {0xA9660000u, 1u}, // glk -> Arab - {0x676E0000u, 41u}, // gn -> Latn + {0xB1860000u, 40u}, // gmm -> Latn + {0xD5860000u, 18u}, // gmv -> Ethi + {0x676E0000u, 40u}, // gn -> Latn + {0x8DA60000u, 40u}, // gnd -> Latn + {0x99A60000u, 40u}, // gng -> Latn + {0x8DC60000u, 40u}, // god -> Latn + {0x95C60000u, 18u}, // gof -> Ethi + {0xA1C60000u, 40u}, // goi -> Latn {0xB1C60000u, 16u}, // gom -> Deva - {0xB5C60000u, 76u}, // gon -> Telu - {0xC5C60000u, 41u}, // gor -> Latn - {0xC9C60000u, 41u}, // gos -> Latn + {0xB5C60000u, 77u}, // gon -> Telu + {0xC5C60000u, 40u}, // gor -> Latn + {0xC9C60000u, 40u}, // gos -> Latn {0xCDC60000u, 20u}, // got -> Goth {0x8A260000u, 14u}, // grc -> Cprt {0xCE260000u, 7u}, // grt -> Beng - {0xDA460000u, 41u}, // gsw -> Latn + {0xDA260000u, 40u}, // grw -> Latn + {0xDA460000u, 40u}, // gsw -> Latn {0x67750000u, 22u}, // gu -> Gujr - {0x86860000u, 41u}, // gub -> Latn - {0x8A860000u, 41u}, // guc -> Latn - {0xC6860000u, 41u}, // gur -> Latn - {0xE6860000u, 41u}, // guz -> Latn - {0x67760000u, 41u}, // gv -> Latn + {0x86860000u, 40u}, // gub -> Latn + {0x8A860000u, 40u}, // guc -> Latn + {0x8E860000u, 40u}, // gud -> Latn + {0xC6860000u, 40u}, // gur -> Latn + {0xDA860000u, 40u}, // guw -> Latn + {0xDE860000u, 40u}, // gux -> Latn + {0xE6860000u, 40u}, // guz -> Latn + {0x67760000u, 40u}, // gv -> Latn + {0x96A60000u, 40u}, // gvf -> Latn {0xC6A60000u, 16u}, // gvr -> Deva - {0xA2C60000u, 41u}, // gwi -> Latn - {0x68610000u, 41u}, // ha -> Latn + {0xCAA60000u, 40u}, // gvs -> Latn + {0x8AC60000u, 1u}, // gwc -> Arab + {0xA2C60000u, 40u}, // gwi -> Latn + {0xCEC60000u, 1u}, // gwt -> Arab + {0xA3060000u, 40u}, // gyi -> Latn + {0x68610000u, 40u}, // ha -> Latn {0x6861434Du, 1u}, // ha-CM -> Arab {0x68615344u, 1u}, // ha-SD -> Arab + {0x98070000u, 40u}, // hag -> Latn {0xA8070000u, 24u}, // hak -> Hans - {0xD8070000u, 41u}, // haw -> Latn + {0xB0070000u, 40u}, // ham -> Latn + {0xD8070000u, 40u}, // haw -> Latn {0xE4070000u, 1u}, // haz -> Arab + {0x84270000u, 40u}, // hbb -> Latn + {0xE0670000u, 18u}, // hdy -> Ethi {0x68650000u, 27u}, // he -> Hebr + {0xE0E70000u, 40u}, // hhy -> Latn {0x68690000u, 16u}, // hi -> Deva - {0x95070000u, 41u}, // hif -> Latn - {0xAD070000u, 41u}, // hil -> Latn + {0x81070000u, 40u}, // hia -> Latn + {0x95070000u, 40u}, // hif -> Latn + {0x99070000u, 40u}, // hig -> Latn + {0x9D070000u, 40u}, // hih -> Latn + {0xAD070000u, 40u}, // hil -> Latn + {0x81670000u, 40u}, // hla -> Latn {0xD1670000u, 28u}, // hlu -> Hluw {0x8D870000u, 62u}, // hmd -> Plrd + {0xCD870000u, 40u}, // hmt -> Latn {0x8DA70000u, 1u}, // hnd -> Arab {0x91A70000u, 16u}, // hne -> Deva {0xA5A70000u, 29u}, // hnj -> Hmng - {0xB5A70000u, 41u}, // hnn -> Latn + {0xB5A70000u, 40u}, // hnn -> Latn {0xB9A70000u, 1u}, // hno -> Arab - {0x686F0000u, 41u}, // ho -> Latn + {0x686F0000u, 40u}, // ho -> Latn {0x89C70000u, 16u}, // hoc -> Deva {0xA5C70000u, 16u}, // hoj -> Deva - {0x68720000u, 41u}, // hr -> Latn - {0x86470000u, 41u}, // hsb -> Latn + {0xCDC70000u, 40u}, // hot -> Latn + {0x68720000u, 40u}, // hr -> Latn + {0x86470000u, 40u}, // hsb -> Latn {0xB6470000u, 24u}, // hsn -> Hans - {0x68740000u, 41u}, // ht -> Latn - {0x68750000u, 41u}, // hu -> Latn + {0x68740000u, 40u}, // ht -> Latn + {0x68750000u, 40u}, // hu -> Latn + {0xA2870000u, 40u}, // hui -> Latn {0x68790000u, 3u}, // hy -> Armn - {0x687A0000u, 41u}, // hz -> Latn - {0x69610000u, 41u}, // ia -> Latn - {0x80280000u, 41u}, // iba -> Latn - {0x84280000u, 41u}, // ibb -> Latn - {0x69640000u, 41u}, // id -> Latn - {0x69670000u, 41u}, // ig -> Latn - {0x69690000u, 85u}, // ii -> Yiii - {0x696B0000u, 41u}, // ik -> Latn - {0xCD480000u, 41u}, // ikt -> Latn - {0xB9680000u, 41u}, // ilo -> Latn - {0x696E0000u, 41u}, // in -> Latn + {0x687A0000u, 40u}, // hz -> Latn + {0x69610000u, 40u}, // ia -> Latn + {0xB4080000u, 40u}, // ian -> Latn + {0xC4080000u, 40u}, // iar -> Latn + {0x80280000u, 40u}, // iba -> Latn + {0x84280000u, 40u}, // ibb -> Latn + {0xE0280000u, 40u}, // iby -> Latn + {0x80480000u, 40u}, // ica -> Latn + {0x9C480000u, 40u}, // ich -> Latn + {0x69640000u, 40u}, // id -> Latn + {0x8C680000u, 40u}, // idd -> Latn + {0xA0680000u, 40u}, // idi -> Latn + {0xD0680000u, 40u}, // idu -> Latn + {0x69670000u, 40u}, // ig -> Latn + {0x84C80000u, 40u}, // igb -> Latn + {0x90C80000u, 40u}, // ige -> Latn + {0x69690000u, 86u}, // ii -> Yiii + {0xA5280000u, 40u}, // ijj -> Latn + {0x696B0000u, 40u}, // ik -> Latn + {0xA9480000u, 40u}, // ikk -> Latn + {0xCD480000u, 40u}, // ikt -> Latn + {0xD9480000u, 40u}, // ikw -> Latn + {0xDD480000u, 40u}, // ikx -> Latn + {0xB9680000u, 40u}, // ilo -> Latn + {0xB9880000u, 40u}, // imo -> Latn + {0x696E0000u, 40u}, // in -> Latn {0x9DA80000u, 15u}, // inh -> Cyrl - {0x69730000u, 41u}, // is -> Latn - {0x69740000u, 41u}, // it -> Latn + {0xD1C80000u, 40u}, // iou -> Latn + {0xA2280000u, 40u}, // iri -> Latn + {0x69730000u, 40u}, // is -> Latn + {0x69740000u, 40u}, // it -> Latn {0x69750000u, 9u}, // iu -> Cans {0x69770000u, 27u}, // iw -> Hebr - {0x9F280000u, 41u}, // izh -> Latn + {0xB2C80000u, 40u}, // iwm -> Latn + {0xCAC80000u, 40u}, // iws -> Latn + {0x9F280000u, 40u}, // izh -> Latn + {0xA3280000u, 40u}, // izi -> Latn {0x6A610000u, 31u}, // ja -> Jpan - {0xB0090000u, 41u}, // jam -> Latn - {0xB8C90000u, 41u}, // jgo -> Latn + {0x84090000u, 40u}, // jab -> Latn + {0xB0090000u, 40u}, // jam -> Latn + {0xD0290000u, 40u}, // jbu -> Latn + {0xB4890000u, 40u}, // jen -> Latn + {0xA8C90000u, 40u}, // jgk -> Latn + {0xB8C90000u, 40u}, // jgo -> Latn {0x6A690000u, 27u}, // ji -> Hebr - {0x89890000u, 41u}, // jmc -> Latn + {0x85090000u, 40u}, // jib -> Latn + {0x89890000u, 40u}, // jmc -> Latn {0xAD890000u, 16u}, // jml -> Deva - {0xCE890000u, 41u}, // jut -> Latn - {0x6A760000u, 41u}, // jv -> Latn - {0x6A770000u, 41u}, // jw -> Latn + {0x82290000u, 40u}, // jra -> Latn + {0xCE890000u, 40u}, // jut -> Latn + {0x6A760000u, 40u}, // jv -> Latn + {0x6A770000u, 40u}, // jw -> Latn {0x6B610000u, 19u}, // ka -> Geor {0x800A0000u, 15u}, // kaa -> Cyrl - {0x840A0000u, 41u}, // kab -> Latn - {0x880A0000u, 41u}, // kac -> Latn - {0xA40A0000u, 41u}, // kaj -> Latn - {0xB00A0000u, 41u}, // kam -> Latn - {0xB80A0000u, 41u}, // kao -> Latn + {0x840A0000u, 40u}, // kab -> Latn + {0x880A0000u, 40u}, // kac -> Latn + {0x8C0A0000u, 40u}, // kad -> Latn + {0xA00A0000u, 40u}, // kai -> Latn + {0xA40A0000u, 40u}, // kaj -> Latn + {0xB00A0000u, 40u}, // kam -> Latn + {0xB80A0000u, 40u}, // kao -> Latn {0x8C2A0000u, 15u}, // kbd -> Cyrl - {0x984A0000u, 41u}, // kcg -> Latn - {0xA84A0000u, 41u}, // kck -> Latn - {0x906A0000u, 41u}, // kde -> Latn - {0xCC6A0000u, 79u}, // kdt -> Thai - {0x808A0000u, 41u}, // kea -> Latn - {0xB48A0000u, 41u}, // ken -> Latn - {0xB8AA0000u, 41u}, // kfo -> Latn + {0xB02A0000u, 40u}, // kbm -> Latn + {0xBC2A0000u, 40u}, // kbp -> Latn + {0xC02A0000u, 40u}, // kbq -> Latn + {0xDC2A0000u, 40u}, // kbx -> Latn + {0xE02A0000u, 1u}, // kby -> Arab + {0x984A0000u, 40u}, // kcg -> Latn + {0xA84A0000u, 40u}, // kck -> Latn + {0xAC4A0000u, 40u}, // kcl -> Latn + {0xCC4A0000u, 40u}, // kct -> Latn + {0x906A0000u, 40u}, // kde -> Latn + {0x9C6A0000u, 1u}, // kdh -> Arab + {0xAC6A0000u, 40u}, // kdl -> Latn + {0xCC6A0000u, 80u}, // kdt -> Thai + {0x808A0000u, 40u}, // kea -> Latn + {0xB48A0000u, 40u}, // ken -> Latn + {0xE48A0000u, 40u}, // kez -> Latn + {0xB8AA0000u, 40u}, // kfo -> Latn {0xC4AA0000u, 16u}, // kfr -> Deva {0xE0AA0000u, 16u}, // kfy -> Deva - {0x6B670000u, 41u}, // kg -> Latn - {0x90CA0000u, 41u}, // kge -> Latn - {0xBCCA0000u, 41u}, // kgp -> Latn - {0x80EA0000u, 41u}, // kha -> Latn + {0x6B670000u, 40u}, // kg -> Latn + {0x90CA0000u, 40u}, // kge -> Latn + {0x94CA0000u, 40u}, // kgf -> Latn + {0xBCCA0000u, 40u}, // kgp -> Latn + {0x80EA0000u, 40u}, // kha -> Latn {0x84EA0000u, 73u}, // khb -> Talu {0xB4EA0000u, 16u}, // khn -> Deva - {0xC0EA0000u, 41u}, // khq -> Latn - {0xCCEA0000u, 53u}, // kht -> Mymr + {0xC0EA0000u, 40u}, // khq -> Latn + {0xC8EA0000u, 40u}, // khs -> Latn + {0xCCEA0000u, 52u}, // kht -> Mymr {0xD8EA0000u, 1u}, // khw -> Arab - {0x6B690000u, 41u}, // ki -> Latn - {0xD10A0000u, 41u}, // kiu -> Latn - {0x6B6A0000u, 41u}, // kj -> Latn - {0x992A0000u, 40u}, // kjg -> Laoo + {0xE4EA0000u, 40u}, // khz -> Latn + {0x6B690000u, 40u}, // ki -> Latn + {0xA50A0000u, 40u}, // kij -> Latn + {0xD10A0000u, 40u}, // kiu -> Latn + {0xD90A0000u, 40u}, // kiw -> Latn + {0x6B6A0000u, 40u}, // kj -> Latn + {0x8D2A0000u, 40u}, // kjd -> Latn + {0x992A0000u, 39u}, // kjg -> Laoo + {0xC92A0000u, 40u}, // kjs -> Latn + {0xE12A0000u, 40u}, // kjy -> Latn {0x6B6B0000u, 15u}, // kk -> Cyrl {0x6B6B4146u, 1u}, // kk-AF -> Arab {0x6B6B434Eu, 1u}, // kk-CN -> Arab {0x6B6B4952u, 1u}, // kk-IR -> Arab {0x6B6B4D4Eu, 1u}, // kk-MN -> Arab - {0xA54A0000u, 41u}, // kkj -> Latn - {0x6B6C0000u, 41u}, // kl -> Latn - {0xB56A0000u, 41u}, // kln -> Latn + {0x894A0000u, 40u}, // kkc -> Latn + {0xA54A0000u, 40u}, // kkj -> Latn + {0x6B6C0000u, 40u}, // kl -> Latn + {0xB56A0000u, 40u}, // kln -> Latn + {0xC16A0000u, 40u}, // klq -> Latn + {0xCD6A0000u, 40u}, // klt -> Latn + {0xDD6A0000u, 40u}, // klx -> Latn {0x6B6D0000u, 35u}, // km -> Khmr - {0x858A0000u, 41u}, // kmb -> Latn + {0x858A0000u, 40u}, // kmb -> Latn + {0x9D8A0000u, 40u}, // kmh -> Latn + {0xB98A0000u, 40u}, // kmo -> Latn + {0xC98A0000u, 40u}, // kms -> Latn + {0xD18A0000u, 40u}, // kmu -> Latn + {0xD98A0000u, 40u}, // kmw -> Latn {0x6B6E0000u, 36u}, // kn -> Knda + {0xBDAA0000u, 40u}, // knp -> Latn {0x6B6F0000u, 37u}, // ko -> Kore {0xA1CA0000u, 15u}, // koi -> Cyrl {0xA9CA0000u, 16u}, // kok -> Deva - {0xC9CA0000u, 41u}, // kos -> Latn - {0x91EA0000u, 41u}, // kpe -> Latn + {0xADCA0000u, 40u}, // kol -> Latn + {0xC9CA0000u, 40u}, // kos -> Latn + {0xE5CA0000u, 40u}, // koz -> Latn + {0x91EA0000u, 40u}, // kpe -> Latn + {0x95EA0000u, 40u}, // kpf -> Latn + {0xB9EA0000u, 40u}, // kpo -> Latn + {0xC5EA0000u, 40u}, // kpr -> Latn + {0xDDEA0000u, 40u}, // kpx -> Latn + {0x860A0000u, 40u}, // kqb -> Latn + {0x960A0000u, 40u}, // kqf -> Latn + {0xCA0A0000u, 40u}, // kqs -> Latn + {0xE20A0000u, 18u}, // kqy -> Ethi {0x8A2A0000u, 15u}, // krc -> Cyrl - {0xA22A0000u, 41u}, // kri -> Latn - {0xA62A0000u, 41u}, // krj -> Latn - {0xAE2A0000u, 41u}, // krl -> Latn + {0xA22A0000u, 40u}, // kri -> Latn + {0xA62A0000u, 40u}, // krj -> Latn + {0xAE2A0000u, 40u}, // krl -> Latn + {0xCA2A0000u, 40u}, // krs -> Latn {0xD22A0000u, 16u}, // kru -> Deva {0x6B730000u, 1u}, // ks -> Arab - {0x864A0000u, 41u}, // ksb -> Latn - {0x964A0000u, 41u}, // ksf -> Latn - {0x9E4A0000u, 41u}, // ksh -> Latn - {0x6B750000u, 41u}, // ku -> Latn + {0x864A0000u, 40u}, // ksb -> Latn + {0x8E4A0000u, 40u}, // ksd -> Latn + {0x964A0000u, 40u}, // ksf -> Latn + {0x9E4A0000u, 40u}, // ksh -> Latn + {0xA64A0000u, 40u}, // ksj -> Latn + {0xC64A0000u, 40u}, // ksr -> Latn + {0x866A0000u, 18u}, // ktb -> Ethi + {0xB26A0000u, 40u}, // ktm -> Latn + {0xBA6A0000u, 40u}, // kto -> Latn + {0x6B750000u, 40u}, // ku -> Latn {0x6B754952u, 1u}, // ku-IR -> Arab {0x6B754C42u, 1u}, // ku-LB -> Arab + {0x868A0000u, 40u}, // kub -> Latn + {0x8E8A0000u, 40u}, // kud -> Latn + {0x928A0000u, 40u}, // kue -> Latn + {0xA68A0000u, 40u}, // kuj -> Latn {0xB28A0000u, 15u}, // kum -> Cyrl + {0xB68A0000u, 40u}, // kun -> Latn + {0xBE8A0000u, 40u}, // kup -> Latn + {0xCA8A0000u, 40u}, // kus -> Latn {0x6B760000u, 15u}, // kv -> Cyrl - {0xC6AA0000u, 41u}, // kvr -> Latn + {0x9AAA0000u, 40u}, // kvg -> Latn + {0xC6AA0000u, 40u}, // kvr -> Latn {0xDEAA0000u, 1u}, // kvx -> Arab - {0x6B770000u, 41u}, // kw -> Latn - {0xB2EA0000u, 79u}, // kxm -> Thai + {0x6B770000u, 40u}, // kw -> Latn + {0xA6CA0000u, 40u}, // kwj -> Latn + {0xBACA0000u, 40u}, // kwo -> Latn + {0x82EA0000u, 40u}, // kxa -> Latn + {0x8AEA0000u, 18u}, // kxc -> Ethi + {0xB2EA0000u, 80u}, // kxm -> Thai {0xBEEA0000u, 1u}, // kxp -> Arab + {0xDAEA0000u, 40u}, // kxw -> Latn + {0xE6EA0000u, 40u}, // kxz -> Latn {0x6B790000u, 15u}, // ky -> Cyrl {0x6B79434Eu, 1u}, // ky-CN -> Arab - {0x6B795452u, 41u}, // ky-TR -> Latn - {0x6C610000u, 41u}, // la -> Latn - {0x840B0000u, 43u}, // lab -> Lina + {0x6B795452u, 40u}, // ky-TR -> Latn + {0x930A0000u, 40u}, // kye -> Latn + {0xDF0A0000u, 40u}, // kyx -> Latn + {0xC72A0000u, 40u}, // kzr -> Latn + {0x6C610000u, 40u}, // la -> Latn + {0x840B0000u, 42u}, // lab -> Lina {0x8C0B0000u, 27u}, // lad -> Hebr - {0x980B0000u, 41u}, // lag -> Latn + {0x980B0000u, 40u}, // lag -> Latn {0x9C0B0000u, 1u}, // lah -> Arab - {0xA40B0000u, 41u}, // laj -> Latn - {0x6C620000u, 41u}, // lb -> Latn + {0xA40B0000u, 40u}, // laj -> Latn + {0xC80B0000u, 40u}, // las -> Latn + {0x6C620000u, 40u}, // lb -> Latn {0x902B0000u, 15u}, // lbe -> Cyrl - {0xD82B0000u, 41u}, // lbw -> Latn - {0xBC4B0000u, 79u}, // lcp -> Thai - {0xBC8B0000u, 42u}, // lep -> Lepc + {0xD02B0000u, 40u}, // lbu -> Latn + {0xD82B0000u, 40u}, // lbw -> Latn + {0xB04B0000u, 40u}, // lcm -> Latn + {0xBC4B0000u, 80u}, // lcp -> Thai + {0x846B0000u, 40u}, // ldb -> Latn + {0x8C8B0000u, 40u}, // led -> Latn + {0x908B0000u, 40u}, // lee -> Latn + {0xB08B0000u, 40u}, // lem -> Latn + {0xBC8B0000u, 41u}, // lep -> Lepc + {0xC08B0000u, 40u}, // leq -> Latn + {0xD08B0000u, 40u}, // leu -> Latn {0xE48B0000u, 15u}, // lez -> Cyrl - {0x6C670000u, 41u}, // lg -> Latn - {0x6C690000u, 41u}, // li -> Latn + {0x6C670000u, 40u}, // lg -> Latn + {0x98CB0000u, 40u}, // lgg -> Latn + {0x6C690000u, 40u}, // li -> Latn + {0x810B0000u, 40u}, // lia -> Latn + {0x8D0B0000u, 40u}, // lid -> Latn {0x950B0000u, 16u}, // lif -> Deva - {0xA50B0000u, 41u}, // lij -> Latn - {0xC90B0000u, 44u}, // lis -> Lisu - {0xBD2B0000u, 41u}, // ljp -> Latn + {0x990B0000u, 40u}, // lig -> Latn + {0x9D0B0000u, 40u}, // lih -> Latn + {0xA50B0000u, 40u}, // lij -> Latn + {0xC90B0000u, 43u}, // lis -> Lisu + {0xBD2B0000u, 40u}, // ljp -> Latn {0xA14B0000u, 1u}, // lki -> Arab - {0xCD4B0000u, 41u}, // lkt -> Latn - {0xB58B0000u, 76u}, // lmn -> Telu - {0xB98B0000u, 41u}, // lmo -> Latn - {0x6C6E0000u, 41u}, // ln -> Latn - {0x6C6F0000u, 40u}, // lo -> Laoo - {0xADCB0000u, 41u}, // lol -> Latn - {0xE5CB0000u, 41u}, // loz -> Latn + {0xCD4B0000u, 40u}, // lkt -> Latn + {0x916B0000u, 40u}, // lle -> Latn + {0xB56B0000u, 40u}, // lln -> Latn + {0xB58B0000u, 77u}, // lmn -> Telu + {0xB98B0000u, 40u}, // lmo -> Latn + {0xBD8B0000u, 40u}, // lmp -> Latn + {0x6C6E0000u, 40u}, // ln -> Latn + {0xC9AB0000u, 40u}, // lns -> Latn + {0xD1AB0000u, 40u}, // lnu -> Latn + {0x6C6F0000u, 39u}, // lo -> Laoo + {0xA5CB0000u, 40u}, // loj -> Latn + {0xA9CB0000u, 40u}, // lok -> Latn + {0xADCB0000u, 40u}, // lol -> Latn + {0xC5CB0000u, 40u}, // lor -> Latn + {0xC9CB0000u, 40u}, // los -> Latn + {0xE5CB0000u, 40u}, // loz -> Latn {0x8A2B0000u, 1u}, // lrc -> Arab - {0x6C740000u, 41u}, // lt -> Latn - {0x9A6B0000u, 41u}, // ltg -> Latn - {0x6C750000u, 41u}, // lu -> Latn - {0x828B0000u, 41u}, // lua -> Latn - {0xBA8B0000u, 41u}, // luo -> Latn - {0xE28B0000u, 41u}, // luy -> Latn + {0x6C740000u, 40u}, // lt -> Latn + {0x9A6B0000u, 40u}, // ltg -> Latn + {0x6C750000u, 40u}, // lu -> Latn + {0x828B0000u, 40u}, // lua -> Latn + {0xBA8B0000u, 40u}, // luo -> Latn + {0xE28B0000u, 40u}, // luy -> Latn {0xE68B0000u, 1u}, // luz -> Arab - {0x6C760000u, 41u}, // lv -> Latn - {0xAECB0000u, 79u}, // lwl -> Thai + {0x6C760000u, 40u}, // lv -> Latn + {0xAECB0000u, 80u}, // lwl -> Thai {0x9F2B0000u, 24u}, // lzh -> Hans - {0xE72B0000u, 41u}, // lzz -> Latn - {0x8C0C0000u, 41u}, // mad -> Latn - {0x940C0000u, 41u}, // maf -> Latn + {0xE72B0000u, 40u}, // lzz -> Latn + {0x8C0C0000u, 40u}, // mad -> Latn + {0x940C0000u, 40u}, // maf -> Latn {0x980C0000u, 16u}, // mag -> Deva {0xA00C0000u, 16u}, // mai -> Deva - {0xA80C0000u, 41u}, // mak -> Latn - {0xB40C0000u, 41u}, // man -> Latn - {0xB40C474Eu, 55u}, // man-GN -> Nkoo - {0xC80C0000u, 41u}, // mas -> Latn - {0xE40C0000u, 41u}, // maz -> Latn + {0xA80C0000u, 40u}, // mak -> Latn + {0xB40C0000u, 40u}, // man -> Latn + {0xB40C474Eu, 54u}, // man-GN -> Nkoo + {0xC80C0000u, 40u}, // mas -> Latn + {0xD80C0000u, 40u}, // maw -> Latn + {0xE40C0000u, 40u}, // maz -> Latn + {0x9C2C0000u, 40u}, // mbh -> Latn + {0xB82C0000u, 40u}, // mbo -> Latn + {0xC02C0000u, 40u}, // mbq -> Latn + {0xD02C0000u, 40u}, // mbu -> Latn + {0xD82C0000u, 40u}, // mbw -> Latn + {0xA04C0000u, 40u}, // mci -> Latn + {0xBC4C0000u, 40u}, // mcp -> Latn + {0xC04C0000u, 40u}, // mcq -> Latn + {0xC44C0000u, 40u}, // mcr -> Latn + {0xD04C0000u, 40u}, // mcu -> Latn + {0x806C0000u, 40u}, // mda -> Latn + {0x906C0000u, 1u}, // mde -> Arab {0x946C0000u, 15u}, // mdf -> Cyrl - {0x9C6C0000u, 41u}, // mdh -> Latn - {0xC46C0000u, 41u}, // mdr -> Latn - {0xB48C0000u, 41u}, // men -> Latn - {0xC48C0000u, 41u}, // mer -> Latn + {0x9C6C0000u, 40u}, // mdh -> Latn + {0xA46C0000u, 40u}, // mdj -> Latn + {0xC46C0000u, 40u}, // mdr -> Latn + {0xDC6C0000u, 18u}, // mdx -> Ethi + {0x8C8C0000u, 40u}, // med -> Latn + {0x908C0000u, 40u}, // mee -> Latn + {0xA88C0000u, 40u}, // mek -> Latn + {0xB48C0000u, 40u}, // men -> Latn + {0xC48C0000u, 40u}, // mer -> Latn + {0xCC8C0000u, 40u}, // met -> Latn + {0xD08C0000u, 40u}, // meu -> Latn {0x80AC0000u, 1u}, // mfa -> Arab - {0x90AC0000u, 41u}, // mfe -> Latn - {0x6D670000u, 41u}, // mg -> Latn - {0x9CCC0000u, 41u}, // mgh -> Latn - {0xB8CC0000u, 41u}, // mgo -> Latn + {0x90AC0000u, 40u}, // mfe -> Latn + {0xB4AC0000u, 40u}, // mfn -> Latn + {0xB8AC0000u, 40u}, // mfo -> Latn + {0xC0AC0000u, 40u}, // mfq -> Latn + {0x6D670000u, 40u}, // mg -> Latn + {0x9CCC0000u, 40u}, // mgh -> Latn + {0xACCC0000u, 40u}, // mgl -> Latn + {0xB8CC0000u, 40u}, // mgo -> Latn {0xBCCC0000u, 16u}, // mgp -> Deva - {0xE0CC0000u, 41u}, // mgy -> Latn - {0x6D680000u, 41u}, // mh -> Latn - {0x6D690000u, 41u}, // mi -> Latn - {0xB50C0000u, 41u}, // min -> Latn + {0xE0CC0000u, 40u}, // mgy -> Latn + {0x6D680000u, 40u}, // mh -> Latn + {0xA0EC0000u, 40u}, // mhi -> Latn + {0xACEC0000u, 40u}, // mhl -> Latn + {0x6D690000u, 40u}, // mi -> Latn + {0x950C0000u, 40u}, // mif -> Latn + {0xB50C0000u, 40u}, // min -> Latn {0xC90C0000u, 26u}, // mis -> Hatr + {0xD90C0000u, 40u}, // miw -> Latn {0x6D6B0000u, 15u}, // mk -> Cyrl - {0x6D6C0000u, 50u}, // ml -> Mlym - {0xC96C0000u, 41u}, // mls -> Latn + {0xA14C0000u, 1u}, // mki -> Arab + {0xAD4C0000u, 40u}, // mkl -> Latn + {0xBD4C0000u, 40u}, // mkp -> Latn + {0xD94C0000u, 40u}, // mkw -> Latn + {0x6D6C0000u, 49u}, // ml -> Mlym + {0x916C0000u, 40u}, // mle -> Latn + {0xBD6C0000u, 40u}, // mlp -> Latn + {0xC96C0000u, 40u}, // mls -> Latn + {0xB98C0000u, 40u}, // mmo -> Latn + {0xD18C0000u, 40u}, // mmu -> Latn + {0xDD8C0000u, 40u}, // mmx -> Latn {0x6D6E0000u, 15u}, // mn -> Cyrl - {0x6D6E434Eu, 51u}, // mn-CN -> Mong + {0x6D6E434Eu, 50u}, // mn-CN -> Mong + {0x81AC0000u, 40u}, // mna -> Latn + {0x95AC0000u, 40u}, // mnf -> Latn {0xA1AC0000u, 7u}, // mni -> Beng - {0xD9AC0000u, 53u}, // mnw -> Mymr - {0x91CC0000u, 41u}, // moe -> Latn - {0x9DCC0000u, 41u}, // moh -> Latn - {0xC9CC0000u, 41u}, // mos -> Latn + {0xD9AC0000u, 52u}, // mnw -> Mymr + {0x81CC0000u, 40u}, // moa -> Latn + {0x91CC0000u, 40u}, // moe -> Latn + {0x9DCC0000u, 40u}, // moh -> Latn + {0xC9CC0000u, 40u}, // mos -> Latn + {0xDDCC0000u, 40u}, // mox -> Latn + {0xBDEC0000u, 40u}, // mpp -> Latn + {0xC9EC0000u, 40u}, // mps -> Latn + {0xCDEC0000u, 40u}, // mpt -> Latn + {0xDDEC0000u, 40u}, // mpx -> Latn + {0xAE0C0000u, 40u}, // mql -> Latn {0x6D720000u, 16u}, // mr -> Deva {0x8E2C0000u, 16u}, // mrd -> Deva {0xA62C0000u, 15u}, // mrj -> Cyrl - {0xD22C0000u, 52u}, // mru -> Mroo - {0x6D730000u, 41u}, // ms -> Latn + {0xBA2C0000u, 51u}, // mro -> Mroo + {0x6D730000u, 40u}, // ms -> Latn {0x6D734343u, 1u}, // ms-CC -> Arab {0x6D734944u, 1u}, // ms-ID -> Arab - {0x6D740000u, 41u}, // mt -> Latn + {0x6D740000u, 40u}, // mt -> Latn + {0x8A6C0000u, 40u}, // mtc -> Latn + {0x966C0000u, 40u}, // mtf -> Latn + {0xA26C0000u, 40u}, // mti -> Latn {0xC66C0000u, 16u}, // mtr -> Deva - {0x828C0000u, 41u}, // mua -> Latn - {0xCA8C0000u, 41u}, // mus -> Latn + {0x828C0000u, 40u}, // mua -> Latn + {0xC68C0000u, 40u}, // mur -> Latn + {0xCA8C0000u, 40u}, // mus -> Latn + {0x82AC0000u, 40u}, // mva -> Latn + {0xB6AC0000u, 40u}, // mvn -> Latn {0xE2AC0000u, 1u}, // mvy -> Arab - {0xAACC0000u, 41u}, // mwk -> Latn + {0xAACC0000u, 40u}, // mwk -> Latn {0xC6CC0000u, 16u}, // mwr -> Deva - {0xD6CC0000u, 41u}, // mwv -> Latn - {0x8AEC0000u, 41u}, // mxc -> Latn - {0x6D790000u, 53u}, // my -> Mymr + {0xD6CC0000u, 40u}, // mwv -> Latn + {0x8AEC0000u, 40u}, // mxc -> Latn + {0xB2EC0000u, 40u}, // mxm -> Latn + {0x6D790000u, 52u}, // my -> Mymr + {0xAB0C0000u, 40u}, // myk -> Latn + {0xB30C0000u, 18u}, // mym -> Ethi {0xD70C0000u, 15u}, // myv -> Cyrl - {0xDF0C0000u, 41u}, // myx -> Latn - {0xE70C0000u, 47u}, // myz -> Mand + {0xDB0C0000u, 40u}, // myw -> Latn + {0xDF0C0000u, 40u}, // myx -> Latn + {0xE70C0000u, 46u}, // myz -> Mand + {0xAB2C0000u, 40u}, // mzk -> Latn + {0xB32C0000u, 40u}, // mzm -> Latn {0xB72C0000u, 1u}, // mzn -> Arab - {0x6E610000u, 41u}, // na -> Latn + {0xBF2C0000u, 40u}, // mzp -> Latn + {0xDB2C0000u, 40u}, // mzw -> Latn + {0xE72C0000u, 40u}, // mzz -> Latn + {0x6E610000u, 40u}, // na -> Latn + {0x880D0000u, 40u}, // nac -> Latn + {0x940D0000u, 40u}, // naf -> Latn + {0xA80D0000u, 40u}, // nak -> Latn {0xB40D0000u, 24u}, // nan -> Hans - {0xBC0D0000u, 41u}, // nap -> Latn - {0xC00D0000u, 41u}, // naq -> Latn - {0x6E620000u, 41u}, // nb -> Latn - {0x9C4D0000u, 41u}, // nch -> Latn - {0x6E640000u, 41u}, // nd -> Latn - {0x886D0000u, 41u}, // ndc -> Latn - {0xC86D0000u, 41u}, // nds -> Latn + {0xBC0D0000u, 40u}, // nap -> Latn + {0xC00D0000u, 40u}, // naq -> Latn + {0xC80D0000u, 40u}, // nas -> Latn + {0x6E620000u, 40u}, // nb -> Latn + {0x804D0000u, 40u}, // nca -> Latn + {0x904D0000u, 40u}, // nce -> Latn + {0x944D0000u, 40u}, // ncf -> Latn + {0x9C4D0000u, 40u}, // nch -> Latn + {0xB84D0000u, 40u}, // nco -> Latn + {0xD04D0000u, 40u}, // ncu -> Latn + {0x6E640000u, 40u}, // nd -> Latn + {0x886D0000u, 40u}, // ndc -> Latn + {0xC86D0000u, 40u}, // nds -> Latn {0x6E650000u, 16u}, // ne -> Deva + {0x848D0000u, 40u}, // neb -> Latn {0xD88D0000u, 16u}, // new -> Deva - {0x6E670000u, 41u}, // ng -> Latn - {0xACCD0000u, 41u}, // ngl -> Latn - {0x90ED0000u, 41u}, // nhe -> Latn - {0xD8ED0000u, 41u}, // nhw -> Latn - {0xA50D0000u, 41u}, // nij -> Latn - {0xD10D0000u, 41u}, // niu -> Latn - {0xB92D0000u, 41u}, // njo -> Latn - {0x6E6C0000u, 41u}, // nl -> Latn - {0x998D0000u, 41u}, // nmg -> Latn - {0x6E6E0000u, 41u}, // nn -> Latn - {0x9DAD0000u, 41u}, // nnh -> Latn - {0x6E6F0000u, 41u}, // no -> Latn - {0x8DCD0000u, 39u}, // nod -> Lana + {0xDC8D0000u, 40u}, // nex -> Latn + {0xC4AD0000u, 40u}, // nfr -> Latn + {0x6E670000u, 40u}, // ng -> Latn + {0x80CD0000u, 40u}, // nga -> Latn + {0x84CD0000u, 40u}, // ngb -> Latn + {0xACCD0000u, 40u}, // ngl -> Latn + {0x84ED0000u, 40u}, // nhb -> Latn + {0x90ED0000u, 40u}, // nhe -> Latn + {0xD8ED0000u, 40u}, // nhw -> Latn + {0x950D0000u, 40u}, // nif -> Latn + {0xA10D0000u, 40u}, // nii -> Latn + {0xA50D0000u, 40u}, // nij -> Latn + {0xB50D0000u, 40u}, // nin -> Latn + {0xD10D0000u, 40u}, // niu -> Latn + {0xE10D0000u, 40u}, // niy -> Latn + {0xE50D0000u, 40u}, // niz -> Latn + {0xB92D0000u, 40u}, // njo -> Latn + {0x994D0000u, 40u}, // nkg -> Latn + {0xB94D0000u, 40u}, // nko -> Latn + {0x6E6C0000u, 40u}, // nl -> Latn + {0x998D0000u, 40u}, // nmg -> Latn + {0xE58D0000u, 40u}, // nmz -> Latn + {0x6E6E0000u, 40u}, // nn -> Latn + {0x95AD0000u, 40u}, // nnf -> Latn + {0x9DAD0000u, 40u}, // nnh -> Latn + {0xA9AD0000u, 40u}, // nnk -> Latn + {0xB1AD0000u, 40u}, // nnm -> Latn + {0x6E6F0000u, 40u}, // no -> Latn + {0x8DCD0000u, 38u}, // nod -> Lana {0x91CD0000u, 16u}, // noe -> Deva {0xB5CD0000u, 64u}, // non -> Runr - {0xBA0D0000u, 55u}, // nqo -> Nkoo - {0x6E720000u, 41u}, // nr -> Latn + {0xBDCD0000u, 40u}, // nop -> Latn + {0xD1CD0000u, 40u}, // nou -> Latn + {0xBA0D0000u, 54u}, // nqo -> Nkoo + {0x6E720000u, 40u}, // nr -> Latn + {0x862D0000u, 40u}, // nrb -> Latn {0xAA4D0000u, 9u}, // nsk -> Cans - {0xBA4D0000u, 41u}, // nso -> Latn - {0xCA8D0000u, 41u}, // nus -> Latn - {0x6E760000u, 41u}, // nv -> Latn - {0xC2ED0000u, 41u}, // nxq -> Latn - {0x6E790000u, 41u}, // ny -> Latn - {0xB30D0000u, 41u}, // nym -> Latn - {0xB70D0000u, 41u}, // nyn -> Latn - {0xA32D0000u, 41u}, // nzi -> Latn - {0x6F630000u, 41u}, // oc -> Latn - {0x6F6D0000u, 41u}, // om -> Latn - {0x6F720000u, 58u}, // or -> Orya + {0xB64D0000u, 40u}, // nsn -> Latn + {0xBA4D0000u, 40u}, // nso -> Latn + {0xCA4D0000u, 40u}, // nss -> Latn + {0xB26D0000u, 40u}, // ntm -> Latn + {0xC66D0000u, 40u}, // ntr -> Latn + {0xA28D0000u, 40u}, // nui -> Latn + {0xBE8D0000u, 40u}, // nup -> Latn + {0xCA8D0000u, 40u}, // nus -> Latn + {0xD68D0000u, 40u}, // nuv -> Latn + {0xDE8D0000u, 40u}, // nux -> Latn + {0x6E760000u, 40u}, // nv -> Latn + {0x86CD0000u, 40u}, // nwb -> Latn + {0xC2ED0000u, 40u}, // nxq -> Latn + {0xC6ED0000u, 40u}, // nxr -> Latn + {0x6E790000u, 40u}, // ny -> Latn + {0xB30D0000u, 40u}, // nym -> Latn + {0xB70D0000u, 40u}, // nyn -> Latn + {0xA32D0000u, 40u}, // nzi -> Latn + {0x6F630000u, 40u}, // oc -> Latn + {0x88CE0000u, 40u}, // ogc -> Latn + {0xC54E0000u, 40u}, // okr -> Latn + {0xD54E0000u, 40u}, // okv -> Latn + {0x6F6D0000u, 40u}, // om -> Latn + {0x99AE0000u, 40u}, // ong -> Latn + {0xB5AE0000u, 40u}, // onn -> Latn + {0xC9AE0000u, 40u}, // ons -> Latn + {0xB1EE0000u, 40u}, // opm -> Latn + {0x6F720000u, 57u}, // or -> Orya + {0xBA2E0000u, 40u}, // oro -> Latn + {0xD22E0000u, 1u}, // oru -> Arab {0x6F730000u, 15u}, // os -> Cyrl - {0xAA6E0000u, 57u}, // otk -> Orkh + {0x824E0000u, 58u}, // osa -> Osge + {0x826E0000u, 1u}, // ota -> Arab + {0xAA6E0000u, 56u}, // otk -> Orkh + {0xB32E0000u, 40u}, // ozm -> Latn {0x70610000u, 23u}, // pa -> Guru {0x7061504Bu, 1u}, // pa-PK -> Arab - {0x980F0000u, 41u}, // pag -> Latn + {0x980F0000u, 40u}, // pag -> Latn {0xAC0F0000u, 60u}, // pal -> Phli - {0xB00F0000u, 41u}, // pam -> Latn - {0xBC0F0000u, 41u}, // pap -> Latn - {0xD00F0000u, 41u}, // pau -> Latn - {0x8C4F0000u, 41u}, // pcd -> Latn - {0xB04F0000u, 41u}, // pcm -> Latn - {0x886F0000u, 41u}, // pdc -> Latn - {0xCC6F0000u, 41u}, // pdt -> Latn - {0xB88F0000u, 83u}, // peo -> Xpeo - {0xACAF0000u, 41u}, // pfl -> Latn + {0xB00F0000u, 40u}, // pam -> Latn + {0xBC0F0000u, 40u}, // pap -> Latn + {0xD00F0000u, 40u}, // pau -> Latn + {0xA02F0000u, 40u}, // pbi -> Latn + {0x8C4F0000u, 40u}, // pcd -> Latn + {0xB04F0000u, 40u}, // pcm -> Latn + {0x886F0000u, 40u}, // pdc -> Latn + {0xCC6F0000u, 40u}, // pdt -> Latn + {0x8C8F0000u, 40u}, // ped -> Latn + {0xB88F0000u, 84u}, // peo -> Xpeo + {0xDC8F0000u, 40u}, // pex -> Latn + {0xACAF0000u, 40u}, // pfl -> Latn + {0xACEF0000u, 1u}, // phl -> Arab {0xB4EF0000u, 61u}, // phn -> Phnx + {0xAD0F0000u, 40u}, // pil -> Latn + {0xBD0F0000u, 40u}, // pip -> Latn {0x814F0000u, 8u}, // pka -> Brah - {0xB94F0000u, 41u}, // pko -> Latn - {0x706C0000u, 41u}, // pl -> Latn - {0xC98F0000u, 41u}, // pms -> Latn + {0xB94F0000u, 40u}, // pko -> Latn + {0x706C0000u, 40u}, // pl -> Latn + {0x816F0000u, 40u}, // pla -> Latn + {0xC98F0000u, 40u}, // pms -> Latn + {0x99AF0000u, 40u}, // png -> Latn + {0xB5AF0000u, 40u}, // pnn -> Latn {0xCDAF0000u, 21u}, // pnt -> Grek - {0xB5CF0000u, 41u}, // pon -> Latn + {0xB5CF0000u, 40u}, // pon -> Latn + {0xB9EF0000u, 40u}, // ppo -> Latn {0x822F0000u, 34u}, // pra -> Khar {0x8E2F0000u, 1u}, // prd -> Arab - {0x9A2F0000u, 41u}, // prg -> Latn + {0x9A2F0000u, 40u}, // prg -> Latn {0x70730000u, 1u}, // ps -> Arab - {0x70740000u, 41u}, // pt -> Latn - {0xD28F0000u, 41u}, // puu -> Latn - {0x71750000u, 41u}, // qu -> Latn - {0x8A900000u, 41u}, // quc -> Latn - {0x9A900000u, 41u}, // qug -> Latn + {0xCA4F0000u, 40u}, // pss -> Latn + {0x70740000u, 40u}, // pt -> Latn + {0xBE6F0000u, 40u}, // ptp -> Latn + {0xD28F0000u, 40u}, // puu -> Latn + {0x82CF0000u, 40u}, // pwa -> Latn + {0x71750000u, 40u}, // qu -> Latn + {0x8A900000u, 40u}, // quc -> Latn + {0x9A900000u, 40u}, // qug -> Latn + {0xA0110000u, 40u}, // rai -> Latn {0xA4110000u, 16u}, // raj -> Deva - {0x94510000u, 41u}, // rcf -> Latn - {0xA4910000u, 41u}, // rej -> Latn - {0xB4D10000u, 41u}, // rgn -> Latn - {0x81110000u, 41u}, // ria -> Latn - {0x95110000u, 77u}, // rif -> Tfng - {0x95114E4Cu, 41u}, // rif-NL -> Latn + {0xB8110000u, 40u}, // rao -> Latn + {0x94510000u, 40u}, // rcf -> Latn + {0xA4910000u, 40u}, // rej -> Latn + {0xAC910000u, 40u}, // rel -> Latn + {0xC8910000u, 40u}, // res -> Latn + {0xB4D10000u, 40u}, // rgn -> Latn + {0x98F10000u, 1u}, // rhg -> Arab + {0x81110000u, 40u}, // ria -> Latn + {0x95110000u, 78u}, // rif -> Tfng + {0x95114E4Cu, 40u}, // rif-NL -> Latn {0xC9310000u, 16u}, // rjs -> Deva {0xCD510000u, 7u}, // rkt -> Beng - {0x726D0000u, 41u}, // rm -> Latn - {0x95910000u, 41u}, // rmf -> Latn - {0xB9910000u, 41u}, // rmo -> Latn + {0x726D0000u, 40u}, // rm -> Latn + {0x95910000u, 40u}, // rmf -> Latn + {0xB9910000u, 40u}, // rmo -> Latn {0xCD910000u, 1u}, // rmt -> Arab - {0xD1910000u, 41u}, // rmu -> Latn - {0x726E0000u, 41u}, // rn -> Latn - {0x99B10000u, 41u}, // rng -> Latn - {0x726F0000u, 41u}, // ro -> Latn - {0x85D10000u, 41u}, // rob -> Latn - {0x95D10000u, 41u}, // rof -> Latn - {0xB2710000u, 41u}, // rtm -> Latn + {0xD1910000u, 40u}, // rmu -> Latn + {0x726E0000u, 40u}, // rn -> Latn + {0x81B10000u, 40u}, // rna -> Latn + {0x99B10000u, 40u}, // rng -> Latn + {0x726F0000u, 40u}, // ro -> Latn + {0x85D10000u, 40u}, // rob -> Latn + {0x95D10000u, 40u}, // rof -> Latn + {0xB9D10000u, 40u}, // roo -> Latn + {0xBA310000u, 40u}, // rro -> Latn + {0xB2710000u, 40u}, // rtm -> Latn {0x72750000u, 15u}, // ru -> Cyrl {0x92910000u, 15u}, // rue -> Cyrl - {0x9A910000u, 41u}, // rug -> Latn - {0x72770000u, 41u}, // rw -> Latn - {0xAAD10000u, 41u}, // rwk -> Latn + {0x9A910000u, 40u}, // rug -> Latn + {0x72770000u, 40u}, // rw -> Latn + {0xAAD10000u, 40u}, // rwk -> Latn + {0xBAD10000u, 40u}, // rwo -> Latn {0xD3110000u, 33u}, // ryu -> Kana {0x73610000u, 16u}, // sa -> Deva - {0x94120000u, 41u}, // saf -> Latn + {0x94120000u, 40u}, // saf -> Latn {0x9C120000u, 15u}, // sah -> Cyrl - {0xC0120000u, 41u}, // saq -> Latn - {0xC8120000u, 41u}, // sas -> Latn - {0xCC120000u, 41u}, // sat -> Latn + {0xC0120000u, 40u}, // saq -> Latn + {0xC8120000u, 40u}, // sas -> Latn + {0xCC120000u, 40u}, // sat -> Latn {0xE4120000u, 67u}, // saz -> Saur - {0xBC320000u, 41u}, // sbp -> Latn - {0x73630000u, 41u}, // sc -> Latn + {0x80320000u, 40u}, // sba -> Latn + {0x90320000u, 40u}, // sbe -> Latn + {0xBC320000u, 40u}, // sbp -> Latn + {0x73630000u, 40u}, // sc -> Latn {0xA8520000u, 16u}, // sck -> Deva - {0xB4520000u, 41u}, // scn -> Latn - {0xB8520000u, 41u}, // sco -> Latn - {0xC8520000u, 41u}, // scs -> Latn + {0xAC520000u, 1u}, // scl -> Arab + {0xB4520000u, 40u}, // scn -> Latn + {0xB8520000u, 40u}, // sco -> Latn + {0xC8520000u, 40u}, // scs -> Latn {0x73640000u, 1u}, // sd -> Arab - {0x88720000u, 41u}, // sdc -> Latn + {0x88720000u, 40u}, // sdc -> Latn {0x9C720000u, 1u}, // sdh -> Arab - {0x73650000u, 41u}, // se -> Latn - {0x94920000u, 41u}, // sef -> Latn - {0x9C920000u, 41u}, // seh -> Latn - {0xA0920000u, 41u}, // sei -> Latn - {0xC8920000u, 41u}, // ses -> Latn - {0x73670000u, 41u}, // sg -> Latn - {0x80D20000u, 56u}, // sga -> Ogam - {0xC8D20000u, 41u}, // sgs -> Latn - {0x73680000u, 41u}, // sh -> Latn - {0xA0F20000u, 77u}, // shi -> Tfng - {0xB4F20000u, 53u}, // shn -> Mymr + {0x73650000u, 40u}, // se -> Latn + {0x94920000u, 40u}, // sef -> Latn + {0x9C920000u, 40u}, // seh -> Latn + {0xA0920000u, 40u}, // sei -> Latn + {0xC8920000u, 40u}, // ses -> Latn + {0x73670000u, 40u}, // sg -> Latn + {0x80D20000u, 55u}, // sga -> Ogam + {0xC8D20000u, 40u}, // sgs -> Latn + {0xD8D20000u, 18u}, // sgw -> Ethi + {0xE4D20000u, 40u}, // sgz -> Latn + {0x73680000u, 40u}, // sh -> Latn + {0xA0F20000u, 78u}, // shi -> Tfng + {0xA8F20000u, 40u}, // shk -> Latn + {0xB4F20000u, 52u}, // shn -> Mymr + {0xD0F20000u, 1u}, // shu -> Arab {0x73690000u, 69u}, // si -> Sinh - {0x8D120000u, 41u}, // sid -> Latn - {0x736B0000u, 41u}, // sk -> Latn + {0x8D120000u, 40u}, // sid -> Latn + {0x99120000u, 40u}, // sig -> Latn + {0xAD120000u, 40u}, // sil -> Latn + {0xB1120000u, 40u}, // sim -> Latn + {0xC5320000u, 40u}, // sjr -> Latn + {0x736B0000u, 40u}, // sk -> Latn + {0x89520000u, 40u}, // skc -> Latn {0xC5520000u, 1u}, // skr -> Arab - {0x736C0000u, 41u}, // sl -> Latn - {0xA1720000u, 41u}, // sli -> Latn - {0xE1720000u, 41u}, // sly -> Latn - {0x736D0000u, 41u}, // sm -> Latn - {0x81920000u, 41u}, // sma -> Latn - {0xA5920000u, 41u}, // smj -> Latn - {0xB5920000u, 41u}, // smn -> Latn + {0xC9520000u, 40u}, // sks -> Latn + {0x736C0000u, 40u}, // sl -> Latn + {0x8D720000u, 40u}, // sld -> Latn + {0xA1720000u, 40u}, // sli -> Latn + {0xAD720000u, 40u}, // sll -> Latn + {0xE1720000u, 40u}, // sly -> Latn + {0x736D0000u, 40u}, // sm -> Latn + {0x81920000u, 40u}, // sma -> Latn + {0xA5920000u, 40u}, // smj -> Latn + {0xB5920000u, 40u}, // smn -> Latn {0xBD920000u, 65u}, // smp -> Samr - {0xC9920000u, 41u}, // sms -> Latn - {0x736E0000u, 41u}, // sn -> Latn - {0xA9B20000u, 41u}, // snk -> Latn - {0x736F0000u, 41u}, // so -> Latn - {0xD1D20000u, 79u}, // sou -> Thai - {0x73710000u, 41u}, // sq -> Latn + {0xC1920000u, 40u}, // smq -> Latn + {0xC9920000u, 40u}, // sms -> Latn + {0x736E0000u, 40u}, // sn -> Latn + {0x89B20000u, 40u}, // snc -> Latn + {0xA9B20000u, 40u}, // snk -> Latn + {0xBDB20000u, 40u}, // snp -> Latn + {0xDDB20000u, 40u}, // snx -> Latn + {0xE1B20000u, 40u}, // sny -> Latn + {0x736F0000u, 40u}, // so -> Latn + {0xA9D20000u, 40u}, // sok -> Latn + {0xC1D20000u, 40u}, // soq -> Latn + {0xD1D20000u, 80u}, // sou -> Thai + {0xE1D20000u, 40u}, // soy -> Latn + {0x8DF20000u, 40u}, // spd -> Latn + {0xADF20000u, 40u}, // spl -> Latn + {0xC9F20000u, 40u}, // sps -> Latn + {0x73710000u, 40u}, // sq -> Latn {0x73720000u, 15u}, // sr -> Cyrl - {0x73724D45u, 41u}, // sr-ME -> Latn - {0x7372524Fu, 41u}, // sr-RO -> Latn - {0x73725255u, 41u}, // sr-RU -> Latn - {0x73725452u, 41u}, // sr-TR -> Latn + {0x73724D45u, 40u}, // sr-ME -> Latn + {0x7372524Fu, 40u}, // sr-RO -> Latn + {0x73725255u, 40u}, // sr-RU -> Latn + {0x73725452u, 40u}, // sr-TR -> Latn {0x86320000u, 70u}, // srb -> Sora - {0xB6320000u, 41u}, // srn -> Latn - {0xC6320000u, 41u}, // srr -> Latn + {0xB6320000u, 40u}, // srn -> Latn + {0xC6320000u, 40u}, // srr -> Latn {0xDE320000u, 16u}, // srx -> Deva - {0x73730000u, 41u}, // ss -> Latn - {0xE2520000u, 41u}, // ssy -> Latn - {0x73740000u, 41u}, // st -> Latn - {0xC2720000u, 41u}, // stq -> Latn - {0x73750000u, 41u}, // su -> Latn - {0xAA920000u, 41u}, // suk -> Latn - {0xCA920000u, 41u}, // sus -> Latn - {0x73760000u, 41u}, // sv -> Latn - {0x73770000u, 41u}, // sw -> Latn + {0x73730000u, 40u}, // ss -> Latn + {0x8E520000u, 40u}, // ssd -> Latn + {0x9A520000u, 40u}, // ssg -> Latn + {0xE2520000u, 40u}, // ssy -> Latn + {0x73740000u, 40u}, // st -> Latn + {0xAA720000u, 40u}, // stk -> Latn + {0xC2720000u, 40u}, // stq -> Latn + {0x73750000u, 40u}, // su -> Latn + {0x82920000u, 40u}, // sua -> Latn + {0x92920000u, 40u}, // sue -> Latn + {0xAA920000u, 40u}, // suk -> Latn + {0xC6920000u, 40u}, // sur -> Latn + {0xCA920000u, 40u}, // sus -> Latn + {0x73760000u, 40u}, // sv -> Latn + {0x73770000u, 40u}, // sw -> Latn {0x86D20000u, 1u}, // swb -> Arab - {0x8AD20000u, 41u}, // swc -> Latn - {0x9AD20000u, 41u}, // swg -> Latn + {0x8AD20000u, 40u}, // swc -> Latn + {0x9AD20000u, 40u}, // swg -> Latn + {0xBED20000u, 40u}, // swp -> Latn {0xD6D20000u, 16u}, // swv -> Deva - {0xB6F20000u, 41u}, // sxn -> Latn + {0xB6F20000u, 40u}, // sxn -> Latn + {0xDAF20000u, 40u}, // sxw -> Latn {0xAF120000u, 7u}, // syl -> Beng {0xC7120000u, 71u}, // syr -> Syrc - {0xAF320000u, 41u}, // szl -> Latn + {0xAF320000u, 40u}, // szl -> Latn {0x74610000u, 74u}, // ta -> Taml {0xA4130000u, 16u}, // taj -> Deva - {0xD8330000u, 41u}, // tbw -> Latn + {0xAC130000u, 40u}, // tal -> Latn + {0xB4130000u, 40u}, // tan -> Latn + {0xC0130000u, 40u}, // taq -> Latn + {0x88330000u, 40u}, // tbc -> Latn + {0x8C330000u, 40u}, // tbd -> Latn + {0x94330000u, 40u}, // tbf -> Latn + {0x98330000u, 40u}, // tbg -> Latn + {0xB8330000u, 40u}, // tbo -> Latn + {0xD8330000u, 40u}, // tbw -> Latn + {0xE4330000u, 40u}, // tbz -> Latn + {0xA0530000u, 40u}, // tci -> Latn {0xE0530000u, 36u}, // tcy -> Knda {0x8C730000u, 72u}, // tdd -> Tale {0x98730000u, 16u}, // tdg -> Deva {0x9C730000u, 16u}, // tdh -> Deva - {0x74650000u, 76u}, // te -> Telu - {0xB0930000u, 41u}, // tem -> Latn - {0xB8930000u, 41u}, // teo -> Latn - {0xCC930000u, 41u}, // tet -> Latn + {0x74650000u, 77u}, // te -> Telu + {0x8C930000u, 40u}, // ted -> Latn + {0xB0930000u, 40u}, // tem -> Latn + {0xB8930000u, 40u}, // teo -> Latn + {0xCC930000u, 40u}, // tet -> Latn + {0xA0B30000u, 40u}, // tfi -> Latn {0x74670000u, 15u}, // tg -> Cyrl {0x7467504Bu, 1u}, // tg-PK -> Arab - {0x74680000u, 79u}, // th -> Thai + {0x88D30000u, 40u}, // tgc -> Latn + {0xB8D30000u, 40u}, // tgo -> Latn + {0xD0D30000u, 40u}, // tgu -> Latn + {0x74680000u, 80u}, // th -> Thai {0xACF30000u, 16u}, // thl -> Deva {0xC0F30000u, 16u}, // thq -> Deva {0xC4F30000u, 16u}, // thr -> Deva {0x74690000u, 18u}, // ti -> Ethi + {0x95130000u, 40u}, // tif -> Latn {0x99130000u, 18u}, // tig -> Ethi - {0xD5130000u, 41u}, // tiv -> Latn - {0x746B0000u, 41u}, // tk -> Latn - {0xAD530000u, 41u}, // tkl -> Latn - {0xC5530000u, 41u}, // tkr -> Latn + {0xA9130000u, 40u}, // tik -> Latn + {0xB1130000u, 40u}, // tim -> Latn + {0xB9130000u, 40u}, // tio -> Latn + {0xD5130000u, 40u}, // tiv -> Latn + {0x746B0000u, 40u}, // tk -> Latn + {0xAD530000u, 40u}, // tkl -> Latn + {0xC5530000u, 40u}, // tkr -> Latn {0xCD530000u, 16u}, // tkt -> Deva - {0x746C0000u, 41u}, // tl -> Latn - {0xE1730000u, 41u}, // tly -> Latn - {0x9D930000u, 41u}, // tmh -> Latn - {0x746E0000u, 41u}, // tn -> Latn - {0x746F0000u, 41u}, // to -> Latn - {0x99D30000u, 41u}, // tog -> Latn - {0xA1F30000u, 41u}, // tpi -> Latn - {0x74720000u, 41u}, // tr -> Latn - {0xD2330000u, 41u}, // tru -> Latn - {0xD6330000u, 41u}, // trv -> Latn - {0x74730000u, 41u}, // ts -> Latn + {0x746C0000u, 40u}, // tl -> Latn + {0x95730000u, 40u}, // tlf -> Latn + {0xDD730000u, 40u}, // tlx -> Latn + {0xE1730000u, 40u}, // tly -> Latn + {0x9D930000u, 40u}, // tmh -> Latn + {0xE1930000u, 40u}, // tmy -> Latn + {0x746E0000u, 40u}, // tn -> Latn + {0x9DB30000u, 40u}, // tnh -> Latn + {0x746F0000u, 40u}, // to -> Latn + {0x95D30000u, 40u}, // tof -> Latn + {0x99D30000u, 40u}, // tog -> Latn + {0xC1D30000u, 40u}, // toq -> Latn + {0xA1F30000u, 40u}, // tpi -> Latn + {0xB1F30000u, 40u}, // tpm -> Latn + {0xE5F30000u, 40u}, // tpz -> Latn + {0xBA130000u, 40u}, // tqo -> Latn + {0x74720000u, 40u}, // tr -> Latn + {0xD2330000u, 40u}, // tru -> Latn + {0xD6330000u, 40u}, // trv -> Latn + {0xDA330000u, 1u}, // trw -> Arab + {0x74730000u, 40u}, // ts -> Latn {0x8E530000u, 21u}, // tsd -> Grek {0x96530000u, 16u}, // tsf -> Deva - {0x9A530000u, 41u}, // tsg -> Latn - {0xA6530000u, 80u}, // tsj -> Tibt + {0x9A530000u, 40u}, // tsg -> Latn + {0xA6530000u, 81u}, // tsj -> Tibt + {0xDA530000u, 40u}, // tsw -> Latn {0x74740000u, 15u}, // tt -> Cyrl - {0xA6730000u, 41u}, // ttj -> Latn - {0xCA730000u, 79u}, // tts -> Thai - {0xCE730000u, 41u}, // ttt -> Latn - {0xB2930000u, 41u}, // tum -> Latn - {0xAEB30000u, 41u}, // tvl -> Latn - {0xC2D30000u, 41u}, // twq -> Latn - {0x74790000u, 41u}, // ty -> Latn + {0x8E730000u, 40u}, // ttd -> Latn + {0x92730000u, 40u}, // tte -> Latn + {0xA6730000u, 40u}, // ttj -> Latn + {0xC6730000u, 40u}, // ttr -> Latn + {0xCA730000u, 80u}, // tts -> Thai + {0xCE730000u, 40u}, // ttt -> Latn + {0x9E930000u, 40u}, // tuh -> Latn + {0xAE930000u, 40u}, // tul -> Latn + {0xB2930000u, 40u}, // tum -> Latn + {0xC2930000u, 40u}, // tuq -> Latn + {0x8EB30000u, 40u}, // tvd -> Latn + {0xAEB30000u, 40u}, // tvl -> Latn + {0xD2B30000u, 40u}, // tvu -> Latn + {0x9ED30000u, 40u}, // twh -> Latn + {0xC2D30000u, 40u}, // twq -> Latn + {0x9AF30000u, 75u}, // txg -> Tang + {0x74790000u, 40u}, // ty -> Latn + {0x83130000u, 40u}, // tya -> Latn {0xD7130000u, 15u}, // tyv -> Cyrl - {0xB3330000u, 41u}, // tzm -> Latn + {0xB3330000u, 40u}, // tzm -> Latn + {0xD0340000u, 40u}, // ubu -> Latn {0xB0740000u, 15u}, // udm -> Cyrl {0x75670000u, 1u}, // ug -> Arab {0x75674B5Au, 15u}, // ug-KZ -> Cyrl {0x75674D4Eu, 15u}, // ug-MN -> Cyrl - {0x80D40000u, 81u}, // uga -> Ugar + {0x80D40000u, 82u}, // uga -> Ugar {0x756B0000u, 15u}, // uk -> Cyrl - {0xA1740000u, 41u}, // uli -> Latn - {0x85940000u, 41u}, // umb -> Latn + {0xA1740000u, 40u}, // uli -> Latn + {0x85940000u, 40u}, // umb -> Latn {0xC5B40000u, 7u}, // unr -> Beng {0xC5B44E50u, 16u}, // unr-NP -> Deva {0xDDB40000u, 7u}, // unx -> Beng {0x75720000u, 1u}, // ur -> Arab - {0x757A0000u, 41u}, // uz -> Latn + {0xA2340000u, 40u}, // uri -> Latn + {0xCE340000u, 40u}, // urt -> Latn + {0xDA340000u, 40u}, // urw -> Latn + {0x82540000u, 40u}, // usa -> Latn + {0xC6740000u, 40u}, // utr -> Latn + {0x9EB40000u, 40u}, // uvh -> Latn + {0xAEB40000u, 40u}, // uvl -> Latn + {0x757A0000u, 40u}, // uz -> Latn {0x757A4146u, 1u}, // uz-AF -> Arab {0x757A434Eu, 15u}, // uz-CN -> Cyrl - {0xA0150000u, 82u}, // vai -> Vaii - {0x76650000u, 41u}, // ve -> Latn - {0x88950000u, 41u}, // vec -> Latn - {0xBC950000u, 41u}, // vep -> Latn - {0x76690000u, 41u}, // vi -> Latn - {0x89150000u, 41u}, // vic -> Latn - {0xC9750000u, 41u}, // vls -> Latn - {0x95950000u, 41u}, // vmf -> Latn - {0xD9950000u, 41u}, // vmw -> Latn - {0x766F0000u, 41u}, // vo -> Latn - {0xCDD50000u, 41u}, // vot -> Latn - {0xBA350000u, 41u}, // vro -> Latn - {0xB6950000u, 41u}, // vun -> Latn - {0x77610000u, 41u}, // wa -> Latn - {0x90160000u, 41u}, // wae -> Latn + {0x98150000u, 40u}, // vag -> Latn + {0xA0150000u, 83u}, // vai -> Vaii + {0xB4150000u, 40u}, // van -> Latn + {0x76650000u, 40u}, // ve -> Latn + {0x88950000u, 40u}, // vec -> Latn + {0xBC950000u, 40u}, // vep -> Latn + {0x76690000u, 40u}, // vi -> Latn + {0x89150000u, 40u}, // vic -> Latn + {0xD5150000u, 40u}, // viv -> Latn + {0xC9750000u, 40u}, // vls -> Latn + {0x95950000u, 40u}, // vmf -> Latn + {0xD9950000u, 40u}, // vmw -> Latn + {0x766F0000u, 40u}, // vo -> Latn + {0xCDD50000u, 40u}, // vot -> Latn + {0xBA350000u, 40u}, // vro -> Latn + {0xB6950000u, 40u}, // vun -> Latn + {0xCE950000u, 40u}, // vut -> Latn + {0x77610000u, 40u}, // wa -> Latn + {0x90160000u, 40u}, // wae -> Latn + {0xA4160000u, 40u}, // waj -> Latn {0xAC160000u, 18u}, // wal -> Ethi - {0xC4160000u, 41u}, // war -> Latn - {0xBC360000u, 41u}, // wbp -> Latn - {0xC0360000u, 76u}, // wbq -> Telu + {0xB4160000u, 40u}, // wan -> Latn + {0xC4160000u, 40u}, // war -> Latn + {0xBC360000u, 40u}, // wbp -> Latn + {0xC0360000u, 77u}, // wbq -> Telu {0xC4360000u, 16u}, // wbr -> Deva - {0xC9760000u, 41u}, // wls -> Latn + {0xA0560000u, 40u}, // wci -> Latn + {0xC4960000u, 40u}, // wer -> Latn + {0xA0D60000u, 40u}, // wgi -> Latn + {0x98F60000u, 40u}, // whg -> Latn + {0x85160000u, 40u}, // wib -> Latn + {0xD1160000u, 40u}, // wiu -> Latn + {0xD5160000u, 40u}, // wiv -> Latn + {0x81360000u, 40u}, // wja -> Latn + {0xA1360000u, 40u}, // wji -> Latn + {0xC9760000u, 40u}, // wls -> Latn + {0xB9960000u, 40u}, // wmo -> Latn + {0x89B60000u, 40u}, // wnc -> Latn {0xA1B60000u, 1u}, // wni -> Arab - {0x776F0000u, 41u}, // wo -> Latn + {0xD1B60000u, 40u}, // wnu -> Latn + {0x776F0000u, 40u}, // wo -> Latn + {0x85D60000u, 40u}, // wob -> Latn + {0xC9D60000u, 40u}, // wos -> Latn + {0xCA360000u, 40u}, // wrs -> Latn + {0xAA560000u, 40u}, // wsk -> Latn {0xB2760000u, 16u}, // wtm -> Deva {0xD2960000u, 24u}, // wuu -> Hans - {0xD4170000u, 41u}, // xav -> Latn + {0xD6960000u, 40u}, // wuv -> Latn + {0x82D60000u, 40u}, // wwa -> Latn + {0xD4170000u, 40u}, // xav -> Latn + {0xA0370000u, 40u}, // xbi -> Latn {0xC4570000u, 10u}, // xcr -> Cari - {0x78680000u, 41u}, // xh -> Latn - {0x89770000u, 45u}, // xlc -> Lyci - {0x8D770000u, 46u}, // xld -> Lydi + {0xC8970000u, 40u}, // xes -> Latn + {0x78680000u, 40u}, // xh -> Latn + {0x81770000u, 40u}, // xla -> Latn + {0x89770000u, 44u}, // xlc -> Lyci + {0x8D770000u, 45u}, // xld -> Lydi {0x95970000u, 19u}, // xmf -> Geor - {0xB5970000u, 48u}, // xmn -> Mani - {0xC5970000u, 49u}, // xmr -> Merc - {0x81B70000u, 54u}, // xna -> Narb + {0xB5970000u, 47u}, // xmn -> Mani + {0xC5970000u, 48u}, // xmr -> Merc + {0x81B70000u, 53u}, // xna -> Narb {0xC5B70000u, 16u}, // xnr -> Deva - {0x99D70000u, 41u}, // xog -> Latn + {0x99D70000u, 40u}, // xog -> Latn + {0xB5D70000u, 40u}, // xon -> Latn {0xC5F70000u, 63u}, // xpr -> Prti + {0x86370000u, 40u}, // xrb -> Latn {0x82570000u, 66u}, // xsa -> Sarb + {0xA2570000u, 40u}, // xsi -> Latn + {0xB2570000u, 40u}, // xsm -> Latn {0xC6570000u, 16u}, // xsr -> Deva - {0xB8180000u, 41u}, // yao -> Latn - {0xBC180000u, 41u}, // yap -> Latn - {0xD4180000u, 41u}, // yav -> Latn - {0x84380000u, 41u}, // ybb -> Latn + {0x92D70000u, 40u}, // xwe -> Latn + {0xB0180000u, 40u}, // yam -> Latn + {0xB8180000u, 40u}, // yao -> Latn + {0xBC180000u, 40u}, // yap -> Latn + {0xC8180000u, 40u}, // yas -> Latn + {0xCC180000u, 40u}, // yat -> Latn + {0xD4180000u, 40u}, // yav -> Latn + {0xE0180000u, 40u}, // yay -> Latn + {0xE4180000u, 40u}, // yaz -> Latn + {0x80380000u, 40u}, // yba -> Latn + {0x84380000u, 40u}, // ybb -> Latn + {0xE0380000u, 40u}, // yby -> Latn + {0xC4980000u, 40u}, // yer -> Latn + {0xC4D80000u, 40u}, // ygr -> Latn + {0xD8D80000u, 40u}, // ygw -> Latn {0x79690000u, 27u}, // yi -> Hebr - {0x796F0000u, 41u}, // yo -> Latn - {0xAE380000u, 41u}, // yrl -> Latn - {0x82980000u, 41u}, // yua -> Latn - {0x7A610000u, 41u}, // za -> Latn - {0x98190000u, 41u}, // zag -> Latn + {0xB9580000u, 40u}, // yko -> Latn + {0x91780000u, 40u}, // yle -> Latn + {0x99780000u, 40u}, // ylg -> Latn + {0xAD780000u, 40u}, // yll -> Latn + {0xAD980000u, 40u}, // yml -> Latn + {0x796F0000u, 40u}, // yo -> Latn + {0xB5D80000u, 40u}, // yon -> Latn + {0x86380000u, 40u}, // yrb -> Latn + {0x92380000u, 40u}, // yre -> Latn + {0xAE380000u, 40u}, // yrl -> Latn + {0xCA580000u, 40u}, // yss -> Latn + {0x82980000u, 40u}, // yua -> Latn + {0x92980000u, 25u}, // yue -> Hant + {0x9298434Eu, 24u}, // yue-CN -> Hans + {0xA6980000u, 40u}, // yuj -> Latn + {0xCE980000u, 40u}, // yut -> Latn + {0xDA980000u, 40u}, // yuw -> Latn + {0x7A610000u, 40u}, // za -> Latn + {0x98190000u, 40u}, // zag -> Latn {0xA4790000u, 1u}, // zdj -> Arab - {0x80990000u, 41u}, // zea -> Latn - {0x9CD90000u, 77u}, // zgh -> Tfng + {0x80990000u, 40u}, // zea -> Latn + {0x9CD90000u, 78u}, // zgh -> Tfng {0x7A680000u, 24u}, // zh -> Hans {0x7A684155u, 25u}, // zh-AU -> Hant {0x7A68424Eu, 25u}, // zh-BN -> Hant @@ -829,9 +1437,12 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ {0x7A685457u, 25u}, // zh-TW -> Hant {0x7A685553u, 25u}, // zh-US -> Hant {0x7A68564Eu, 25u}, // zh-VN -> Hant - {0xA1990000u, 41u}, // zmi -> Latn - {0x7A750000u, 41u}, // zu -> Latn - {0x83390000u, 41u}, // zza -> Latn + {0x81190000u, 40u}, // zia -> Latn + {0xB1790000u, 40u}, // zlm -> Latn + {0xA1990000u, 40u}, // zmi -> Latn + {0x91B90000u, 40u}, // zne -> Latn + {0x7A750000u, 40u}, // zu -> Latn + {0x83390000u, 40u}, // zza -> Latn }); std::unordered_set<uint64_t> REPRESENTATIVE_LOCALES({ @@ -854,6 +1465,7 @@ std::unordered_set<uint64_t> REPRESENTATIVE_LOCALES({ 0x616D455445746869llu, // am_Ethi_ET 0xB9804E474C61746Ellu, // amo_Latn_NG 0xE5C049444C61746Ellu, // aoz_Latn_ID + 0x8DE0544741726162llu, // apd_Arab_TG 0x6172454741726162llu, // ar_Arab_EG 0x8A20495241726D69llu, // arc_Armi_IR 0x8A204A4F4E626174llu, // arc_Nbat_JO @@ -896,7 +1508,6 @@ std::unordered_set<uint64_t> REPRESENTATIVE_LOCALES({ 0x88C1494E44657661llu, // bgc_Deva_IN 0xB4C1504B41726162llu, // bgn_Arab_PK 0xDCC154524772656Bllu, // bgx_Grek_TR - 0x6268494E4B746869llu, // bh_Kthi_IN 0x84E1494E44657661llu, // bhb_Deva_IN 0xA0E1494E44657661llu, // bhi_Deva_IN 0xA8E150484C61746Ellu, // bhk_Latn_PH @@ -980,6 +1591,7 @@ std::unordered_set<uint64_t> REPRESENTATIVE_LOCALES({ 0x864344454C61746Ellu, // dsb_Latn_DE 0xB2634D4C4C61746Ellu, // dtm_Latn_ML 0xBE634D594C61746Ellu, // dtp_Latn_MY + 0xE2634E5044657661llu, // dty_Deva_NP 0x8283434D4C61746Ellu, // dua_Latn_CM 0x64764D5654686161llu, // dv_Thaa_MV 0xBB03534E4C61746Ellu, // dyo_Latn_SN @@ -1006,6 +1618,7 @@ std::unordered_set<uint64_t> REPRESENTATIVE_LOCALES({ 0xCEE445534C61746Ellu, // ext_Latn_ES 0x6661495241726162llu, // fa_Arab_IR 0xB40547514C61746Ellu, // fan_Latn_GQ + 0x6666474E41646C6Dllu, // ff_Adlm_GN 0x6666534E4C61746Ellu, // ff_Latn_SN 0xB0A54D4C4C61746Ellu, // ffm_Latn_ML 0x666946494C61746Ellu, // fi_Latn_FI @@ -1020,7 +1633,9 @@ std::unordered_set<uint64_t> REPRESENTATIVE_LOCALES({ 0xBE2546524C61746Ellu, // frp_Latn_FR 0xC62544454C61746Ellu, // frr_Latn_DE 0xCA2544454C61746Ellu, // frs_Latn_DE + 0x8685434D41726162llu, // fub_Arab_CM 0x8E8557464C61746Ellu, // fud_Latn_WF + 0x9685474E4C61746Ellu, // fuf_Latn_GN 0xC2854E454C61746Ellu, // fuq_Latn_NE 0xC68549544C61746Ellu, // fur_Latn_IT 0xD6854E474C61746Ellu, // fuv_Latn_NG @@ -1104,7 +1719,6 @@ std::unordered_set<uint64_t> REPRESENTATIVE_LOCALES({ 0x6A614A504A70616Ellu, // ja_Jpan_JP 0xB0094A4D4C61746Ellu, // jam_Latn_JM 0xB8C9434D4C61746Ellu, // jgo_Latn_CM - 0x6A69554148656272llu, // ji_Hebr_UA 0x8989545A4C61746Ellu, // jmc_Latn_TZ 0xAD894E5044657661llu, // jml_Deva_NP 0xCE89444B4C61746Ellu, // jut_Latn_DK @@ -1118,9 +1732,11 @@ std::unordered_set<uint64_t> REPRESENTATIVE_LOCALES({ 0xB00A4B454C61746Ellu, // kam_Latn_KE 0xB80A4D4C4C61746Ellu, // kao_Latn_ML 0x8C2A52554379726Cllu, // kbd_Cyrl_RU + 0xE02A4E4541726162llu, // kby_Arab_NE 0x984A4E474C61746Ellu, // kcg_Latn_NG 0xA84A5A574C61746Ellu, // kck_Latn_ZW 0x906A545A4C61746Ellu, // kde_Latn_TZ + 0x9C6A544741726162llu, // kdh_Arab_TG 0xCC6A544854686169llu, // kdt_Thai_TH 0x808A43564C61746Ellu, // kea_Latn_CV 0xB48A434D4C61746Ellu, // ken_Latn_CM @@ -1251,7 +1867,7 @@ std::unordered_set<uint64_t> REPRESENTATIVE_LOCALES({ 0x6D72494E44657661llu, // mr_Deva_IN 0x8E2C4E5044657661llu, // mrd_Deva_NP 0xA62C52554379726Cllu, // mrj_Cyrl_RU - 0xD22C42444D726F6Fllu, // mru_Mroo_BD + 0xBA2C42444D726F6Fllu, // mro_Mroo_BD 0x6D734D594C61746Ellu, // ms_Latn_MY 0x6D744D544C61746Ellu, // mt_Latn_MT 0xC66C494E44657661llu, // mtr_Deva_IN @@ -1308,6 +1924,7 @@ std::unordered_set<uint64_t> REPRESENTATIVE_LOCALES({ 0x6F6D45544C61746Ellu, // om_Latn_ET 0x6F72494E4F727961llu, // or_Orya_IN 0x6F7347454379726Cllu, // os_Cyrl_GE + 0x824E55534F736765llu, // osa_Osge_US 0xAA6E4D4E4F726B68llu, // otk_Orkh_MN 0x7061504B41726162llu, // pa_Arab_PK 0x7061494E47757275llu, // pa_Guru_IN @@ -1479,6 +2096,7 @@ std::unordered_set<uint64_t> REPRESENTATIVE_LOCALES({ 0xB2934D574C61746Ellu, // tum_Latn_MW 0xAEB354564C61746Ellu, // tvl_Latn_TV 0xC2D34E454C61746Ellu, // twq_Latn_NE + 0x9AF3434E54616E67llu, // txg_Tang_CN 0x747950464C61746Ellu, // ty_Latn_PF 0xD71352554379726Cllu, // tyv_Cyrl_RU 0xB3334D414C61746Ellu, // tzm_Latn_MA @@ -1540,14 +2158,18 @@ std::unordered_set<uint64_t> REPRESENTATIVE_LOCALES({ 0x796F4E474C61746Ellu, // yo_Latn_NG 0xAE3842524C61746Ellu, // yrl_Latn_BR 0x82984D584C61746Ellu, // yua_Latn_MX + 0x9298434E48616E73llu, // yue_Hans_CN + 0x9298484B48616E74llu, // yue_Hant_HK 0x7A61434E4C61746Ellu, // za_Latn_CN 0x981953444C61746Ellu, // zag_Latn_SD 0xA4794B4D41726162llu, // zdj_Arab_KM 0x80994E4C4C61746Ellu, // zea_Latn_NL 0x9CD94D4154666E67llu, // zgh_Tfng_MA 0x7A685457426F706Fllu, // zh_Bopo_TW + 0x7A68545748616E62llu, // zh_Hanb_TW 0x7A68434E48616E73llu, // zh_Hans_CN 0x7A68545748616E74llu, // zh_Hant_TW + 0xB17954474C61746Ellu, // zlm_Latn_TG 0xA1994D594C61746Ellu, // zmi_Latn_MY 0x7A755A414C61746Ellu, // zu_Latn_ZA 0x833954524C61746Ellu, // zza_Latn_TR @@ -1662,6 +2284,7 @@ const std::unordered_map<uint32_t, uint32_t> LATN_PARENTS({ {0x656E5A57u, 0x656E8400u}, // en-ZW -> en-001 {0x65734152u, 0x6573A424u}, // es-AR -> es-419 {0x6573424Fu, 0x6573A424u}, // es-BO -> es-419 + {0x65734252u, 0x6573A424u}, // es-BR -> es-419 {0x6573434Cu, 0x6573A424u}, // es-CL -> es-419 {0x6573434Fu, 0x6573A424u}, // es-CO -> es-419 {0x65734352u, 0x6573A424u}, // es-CR -> es-419 @@ -1681,8 +2304,11 @@ const std::unordered_map<uint32_t, uint32_t> LATN_PARENTS({ {0x65735559u, 0x6573A424u}, // es-UY -> es-419 {0x65735645u, 0x6573A424u}, // es-VE -> es-419 {0x7074414Fu, 0x70745054u}, // pt-AO -> pt-PT + {0x70744348u, 0x70745054u}, // pt-CH -> pt-PT {0x70744356u, 0x70745054u}, // pt-CV -> pt-PT + {0x70744751u, 0x70745054u}, // pt-GQ -> pt-PT {0x70744757u, 0x70745054u}, // pt-GW -> pt-PT + {0x70744C55u, 0x70745054u}, // pt-LU -> pt-PT {0x70744D4Fu, 0x70745054u}, // pt-MO -> pt-PT {0x70744D5Au, 0x70745054u}, // pt-MZ -> pt-PT {0x70745354u, 0x70745054u}, // pt-ST -> pt-PT diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp index e10db05e8557..a30c8497b0f8 100644 --- a/libs/androidfw/ResourceTypes.cpp +++ b/libs/androidfw/ResourceTypes.cpp @@ -140,7 +140,7 @@ static void fill9patchOffsets(Res_png_9patch* patch) { patch->colorsOffset = patch->yDivsOffset + (patch->numYDivs * sizeof(int32_t)); } -inline void Res_value::copyFrom_dtoh(const Res_value& src) +void Res_value::copyFrom_dtoh(const Res_value& src) { size = dtohs(src.size); res0 = src.res0; @@ -804,8 +804,14 @@ const char* ResStringPool::string8At(size_t idx, size_t* outLen) const if (off < (mStringPoolSize-1)) { const uint8_t* strings = (uint8_t*)mStrings; const uint8_t* str = strings+off; - *outLen = decodeLength(&str); - size_t encLen = decodeLength(&str); + + // Decode the UTF-16 length. This is not used if we're not + // converting to UTF-16 from UTF-8. + decodeLength(&str); + + const size_t encLen = decodeLength(&str); + *outLen = encLen; + if ((uint32_t)(str+encLen-strings) < mStringPoolSize) { return (const char*)str; } else { @@ -2024,7 +2030,6 @@ int ResTable_config::isLocaleMoreSpecificThan(const ResTable_config& o) const { ((o.localeVariant[0] != '\0') ? 2 : 0); return score - oScore; - } bool ResTable_config::isMoreSpecificThan(const ResTable_config& o) const { @@ -2170,6 +2175,23 @@ bool ResTable_config::isMoreSpecificThan(const ResTable_config& o) const { return false; } +// Codes for specially handled languages and regions +static const char kEnglish[2] = {'e', 'n'}; // packed version of "en" +static const char kUnitedStates[2] = {'U', 'S'}; // packed version of "US" +static const char kFilipino[2] = {'\xAD', '\x05'}; // packed version of "fil" +static const char kTagalog[2] = {'t', 'l'}; // packed version of "tl" + +// Checks if two language or region codes are identical +inline bool areIdentical(const char code1[2], const char code2[2]) { + return code1[0] == code2[0] && code1[1] == code2[1]; +} + +inline bool langsAreEquivalent(const char lang1[2], const char lang2[2]) { + return areIdentical(lang1, lang2) || + (areIdentical(lang1, kTagalog) && areIdentical(lang2, kFilipino)) || + (areIdentical(lang1, kFilipino) && areIdentical(lang2, kTagalog)); +} + bool ResTable_config::isLocaleBetterThan(const ResTable_config& o, const ResTable_config* requested) const { if (requested->locale == 0) { @@ -2179,7 +2201,7 @@ bool ResTable_config::isLocaleBetterThan(const ResTable_config& o, } if (locale == 0 && o.locale == 0) { - // The locales parts of both resources are empty, so no one is better + // The locale part of both resources is empty, so none is better // than the other. return false; } @@ -2194,10 +2216,11 @@ bool ResTable_config::isLocaleBetterThan(const ResTable_config& o, // 2) If the request's script is known, the resource scripts are either // unknown or match the request. - if (language[0] != o.language[0]) { - // The languages of the two resources are not the same. We can only - // assume that one of the two resources matched the request because one - // doesn't have a language and the other has a matching language. + if (!langsAreEquivalent(language, o.language)) { + // The languages of the two resources are not equivalent. If we are + // here, we can only assume that the two resources matched the request + // because one doesn't have a language and the other has a matching + // language. // // We consider the one that has the language specified a better match. // @@ -2205,15 +2228,15 @@ bool ResTable_config::isLocaleBetterThan(const ResTable_config& o, // for US English and similar locales than locales that are a descendant // of Internatinal English (en-001), since no-language resources are // where the US English resource have traditionally lived for most apps. - if (requested->language[0] == 'e' && requested->language[1] == 'n') { - if (requested->country[0] == 'U' && requested->country[1] == 'S') { + if (areIdentical(requested->language, kEnglish)) { + if (areIdentical(requested->country, kUnitedStates)) { // For US English itself, we consider a no-locale resource a // better match if the other resource has a country other than // US specified. if (language[0] != '\0') { - return country[0] == '\0' || (country[0] == 'U' && country[1] == 'S'); + return country[0] == '\0' || areIdentical(country, kUnitedStates); } else { - return !(o.country[0] == '\0' || (o.country[0] == 'U' && o.country[1] == 'S')); + return !(o.country[0] == '\0' || areIdentical(o.country, kUnitedStates)); } } else if (localeDataIsCloseToUsEnglish(requested->country)) { if (language[0] != '\0') { @@ -2226,27 +2249,38 @@ bool ResTable_config::isLocaleBetterThan(const ResTable_config& o, return (language[0] != '\0'); } - // If we are here, both the resources have the same non-empty language as - // the request. + // If we are here, both the resources have an equivalent non-empty language + // to the request. // - // Because the languages are the same, computeScript() always - // returns a non-empty script for languages it knows about, and we have passed - // the script checks in match(), the scripts are either all unknown or are - // all the same. So we can't gain anything by checking the scripts. We need - // to check the region and variant. + // Because the languages are equivalent, computeScript() always returns a + // non-empty script for languages it knows about, and we have passed the + // script checks in match(), the scripts are either all unknown or are all + // the same. So we can't gain anything by checking the scripts. We need to + // check the region and variant. - // See if any of the regions is better than the other + // See if any of the regions is better than the other. const int region_comparison = localeDataCompareRegions( country, o.country, - language, requested->localeScript, requested->country); + requested->language, requested->localeScript, requested->country); if (region_comparison != 0) { return (region_comparison > 0); } // The regions are the same. Try the variant. - if (requested->localeVariant[0] != '\0' - && strncmp(localeVariant, requested->localeVariant, sizeof(localeVariant)) == 0) { - return (strncmp(o.localeVariant, requested->localeVariant, sizeof(localeVariant)) != 0); + const bool localeMatches = strncmp( + localeVariant, requested->localeVariant, sizeof(localeVariant)) == 0; + const bool otherMatches = strncmp( + o.localeVariant, requested->localeVariant, sizeof(localeVariant)) == 0; + if (localeMatches != otherMatches) { + return localeMatches; + } + + // Finally, the languages, although equivalent, may still be different + // (like for Tagalog and Filipino). Identical is better than just + // equivalent. + if (areIdentical(language, requested->language) + && !areIdentical(o.language, requested->language)) { + return true; } return false; @@ -2522,7 +2556,7 @@ bool ResTable_config::match(const ResTable_config& settings) const { // // If two configs differ only in their country and variant, // they can be weeded out in the isMoreSpecificThan test. - if (language[0] != settings.language[0] || language[1] != settings.language[1]) { + if (!langsAreEquivalent(language, settings.language)) { return false; } @@ -2550,9 +2584,7 @@ bool ResTable_config::match(const ResTable_config& settings) const { } if (countriesMustMatch) { - if (country[0] != '\0' - && (country[0] != settings.country[0] - || country[1] != settings.country[1])) { + if (country[0] != '\0' && !areIdentical(country, settings.country)) { return false; } } else { @@ -2734,37 +2766,43 @@ void ResTable_config::appendDirLocale(String8& out) const { } } -void ResTable_config::getBcp47Locale(char str[RESTABLE_MAX_LOCALE_LEN]) const { +void ResTable_config::getBcp47Locale(char str[RESTABLE_MAX_LOCALE_LEN], bool canonicalize) const { memset(str, 0, RESTABLE_MAX_LOCALE_LEN); // This represents the "any" locale value, which has traditionally been // represented by the empty string. - if (!language[0] && !country[0]) { + if (language[0] == '\0' && country[0] == '\0') { return; } size_t charsWritten = 0; - if (language[0]) { - charsWritten += unpackLanguage(str); + if (language[0] != '\0') { + if (canonicalize && areIdentical(language, kTagalog)) { + // Replace Tagalog with Filipino if we are canonicalizing + str[0] = 'f'; str[1] = 'i'; str[2] = 'l'; str[3] = '\0'; // 3-letter code for Filipino + charsWritten += 3; + } else { + charsWritten += unpackLanguage(str); + } } - if (localeScript[0] && !localeScriptWasComputed) { - if (charsWritten) { + if (localeScript[0] != '\0' && !localeScriptWasComputed) { + if (charsWritten > 0) { str[charsWritten++] = '-'; } memcpy(str + charsWritten, localeScript, sizeof(localeScript)); charsWritten += sizeof(localeScript); } - if (country[0]) { - if (charsWritten) { + if (country[0] != '\0') { + if (charsWritten > 0) { str[charsWritten++] = '-'; } charsWritten += unpackRegion(str + charsWritten); } - if (localeVariant[0]) { - if (charsWritten) { + if (localeVariant[0] != '\0') { + if (charsWritten > 0) { str[charsWritten++] = '-'; } memcpy(str + charsWritten, localeVariant, sizeof(localeVariant)); @@ -2956,6 +2994,9 @@ String8 ResTable_config::toString() const { case ResTable_config::UI_MODE_TYPE_WATCH: res.append("watch"); break; + case ResTable_config::UI_MODE_TYPE_VR_HEADSET: + res.append("vrheadset"); + break; default: res.appendFormat("uiModeType=%d", dtohs(screenLayout&ResTable_config::MASK_UI_MODE_TYPE)); @@ -5881,12 +5922,13 @@ static bool compareString8AndCString(const String8& str, const char* cStr) { return strcmp(str.string(), cStr) < 0; } -void ResTable::getLocales(Vector<String8>* locales, bool includeSystemLocales) const { +void ResTable::getLocales(Vector<String8>* locales, bool includeSystemLocales, + bool mergeEquivalentLangs) const { char locale[RESTABLE_MAX_LOCALE_LEN]; forEachConfiguration(false, false, includeSystemLocales, [&](const ResTable_config& cfg) { if (cfg.locale != 0) { - cfg.getBcp47Locale(locale); + cfg.getBcp47Locale(locale, mergeEquivalentLangs /* canonicalize if merging */); const auto beginIter = locales->begin(); const auto endIter = locales->end(); diff --git a/libs/androidfw/include/androidfw/ApkAssets.h b/libs/androidfw/include/androidfw/ApkAssets.h new file mode 100644 index 000000000000..a3d67f04eb90 --- /dev/null +++ b/libs/androidfw/include/androidfw/ApkAssets.h @@ -0,0 +1,62 @@ +/* + * 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. + */ + +#ifndef APKASSETS_H_ +#define APKASSETS_H_ + +#include <memory> +#include <string> + +#include "android-base/macros.h" +#include "ziparchive/zip_archive.h" + +#include "androidfw/Asset.h" +#include "androidfw/LoadedArsc.h" + +namespace android { + +// Holds an APK. +class ApkAssets { + public: + static std::unique_ptr<ApkAssets> Load(const std::string& path); + + std::unique_ptr<Asset> Open(const std::string& path, + Asset::AccessMode mode = Asset::AccessMode::ACCESS_RANDOM) const; + + inline const std::string& GetPath() const { return path_; } + + inline const LoadedArsc* GetLoadedArsc() const { return loaded_arsc_.get(); } + + private: + DISALLOW_COPY_AND_ASSIGN(ApkAssets); + + ApkAssets() = default; + + struct ZipArchivePtrCloser { + void operator()(::ZipArchiveHandle handle) { ::CloseArchive(handle); } + }; + + using ZipArchivePtr = + std::unique_ptr<typename std::remove_pointer<::ZipArchiveHandle>::type, ZipArchivePtrCloser>; + ZipArchivePtr zip_handle_; + std::string path_; + std::unique_ptr<Asset> resources_asset_; + std::unique_ptr<LoadedArsc> loaded_arsc_; +}; + +} // namespace android + +#endif /* APKASSETS_H_ */ diff --git a/libs/androidfw/include/androidfw/Asset.h b/libs/androidfw/include/androidfw/Asset.h index 2cd8c0bf3c78..461e773e5818 100644 --- a/libs/androidfw/include/androidfw/Asset.h +++ b/libs/androidfw/include/androidfw/Asset.h @@ -26,11 +26,12 @@ #include <utils/Compat.h> #include <utils/Errors.h> -#include <utils/FileMap.h> #include <utils/String8.h> namespace android { +class FileMap; + /* * Instances of this class provide read-only operations on a byte stream. * diff --git a/libs/androidfw/include/androidfw/AssetManager.h b/libs/androidfw/include/androidfw/AssetManager.h index 594dba5049dd..becd307d114d 100644 --- a/libs/androidfw/include/androidfw/AssetManager.h +++ b/libs/androidfw/include/androidfw/AssetManager.h @@ -50,10 +50,7 @@ struct ResTable_config; * single instance may be shared across multiple threads, and a single * thread may have more than one instance (the latter is discouraged). * - * The purpose of the AssetManager is to create Asset objects. To do - * this efficiently it may cache information about the locations of - * files it has seen. This can be controlled with the "cacheMode" - * argument. + * The purpose of the AssetManager is to create Asset objects. * * The asset hierarchy may be examined like a filesystem, using * AssetDir objects to peruse a single directory. @@ -69,18 +66,16 @@ public: * OVERLAY_DIR. */ static const char* OVERLAY_THEME_DIR_PROPERTY; + /** + * If OVERLAY_THEME_DIR_PERSIST_PROPERTY, use it to override + * OVERLAY_THEME_DIR_PROPERTY. + */ + static const char* OVERLAY_THEME_DIR_PERSIST_PROPERTY; static const char* TARGET_PACKAGE_NAME; static const char* TARGET_APK_PATH; static const char* IDMAP_DIR; - typedef enum CacheMode { - CACHE_UNKNOWN = 0, - CACHE_OFF, // don't try to cache file locations - CACHE_DEFER, // construct cache as pieces are needed - //CACHE_SCAN, // scan full(!) asset hierarchy at init() time - } CacheMode; - - AssetManager(CacheMode cacheMode = CACHE_OFF); + AssetManager(); virtual ~AssetManager(void); static int32_t getGlobalCount(); @@ -117,23 +112,16 @@ public: int32_t nextAssetPath(const int32_t cookie) const; /* - * Return an asset path in the manager. 'which' must be between 0 and - * countAssetPaths(). + * Return an asset path in the manager. 'cookie' must be a non-negative value + * previously returned from addAssetPath() or nextAssetPath(). */ String8 getAssetPath(const int32_t cookie) const; /* - * Set the current locale and vendor. The locale can change during - * the lifetime of an AssetManager if the user updates the device's - * language setting. The vendor is less likely to change. - * - * Pass in NULL to indicate no preference. - */ - void setLocale(const char* locale); - void setVendor(const char* vendor); - - /* - * Choose screen orientation for resources values returned. + * Sets various device configuration parameters, like screen orientation, layout, + * size, locale, etc. + * The optional 'locale' string takes precedence over the locale within 'config' + * and must be in bcp47 format. */ void setConfiguration(const ResTable_config& config, const char* locale = NULL); @@ -144,9 +132,6 @@ public: /* * Open an asset. * - * This will search through locale-specific and vendor-specific - * directories and packages to find the file. - * * The object returned does not depend on the AssetManager. It should * be freed by calling Asset::close(). */ @@ -156,9 +141,8 @@ public: * Open a non-asset file as an asset. * * This is for opening files that are included in an asset package - * but aren't assets. These sit outside the usual "locale/vendor" - * path hierarchy, and will not be seen by "AssetDir" or included - * in our filename cache. + * but aren't assets. These sit outside the usual "assets/" + * path hierarchy, and will not be seen by "AssetDir". */ Asset* openNonAsset(const char* fileName, AccessMode mode, int32_t* outCookie = NULL); @@ -171,11 +155,6 @@ public: /* * Open a directory within the asset hierarchy. * - * The contents of the directory are an amalgam of vendor-specific, - * locale-specific, and generic assets stored loosely or in asset - * packages. Depending on the cache setting and previous accesses, - * this call may incur significant disk overhead. - * * To open the top-level directory, pass in "". */ AssetDir* openDir(const char* dirName); @@ -183,11 +162,6 @@ public: /* * Open a directory within a particular path of the asset manager. * - * The contents of the directory are an amalgam of vendor-specific, - * locale-specific, and generic assets stored loosely or in asset - * packages. Depending on the cache setting and previous accesses, - * this call may incur significant disk overhead. - * * To open the top-level directory, pass in "". */ AssetDir* openNonAssetDir(const int32_t cookie, const char* dirName); @@ -206,13 +180,6 @@ public: const ResTable& getResources(bool required = true) const; /* - * Discard cached filename information. This only needs to be called - * if somebody has updated the set of "loose" files, and we want to - * discard our cached notion of what's where. - */ - void purge(void) { purgeFileNameCacheLocked(); } - - /* * Return true if the files this AssetManager references are all * up-to-date (have not been changed since it was created). If false * is returned, you will need to create a new AssetManager to get @@ -244,14 +211,8 @@ private: bool isSystemAsset; }; - Asset* openInPathLocked(const char* fileName, AccessMode mode, - const asset_path& path); Asset* openNonAssetInPathLocked(const char* fileName, AccessMode mode, const asset_path& path); - Asset* openInLocaleVendorLocked(const char* fileName, AccessMode mode, - const asset_path& path, const char* locale, const char* vendor); - String8 createPathNameLocked(const asset_path& path, const char* locale, - const char* vendor); String8 createPathNameLocked(const asset_path& path, const char* rootDir); String8 createZipSourceNameLocked(const String8& zipFileName, const String8& dirName, const String8& fileName); @@ -269,15 +230,6 @@ private: void mergeInfoLocked(SortedVector<AssetDir::FileInfo>* pMergedInfo, const SortedVector<AssetDir::FileInfo>* pContents); - void loadFileNameCacheLocked(void); - void fncScanLocked(SortedVector<AssetDir::FileInfo>* pMergedInfo, - const char* dirName); - bool fncScanAndMergeDirLocked( - SortedVector<AssetDir::FileInfo>* pMergedInfo, - const asset_path& path, const char* locale, const char* vendor, - const char* dirName); - void purgeFileNameCacheLocked(void); - const ResTable* getResTable(bool required = true) const; void setLocaleLocked(const char* locale); void updateResourceParamsLocked() const; @@ -334,8 +286,8 @@ private: */ class ZipSet { public: - ZipSet(void); - ~ZipSet(void); + ZipSet() = default; + ~ZipSet(); /* * Return a ZipFileRO structure for a ZipFileRO with the specified @@ -372,23 +324,9 @@ private: Vector<asset_path> mAssetPaths; char* mLocale; - char* mVendor; mutable ResTable* mResources; ResTable_config* mConfig; - - /* - * Cached data for "loose" files. This lets us avoid poking at the - * filesystem when searching for loose assets. Each entry is the - * "extended partial" path, e.g. "default/default/foo/bar.txt". The - * full set of files is present, including ".EXCLUDE" entries. - * - * We do not cache directory names. We don't retain the ".gz", - * because to our clients "foo" and "foo.gz" both look like "foo". - */ - CacheMode mCacheMode; // is the cache enabled? - bool mCacheValid; // clear when locale or vendor changes - SortedVector<AssetDir::FileInfo> mCache; }; }; // namespace android diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h new file mode 100644 index 000000000000..66d5034463a5 --- /dev/null +++ b/libs/androidfw/include/androidfw/AssetManager2.h @@ -0,0 +1,298 @@ +/* + * 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. + */ + +#ifndef ANDROIDFW_ASSETMANAGER2_H_ +#define ANDROIDFW_ASSETMANAGER2_H_ + +#include "android-base/macros.h" + +#include <limits> +#include <unordered_map> + +#include "androidfw/ApkAssets.h" +#include "androidfw/Asset.h" +#include "androidfw/AssetManager.h" +#include "androidfw/ResourceTypes.h" +#include "androidfw/Util.h" + +namespace android { + +class Theme; + +using ApkAssetsCookie = int32_t; + +enum : ApkAssetsCookie { + kInvalidCookie = -1, +}; + +// Holds a bag that has been merged with its parent, if one exists. +struct ResolvedBag { + // A single key-value entry in a bag. + struct Entry { + // The key, as described in ResTable_map::name. + uint32_t key; + + Res_value value; + + // Which ApkAssets this entry came from. + ApkAssetsCookie cookie; + + ResStringPool* key_pool; + ResStringPool* type_pool; + }; + + // Denotes the configuration axis that this bag varies with. + // If a configuration changes with respect to one of these axis, + // the bag should be reloaded. + uint32_t type_spec_flags; + + // The number of entries in this bag. Access them by indexing into `entries`. + uint32_t entry_count; + + // The array of entries for this bag. An empty array is a neat trick to force alignment + // of the Entry structs that follow this structure and avoids a bunch of casts. + Entry entries[0]; +}; + +// 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 { + public: + struct ResourceName { + const char* package = nullptr; + size_t package_len = 0u; + + const char* type = nullptr; + const char16_t* type16 = nullptr; + size_t type_len = 0u; + + const char* entry = nullptr; + const char16_t* entry16 = nullptr; + size_t entry_len = 0u; + }; + + AssetManager2(); + + // Sets/resets the underlying ApkAssets for this AssetManager. The ApkAssets + // are not owned by the AssetManager, and must have a longer lifetime. + // + // Only pass invalidate_caches=false when it is known that the structure + // change in ApkAssets is due to a safe addition of resources with completely + // new resource IDs. + bool SetApkAssets(const std::vector<const ApkAssets*>& apk_assets, bool invalidate_caches = true); + + const std::vector<const ApkAssets*> GetApkAssets() const; + + // 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. + const ResStringPool* GetStringPoolForCookie(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); + + const ResTable_config& GetConfiguration() 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); + + // 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); + + // Searches the set of APKs loaded by this AssetManager and opens the first one found. + // `mode` controls how the file is opened. + // `out_cookie` is populated with the cookie of the APK this file was found in. + // + // 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); + + // 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); + + // 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); + + // 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); + + // 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`. + // `out_flags` holds the same flags as retrieved with GetResourceFlags(). + // If `density_override` is non-zero, the configuration to match against is overridden with that + // density. + // + // Returns a valid cookie if the resource was found. If the resource was not found, or if the + // resource was a map/bag type, then kInvalidCookie is returned. If `may_be_bag` is false, + // 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); + + // 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. + // To iterate over the keys, use the following idiom: + // + // const AssetManager2::ResolvedBag* bag = asset_manager->GetBag(id); + // if (bag != nullptr) { + // for (auto iter = begin(bag); iter != end(bag); ++iter) { + // ... + // } + // } + const ResolvedBag* GetBag(uint32_t resid); + + // Creates a new Theme from this AssetManager. + std::unique_ptr<Theme> NewTheme(); + + 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. + // + // `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. + // + // `out_flags` stores the resulting bitmask of configuration axis with which the resource + // value varies. + ApkAssetsCookie FindEntry(uint32_t resid, uint16_t density_override, bool stop_at_first_match, + LoadedArsc::Entry* out_entry, ResTable_config* out_selected_config, + uint32_t* out_flags); + + // Purge all resources that are cached and vary by the configuration axis denoted by the + // bitmask `diff`. + void InvalidateCaches(uint32_t diff); + + // 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_; + + // The current configuration set for this AssetManager. When this changes, cached resources + // may need to be purged. + ResTable_config configuration_; + + // Cached set of bags. These are cached because they can inherit keys from parent bags, + // which involves some calculation. + std::unordered_map<uint32_t, util::unique_cptr<ResolvedBag>> cached_bags_; +}; + +class Theme { + friend class AssetManager2; + + public: + // 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 + // to true, this behavior is changed and all theme attributes from the style at + // `resid` are applied. + // Returns false if the style failed to apply. + bool ApplyStyle(uint32_t resid, bool force = false); + + // Sets this Theme to be a copy of `o` if `o` has the same AssetManager as this Theme. + // Returns false if the AssetManagers of the Themes were not compatible. + bool SetTo(const Theme& o); + + void Clear(); + + inline const AssetManager2* GetAssetManager() const { 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. + // + // 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; + + // This is like AssetManager2::ResolveReference(), but also takes + // care of resolving attribute references to the theme. + ApkAssetsCookie ResolveAttributeReference(Res_value* in_out_value, ApkAssetsCookie src_cookie, + uint32_t* out_last_ref = nullptr, + uint32_t* in_out_type_spec_flags = nullptr, + ResTable_config* out_selected_config = 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. + util::unique_cptr<Type> types[kTypeCount]; + }; + + AssetManager2* asset_manager_; + uint32_t type_spec_flags_ = 0u; + std::unique_ptr<Package> packages_[kPackageCount]; +}; + +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; +} + +} // namespace android + +#endif /* ANDROIDFW_ASSETMANAGER2_H_ */ diff --git a/libs/androidfw/include/androidfw/AttributeFinder.h b/libs/androidfw/include/androidfw/AttributeFinder.h index acf70565c4f9..f281921824e7 100644 --- a/libs/androidfw/include/androidfw/AttributeFinder.h +++ b/libs/androidfw/include/androidfw/AttributeFinder.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014 The Android Open Source Project + * 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. @@ -14,17 +14,16 @@ * limitations under the License. */ -#ifndef H_ATTRIBUTE_FINDER -#define H_ATTRIBUTE_FINDER +#ifndef ANDROIDFW_ATTRIBUTE_FINDER_H +#define ANDROIDFW_ATTRIBUTE_FINDER_H -#include <stdint.h> #include <utils/KeyedVector.h> +#include <stdint.h> + namespace android { -static inline uint32_t getPackage(uint32_t attr) { - return attr >> 24; -} +static inline uint32_t get_package(uint32_t attr) { return attr >> 24; } /** * A helper class to search linearly for the requested @@ -55,152 +54,153 @@ static inline uint32_t getPackage(uint32_t attr) { */ template <typename Derived, typename Iterator> class BackTrackingAttributeFinder { -public: - BackTrackingAttributeFinder(const Iterator& begin, const Iterator& end); + public: + BackTrackingAttributeFinder(const Iterator& begin, const Iterator& end); - Iterator find(uint32_t attr); + Iterator Find(uint32_t attr); -private: - void jumpToClosestAttribute(uint32_t packageId); - void markCurrentPackageId(uint32_t packageId); + private: + void JumpToClosestAttribute(uint32_t package_id); + void MarkCurrentPackageId(uint32_t package_id); - bool mFirstTime; - Iterator mBegin; - Iterator mEnd; - Iterator mCurrent; - Iterator mLargest; - uint32_t mLastPackageId; - uint32_t mCurrentAttr; + bool first_time_; + Iterator begin_; + Iterator end_; + Iterator current_; + Iterator largest_; + uint32_t last_package_id_; + uint32_t current_attr_; - // Package Offsets (best-case, fast look-up). - Iterator mFrameworkStart; - Iterator mAppStart; + // Package offsets (best-case, fast look-up). + Iterator framework_start_; + Iterator app_start_; - // Worst case, we have shared-library resources. - KeyedVector<uint32_t, Iterator> mPackageOffsets; + // Worst case, we have shared-library resources. + KeyedVector<uint32_t, Iterator> package_offsets_; }; -template <typename Derived, typename Iterator> inline -BackTrackingAttributeFinder<Derived, Iterator>::BackTrackingAttributeFinder(const Iterator& begin, const Iterator& end) - : mFirstTime(true) - , mBegin(begin) - , mEnd(end) - , mCurrent(begin) - , mLargest(begin) - , mLastPackageId(0) - , mCurrentAttr(0) - , mFrameworkStart(end) - , mAppStart(end) { -} +template <typename Derived, typename Iterator> +inline BackTrackingAttributeFinder<Derived, Iterator>::BackTrackingAttributeFinder( + const Iterator& begin, const Iterator& end) + : first_time_(true), + begin_(begin), + end_(end), + current_(begin), + largest_(begin), + last_package_id_(0), + current_attr_(0), + framework_start_(end), + app_start_(end) {} template <typename Derived, typename Iterator> -void BackTrackingAttributeFinder<Derived, Iterator>::jumpToClosestAttribute(const uint32_t packageId) { - switch (packageId) { - case 0x01: - mCurrent = mFrameworkStart; - break; - case 0x7f: - mCurrent = mAppStart; - break; - default: { - ssize_t idx = mPackageOffsets.indexOfKey(packageId); - if (idx >= 0) { - // We have seen this package ID before, so jump to the first - // attribute with this package ID. - mCurrent = mPackageOffsets[idx]; - } else { - mCurrent = mEnd; - } - break; - } +void BackTrackingAttributeFinder<Derived, Iterator>::JumpToClosestAttribute( + const uint32_t package_id) { + switch (package_id) { + case 0x01u: + current_ = framework_start_; + break; + case 0x7fu: + current_ = app_start_; + break; + default: { + ssize_t idx = package_offsets_.indexOfKey(package_id); + if (idx >= 0) { + // We have seen this package ID before, so jump to the first + // attribute with this package ID. + current_ = package_offsets_[idx]; + } else { + current_ = end_; + } + break; } + } - // We have never seen this package ID yet, so jump to the - // latest/largest index we have processed so far. - if (mCurrent == mEnd) { - mCurrent = mLargest; - } + // We have never seen this package ID yet, so jump to the + // latest/largest index we have processed so far. + if (current_ == end_) { + current_ = largest_; + } - if (mCurrent != mEnd) { - mCurrentAttr = static_cast<const Derived*>(this)->getAttribute(mCurrent); - } + if (current_ != end_) { + current_attr_ = static_cast<const Derived*>(this)->GetAttribute(current_); + } } template <typename Derived, typename Iterator> -void BackTrackingAttributeFinder<Derived, Iterator>::markCurrentPackageId(const uint32_t packageId) { - switch (packageId) { - case 0x01: - mFrameworkStart = mCurrent; - break; - case 0x7f: - mAppStart = mCurrent; - break; - default: - mPackageOffsets.add(packageId, mCurrent); - break; - } +void BackTrackingAttributeFinder<Derived, Iterator>::MarkCurrentPackageId( + const uint32_t package_id) { + switch (package_id) { + case 0x01u: + framework_start_ = current_; + break; + case 0x7fu: + app_start_ = current_; + break; + default: + package_offsets_.add(package_id, current_); + break; + } } template <typename Derived, typename Iterator> -Iterator BackTrackingAttributeFinder<Derived, Iterator>::find(uint32_t attr) { - if (!(mBegin < mEnd)) { - return mEnd; +Iterator BackTrackingAttributeFinder<Derived, Iterator>::Find(uint32_t attr) { + if (!(begin_ < end_)) { + return end_; + } + + if (first_time_) { + // One-time initialization. We do this here instead of the constructor + // because the derived class we access in getAttribute() may not be + // fully constructed. + first_time_ = false; + current_attr_ = static_cast<const Derived*>(this)->GetAttribute(begin_); + last_package_id_ = get_package(current_attr_); + MarkCurrentPackageId(last_package_id_); + } + + // Looking for the needle (attribute we're looking for) + // in the haystack (the attributes we're searching through) + const uint32_t needle_package_id = get_package(attr); + if (last_package_id_ != needle_package_id) { + JumpToClosestAttribute(needle_package_id); + last_package_id_ = needle_package_id; + } + + // Walk through the xml attributes looking for the requested attribute. + while (current_ != end_) { + const uint32_t haystack_package_id = get_package(current_attr_); + if (needle_package_id == haystack_package_id && attr < current_attr_) { + // The attribute we are looking was not found. + break; } - - if (mFirstTime) { - // One-time initialization. We do this here instead of the constructor - // because the derived class we access in getAttribute() may not be - // fully constructed. - mFirstTime = false; - mCurrentAttr = static_cast<const Derived*>(this)->getAttribute(mBegin); - mLastPackageId = getPackage(mCurrentAttr); - markCurrentPackageId(mLastPackageId); + const uint32_t prev_attr = current_attr_; + + // Move to the next attribute in the XML. + ++current_; + if (current_ != end_) { + current_attr_ = static_cast<const Derived*>(this)->GetAttribute(current_); + const uint32_t new_haystack_package_id = get_package(current_attr_); + if (haystack_package_id != new_haystack_package_id) { + // We've moved to the next group of attributes + // with a new package ID, so we should record + // the offset of this new package ID. + MarkCurrentPackageId(new_haystack_package_id); + } } - // Looking for the needle (attribute we're looking for) - // in the haystack (the attributes we're searching through) - const uint32_t needlePackageId = getPackage(attr); - if (mLastPackageId != needlePackageId) { - jumpToClosestAttribute(needlePackageId); - mLastPackageId = needlePackageId; + if (current_ > largest_) { + // We've moved past the latest attribute we've seen. + largest_ = current_; } - // Walk through the xml attributes looking for the requested attribute. - while (mCurrent != mEnd) { - const uint32_t haystackPackageId = getPackage(mCurrentAttr); - if (needlePackageId == haystackPackageId && attr < mCurrentAttr) { - // The attribute we are looking was not found. - break; - } - const uint32_t prevAttr = mCurrentAttr; - - // Move to the next attribute in the XML. - ++mCurrent; - if (mCurrent != mEnd) { - mCurrentAttr = static_cast<const Derived*>(this)->getAttribute(mCurrent); - const uint32_t newHaystackPackageId = getPackage(mCurrentAttr); - if (haystackPackageId != newHaystackPackageId) { - // We've moved to the next group of attributes - // with a new package ID, so we should record - // the offset of this new package ID. - markCurrentPackageId(newHaystackPackageId); - } - } - - if (mCurrent > mLargest) { - // We've moved past the latest attribute we've - // seen. - mLargest = mCurrent; - } - - if (attr == prevAttr) { - // We found the attribute we were looking for. - return mCurrent - 1; - } + if (attr == prev_attr) { + // We found the attribute we were looking for. + return current_ - 1; } - return mEnd; + } + return end_; } -} // namespace android +} // namespace android -#endif // H_ATTRIBUTE_FINDER +#endif // ANDROIDFW_ATTRIBUTE_FINDER_H diff --git a/libs/androidfw/include/androidfw/AttributeResolution.h b/libs/androidfw/include/androidfw/AttributeResolution.h new file mode 100644 index 000000000000..69b760414846 --- /dev/null +++ b/libs/androidfw/include/androidfw/AttributeResolution.h @@ -0,0 +1,62 @@ +/* + * 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. + */ + +#ifndef ANDROIDFW_ATTRIBUTERESOLUTION_H +#define ANDROIDFW_ATTRIBUTERESOLUTION_H + +#include <androidfw/ResourceTypes.h> + +namespace android { + +// Offsets into the outValues array populated by the methods below. outValues is a uint32_t +// array, but each logical element takes up 6 uint32_t-sized physical elements. +enum { + STYLE_NUM_ENTRIES = 6, + STYLE_TYPE = 0, + STYLE_DATA = 1, + STYLE_ASSET_COOKIE = 2, + STYLE_RESOURCE_ID = 3, + STYLE_CHANGING_CONFIGURATIONS = 4, + STYLE_DENSITY = 5 +}; + +// These are all variations of the same method. They each perform the exact same operation, +// but on various data sources. I *think* they are re-written to avoid an extra branch +// in the inner loop, but after one branch miss (some pointer != null), the branch predictor should +// predict the rest of the iterations' branch correctly. +// TODO(adamlesinski): Run performance tests against these methods and a new, single method +// that uses all the sources and branches to the right ones within the inner loop. + +// `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, + 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, + 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, + size_t attrs_length, uint32_t* out_values, uint32_t* out_indices); + +} // namespace android + +#endif /* ANDROIDFW_ATTRIBUTERESOLUTION_H */ diff --git a/libs/androidfw/include/androidfw/ByteBucketArray.h b/libs/androidfw/include/androidfw/ByteBucketArray.h index 87c6b128eca1..d84a207697e9 100644 --- a/libs/androidfw/include/androidfw/ByteBucketArray.h +++ b/libs/androidfw/include/androidfw/ByteBucketArray.h @@ -17,9 +17,10 @@ #ifndef __BYTE_BUCKET_ARRAY_H #define __BYTE_BUCKET_ARRAY_H -#include <utils/Log.h> -#include <stdint.h> -#include <string.h> +#include <cstdint> +#include <cstring> + +#include "android-base/logging.h" namespace android { @@ -27,71 +28,65 @@ namespace android { * Stores a sparsely populated array. Has a fixed size of 256 * (number of entries that a byte can represent). */ -template<typename T> +template <typename T> class ByteBucketArray { -public: - ByteBucketArray() : mDefault() { - memset(mBuckets, 0, sizeof(mBuckets)); + public: + ByteBucketArray() : default_() { memset(buckets_, 0, sizeof(buckets_)); } + + ~ByteBucketArray() { + for (size_t i = 0; i < kNumBuckets; i++) { + if (buckets_[i] != NULL) { + delete[] buckets_[i]; + } } + memset(buckets_, 0, sizeof(buckets_)); + } - ~ByteBucketArray() { - for (size_t i = 0; i < NUM_BUCKETS; i++) { - if (mBuckets[i] != NULL) { - delete [] mBuckets[i]; - } - } - memset(mBuckets, 0, sizeof(mBuckets)); - } + inline size_t size() const { return kNumBuckets * kBucketSize; } - inline size_t size() const { - return NUM_BUCKETS * BUCKET_SIZE; - } + inline const T& get(size_t index) const { return (*this)[index]; } - inline const T& get(size_t index) const { - return (*this)[index]; + const T& operator[](size_t index) const { + if (index >= size()) { + return default_; } - const T& operator[](size_t index) const { - if (index >= size()) { - return mDefault; - } - - uint8_t bucketIndex = static_cast<uint8_t>(index) >> 4; - T* bucket = mBuckets[bucketIndex]; - if (bucket == NULL) { - return mDefault; - } - return bucket[0x0f & static_cast<uint8_t>(index)]; + uint8_t bucket_index = static_cast<uint8_t>(index) >> 4; + T* bucket = buckets_[bucket_index]; + if (bucket == NULL) { + return default_; } + return bucket[0x0f & static_cast<uint8_t>(index)]; + } - T& editItemAt(size_t index) { - ALOG_ASSERT(index < size(), "ByteBucketArray.getOrCreate(index=%u) with size=%u", - (uint32_t) index, (uint32_t) size()); + T& editItemAt(size_t index) { + CHECK(index < size()) << "ByteBucketArray.getOrCreate(index=" << index + << ") with size=" << size(); - uint8_t bucketIndex = static_cast<uint8_t>(index) >> 4; - T* bucket = mBuckets[bucketIndex]; - if (bucket == NULL) { - bucket = mBuckets[bucketIndex] = new T[BUCKET_SIZE](); - } - return bucket[0x0f & static_cast<uint8_t>(index)]; + uint8_t bucket_index = static_cast<uint8_t>(index) >> 4; + T* bucket = buckets_[bucket_index]; + if (bucket == NULL) { + bucket = buckets_[bucket_index] = new T[kBucketSize](); } + return bucket[0x0f & static_cast<uint8_t>(index)]; + } - bool set(size_t index, const T& value) { - if (index >= size()) { - return false; - } - - editItemAt(index) = value; - return true; + bool set(size_t index, const T& value) { + if (index >= size()) { + return false; } -private: - enum { NUM_BUCKETS = 16, BUCKET_SIZE = 16 }; + editItemAt(index) = value; + return true; + } + + private: + enum { kNumBuckets = 16, kBucketSize = 16 }; - T* mBuckets[NUM_BUCKETS]; - T mDefault; + T* buckets_[kNumBuckets]; + T default_; }; -} // namespace android +} // namespace android -#endif // __BYTE_BUCKET_ARRAY_H +#endif // __BYTE_BUCKET_ARRAY_H diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h new file mode 100644 index 000000000000..e2e56c88ee1d --- /dev/null +++ b/libs/androidfw/include/androidfw/LoadedArsc.h @@ -0,0 +1,83 @@ +/* + * 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. + */ + +#ifndef LOADEDARSC_H_ +#define LOADEDARSC_H_ + +#include <memory> +#include <vector> + +#include "android-base/macros.h" + +#include "androidfw/ResourceTypes.h" + +namespace android { + +class Chunk; +class LoadedPackage; + +// Read-only view into a resource table. This class validates all data +// when loading, including offsets and lengths. +class LoadedArsc { + public: + // Load the resource table from memory. The data's lifetime must out-live the + // object returned from this method. + static std::unique_ptr<LoadedArsc> Load(const void* data, size_t len); + + ~LoadedArsc(); + + // 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_; } + + struct Entry { + // 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; + + // 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; + }; + + // 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, Entry* out_entry, + ResTable_config* selected_config, uint32_t* out_flags) const; + + // Gets a pointer to the name of the package in `resid`, or nullptr if the package doesn't exist. + const std::string* GetPackageNameForId(uint32_t resid) const; + + private: + DISALLOW_COPY_AND_ASSIGN(LoadedArsc); + + LoadedArsc() = default; + bool LoadTable(const Chunk& chunk); + + ResStringPool global_string_pool_; + std::vector<std::unique_ptr<LoadedPackage>> packages_; +}; + +} // namespace android + +#endif /* LOADEDARSC_H_ */ diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h index 12a6b0f9a4ec..c118b57510f9 100644 --- a/libs/androidfw/include/androidfw/ResourceTypes.h +++ b/libs/androidfw/include/androidfw/ResourceTypes.h @@ -22,7 +22,6 @@ #include <androidfw/Asset.h> #include <androidfw/LocaleData.h> -#include <utils/ByteOrder.h> #include <utils/Errors.h> #include <utils/String16.h> #include <utils/Vector.h> @@ -266,7 +265,7 @@ struct Res_value uint8_t res0; // Type of the data value. - enum { + enum : uint8_t { // The 'data' is either 0 or 1, specifying this resource is either // undefined or empty, respectively. TYPE_NULL = 0x00, @@ -1103,6 +1102,7 @@ struct ResTable_config UI_MODE_TYPE_TELEVISION = ACONFIGURATION_UI_MODE_TYPE_TELEVISION, UI_MODE_TYPE_APPLIANCE = ACONFIGURATION_UI_MODE_TYPE_APPLIANCE, UI_MODE_TYPE_WATCH = ACONFIGURATION_UI_MODE_TYPE_WATCH, + UI_MODE_TYPE_VR_HEADSET = ACONFIGURATION_UI_MODE_TYPE_VR_HEADSET, // uiMode bits for the night switch. MASK_UI_MODE_NIGHT = 0x30, @@ -1227,7 +1227,10 @@ struct ResTable_config // |RESTABLE_MAX_LOCALE_LEN| (including a terminating '\0'). // // Example: en-US, en-Latn-US, en-POSIX. - void getBcp47Locale(char* out) const; + // + // If canonicalize is set, Tagalog (tl) locales get converted + // to Filipino (fil). + void getBcp47Locale(char* out, bool canonicalize=false) const; // Append to str the resource-qualifer string representation of the // locale component of this Config. If the locale is only country @@ -1849,7 +1852,8 @@ public: void getConfigurations(Vector<ResTable_config>* configs, bool ignoreMipmap=false, bool ignoreAndroidPackage=false, bool includeSystemConfigs=true) const; - void getLocales(Vector<String8>* locales, bool includeSystemLocales=true) const; + void getLocales(Vector<String8>* locales, bool includeSystemLocales=true, + bool mergeEquivalentLangs=false) const; // Generate an idmap. // diff --git a/libs/androidfw/include/androidfw/TypeWrappers.h b/libs/androidfw/include/androidfw/TypeWrappers.h index fd848736d2d6..f1daf3365c28 100644 --- a/libs/androidfw/include/androidfw/TypeWrappers.h +++ b/libs/androidfw/include/androidfw/TypeWrappers.h @@ -18,6 +18,7 @@ #define __TYPE_WRAPPERS_H #include <androidfw/ResourceTypes.h> +#include <utils/ByteOrder.h> namespace android { diff --git a/libs/androidfw/include/androidfw/Util.h b/libs/androidfw/include/androidfw/Util.h new file mode 100644 index 000000000000..5266d09e84e3 --- /dev/null +++ b/libs/androidfw/include/androidfw/Util.h @@ -0,0 +1,127 @@ +/* + * 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. + */ + +#ifndef UTIL_H_ +#define UTIL_H_ + +#include <cstdlib> +#include <memory> + +#include "android-base/macros.h" + +namespace android { +namespace util { + +/** + * Makes a std::unique_ptr<> with the template parameter inferred by the + * compiler. + * This will be present in C++14 and can be removed then. + */ +template <typename T, class... Args> +std::unique_ptr<T> make_unique(Args&&... args) { + return std::unique_ptr<T>(new T{std::forward<Args>(args)...}); +} + +// Based on std::unique_ptr, but uses free() to release malloc'ed memory +// without incurring the size increase of holding on to a custom deleter. +template <typename T> +class unique_cptr { + public: + using pointer = typename std::add_pointer<T>::type; + + constexpr unique_cptr() : ptr_(nullptr) {} + constexpr unique_cptr(std::nullptr_t) : ptr_(nullptr) {} + explicit unique_cptr(pointer ptr) : ptr_(ptr) {} + unique_cptr(unique_cptr&& o) : ptr_(o.ptr_) { o.ptr_ = nullptr; } + + ~unique_cptr() { std::free(reinterpret_cast<void*>(ptr_)); } + + inline unique_cptr& operator=(unique_cptr&& o) { + if (&o == this) { + return *this; + } + + std::free(reinterpret_cast<void*>(ptr_)); + ptr_ = o.ptr_; + o.ptr_ = nullptr; + return *this; + } + + inline unique_cptr& operator=(std::nullptr_t) { + std::free(reinterpret_cast<void*>(ptr_)); + ptr_ = nullptr; + return *this; + } + + pointer release() { + pointer result = ptr_; + ptr_ = nullptr; + return result; + } + + inline pointer get() const { return ptr_; } + + void reset(pointer ptr = pointer()) { + if (ptr == ptr_) { + return; + } + + pointer old_ptr = ptr_; + ptr_ = ptr; + std::free(reinterpret_cast<void*>(old_ptr)); + } + + inline void swap(unique_cptr& o) { std::swap(ptr_, o.ptr_); } + + inline explicit operator bool() const { return ptr_ != nullptr; } + + inline typename std::add_lvalue_reference<T>::type operator*() const { return *ptr_; } + + inline pointer operator->() const { return ptr_; } + + inline bool operator==(const unique_cptr& o) const { return ptr_ == o.ptr_; } + + inline bool operator==(std::nullptr_t) const { return ptr_ == nullptr; } + + private: + DISALLOW_COPY_AND_ASSIGN(unique_cptr); + + pointer ptr_; +}; + +inline uint8_t get_package_id(uint32_t resid) { + return static_cast<uint8_t>((resid >> 24) & 0x000000ffu); +} + +// The type ID is 1-based, so if the returned value is 0 it is invalid. +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 bool is_internal_id(uint32_t resid) { + return (resid & 0xffff0000u) != 0 && (resid & 0x00ff0000u) == 0; +} + +inline bool is_valid_resid(uint32_t resid) { + return (resid & 0x00ff0000u) != 0 && (resid & 0xff000000u) != 0; +} + +} // namespace util +} // namespace android + +#endif /* UTIL_H_ */ diff --git a/libs/androidfw/tests/Android.mk b/libs/androidfw/tests/Android.mk index 1fe1773578fa..6754cd89944a 100644 --- a/libs/androidfw/tests/Android.mk +++ b/libs/androidfw/tests/Android.mk @@ -21,26 +21,37 @@ 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 \ ResTable_test.cpp \ Split_test.cpp \ TestHelpers.cpp \ + TestMain.cpp \ Theme_test.cpp \ TypeWrappers_test.cpp \ ZipUtils_test.cpp +benchmarkFiles := \ + AssetManager2_bench.cpp \ + BenchMain.cpp \ + TestHelpers.cpp \ + Theme_bench.cpp + androidfw_test_cflags := \ -Wall \ -Werror \ -Wunused \ -Wunreachable-code \ - -Wno-missing-field-initializers \ + -Wno-missing-field-initializers # gtest is broken. androidfw_test_cflags += -Wno-unnamed-type-template-args @@ -55,10 +66,13 @@ 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) @@ -76,9 +90,32 @@ LOCAL_SRC_FILES := $(testFiles) \ 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 new file mode 100644 index 000000000000..3a1fc8fa0d3f --- /dev/null +++ b/libs/androidfw/tests/ApkAssets_test.cpp @@ -0,0 +1,34 @@ +/* + * 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 "androidfw/ApkAssets.h" + +#include "TestHelpers.h" +#include "data/basic/R.h" + +using com::android::basic::R; + +namespace android { + +TEST(ApkAssetsTest, LoadApk) { + std::unique_ptr<ApkAssets> loaded_apk = ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk"); + ASSERT_NE(nullptr, loaded_apk); + + std::unique_ptr<Asset> asset = loaded_apk->Open("res/layout/main.xml"); + ASSERT_NE(nullptr, asset); +} + +} // namespace android diff --git a/libs/androidfw/tests/AppAsLib_test.cpp b/libs/androidfw/tests/AppAsLib_test.cpp index 8489acf6246f..ddaa46d274cd 100644 --- a/libs/androidfw/tests/AppAsLib_test.cpp +++ b/libs/androidfw/tests/AppAsLib_test.cpp @@ -14,55 +14,69 @@ * limitations under the License. */ -#include <androidfw/ResourceTypes.h> +#include "androidfw/ResourceTypes.h" +#include "TestHelpers.h" #include "data/appaslib/R.h" -#include <gtest/gtest.h> +namespace app = com::android::appaslib::app; +namespace lib = com::android::appaslib::lib; -using namespace android; - -namespace { - -#include "data/appaslib/appaslib_arsc.h" -#include "data/appaslib/appaslib_lib_arsc.h" +namespace android { // This tests the app resources loaded as app. -TEST(AppAsLibTest, loadedAsApp) { +TEST(AppAsLibTest, LoadedAsApp) { + std::string contents; + ASSERT_TRUE( + ReadFileFromZipToString(GetTestDataPath() + "/appaslib/appaslib.apk", + "resources.arsc", &contents)); + ResTable table; - ASSERT_EQ(NO_ERROR, table.add(appaslib_arsc, appaslib_arsc_len)); + ASSERT_EQ(NO_ERROR, table.add(contents.data(), contents.size())); Res_value val; - ssize_t block = table.getResource(appaslib::R::app::integer::number1, &val); + ssize_t block = table.getResource(app::R::integer::number1, &val); ASSERT_GE(block, 0); ASSERT_EQ(Res_value::TYPE_REFERENCE, val.dataType); - ASSERT_EQ(appaslib::R::app::array::integerArray1, val.data); + ASSERT_EQ(app::R::array::integerArray1, val.data); } // This tests the app resources loaded as shared-lib. -TEST(AppAsLibTest, loadedAsSharedLib) { +TEST(AppAsLibTest, LoadedAsSharedLib) { + std::string contents; + ASSERT_TRUE( + ReadFileFromZipToString(GetTestDataPath() + "/appaslib/appaslib.apk", + "resources.arsc", &contents)); + ResTable table; // Load as shared library. - ASSERT_EQ(NO_ERROR, table.add(appaslib_arsc, appaslib_arsc_len, NULL, 0, -1, false, true)); + ASSERT_EQ(NO_ERROR, table.add(contents.data(), contents.size(), NULL, 0, -1, + false, true)); Res_value val; - ssize_t block = table.getResource(appaslib::R::lib::integer::number1, &val); + ssize_t block = table.getResource(lib::R::integer::number1, &val); ASSERT_GE(block, 0); ASSERT_EQ(Res_value::TYPE_REFERENCE, val.dataType); - ASSERT_EQ(appaslib::R::lib::array::integerArray1, val.data); + ASSERT_EQ(lib::R::array::integerArray1, val.data); } // This tests the shared-lib loaded with appAsLib as true. -TEST(AppAsLibTest, loadedSharedLib) { +TEST(AppAsLibTest, LoadedSharedLib) { + std::string contents; + ASSERT_TRUE( + ReadFileFromZipToString(GetTestDataPath() + "/appaslib/appaslib_lib.apk", + "resources.arsc", &contents)); + ResTable table; // Load shared library with appAsLib as true. - ASSERT_EQ(NO_ERROR, table.add(appaslib_lib_arsc, appaslib_lib_arsc_len, NULL, 0, -1, false, true)); + ASSERT_EQ(NO_ERROR, table.add(contents.data(), contents.size(), NULL, 0, -1, + false, true)); Res_value val; - ssize_t block = table.getResource(appaslib::R::lib::integer::number1, &val); + ssize_t block = table.getResource(lib::R::integer::number1, &val); ASSERT_GE(block, 0); ASSERT_EQ(Res_value::TYPE_REFERENCE, val.dataType); - ASSERT_EQ(appaslib::R::lib::array::integerArray1, val.data); + ASSERT_EQ(lib::R::array::integerArray1, val.data); } -} +} // namespace android diff --git a/libs/androidfw/tests/AssetManager2_bench.cpp b/libs/androidfw/tests/AssetManager2_bench.cpp new file mode 100644 index 000000000000..9ff947807a1e --- /dev/null +++ b/libs/androidfw/tests/AssetManager2_bench.cpp @@ -0,0 +1,228 @@ +/* + * 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 "benchmark/benchmark.h" + +#include "androidfw/ApkAssets.h" +#include "androidfw/AssetManager.h" +#include "androidfw/AssetManager2.h" +#include "androidfw/ResourceTypes.h" + +#include "TestHelpers.h" +#include "data/basic/R.h" +#include "data/styles/R.h" + +namespace basic = com::android::basic; +namespace app = com::android::app; + +namespace android { + +constexpr const static char* kFrameworkPath = "/system/framework/framework-res.apk"; + +static void BM_AssetManagerLoadAssets(benchmark::State& state) { + std::string path = GetTestDataPath() + "/basic/basic.apk"; + while (state.KeepRunning()) { + std::unique_ptr<ApkAssets> apk = ApkAssets::Load(path); + AssetManager2 assets; + assets.SetApkAssets({apk.get()}); + } +} +BENCHMARK(BM_AssetManagerLoadAssets); + +static void BM_AssetManagerLoadAssetsOld(benchmark::State& state) { + String8 path((GetTestDataPath() + "/basic/basic.apk").data()); + while (state.KeepRunning()) { + AssetManager assets; + assets.addAssetPath(path, nullptr /* cookie */, false /* appAsLib */, + false /* isSystemAsset */); + + // Force creation. + assets.getResources(true); + } +} +BENCHMARK(BM_AssetManagerLoadAssetsOld); + +static void BM_AssetManagerLoadFrameworkAssets(benchmark::State& state) { + std::string path = kFrameworkPath; + while (state.KeepRunning()) { + std::unique_ptr<ApkAssets> apk = ApkAssets::Load(path); + AssetManager2 assets; + assets.SetApkAssets({apk.get()}); + } +} +BENCHMARK(BM_AssetManagerLoadFrameworkAssets); + +static void BM_AssetManagerLoadFrameworkAssetsOld(benchmark::State& state) { + String8 path(kFrameworkPath); + while (state.KeepRunning()) { + AssetManager assets; + assets.addAssetPath(path, nullptr /* cookie */, false /* appAsLib */, + false /* isSystemAsset */); + + // Force creation. + assets.getResources(true); + } +} +BENCHMARK(BM_AssetManagerLoadFrameworkAssetsOld); + +static void BM_AssetManagerGetResource(benchmark::State& state) { + std::unique_ptr<ApkAssets> apk = ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk"); + if (apk == nullptr) { + state.SkipWithError("Failed to load assets"); + return; + } + + AssetManager2 assets; + assets.SetApkAssets({apk.get()}); + + Res_value value; + ResTable_config selected_config; + uint32_t flags; + + while (state.KeepRunning()) { + assets.GetResource(basic::R::integer::number1, false /* may_be_bag */, + 0u /* density_override */, &value, &selected_config, &flags); + } +} +BENCHMARK(BM_AssetManagerGetResource); + +static void BM_AssetManagerGetResourceOld(benchmark::State& state) { + AssetManager assets; + if (!assets.addAssetPath(String8((GetTestDataPath() + "/basic/basic.apk").data()), + nullptr /* cookie */, false /* appAsLib */, + false /* isSystemAssets */)) { + state.SkipWithError("Failed to load assets"); + return; + } + + const ResTable& table = assets.getResources(true); + + Res_value value; + ResTable_config selected_config; + uint32_t flags; + + while (state.KeepRunning()) { + table.getResource(basic::R::integer::number1, &value, false /* may_be_bag */, + 0u /* density_override */, &flags, &selected_config); + } +} +BENCHMARK(BM_AssetManagerGetResourceOld); + +constexpr static const uint32_t kStringOkId = 0x0104000au; + +static void BM_AssetManagerGetResourceFrameworkLocale(benchmark::State& state) { + std::unique_ptr<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)); + memcpy(config.language, "fr", 2); + assets.SetConfiguration(config); + + Res_value value; + ResTable_config selected_config; + uint32_t flags; + + while (state.KeepRunning()) { + assets.GetResource(kStringOkId, false /* may_be_bag */, 0u /* density_override */, &value, + &selected_config, &flags); + } +} +BENCHMARK(BM_AssetManagerGetResourceFrameworkLocale); + +static void BM_AssetManagerGetResourceFrameworkLocaleOld(benchmark::State& state) { + AssetManager assets; + if (!assets.addAssetPath(String8((GetTestDataPath() + "/basic/basic.apk").data()), + nullptr /* cookie */, false /* appAsLib */, + false /* isSystemAssets */)) { + state.SkipWithError("Failed to load assets"); + return; + } + + ResTable_config config; + memset(&config, 0, sizeof(config)); + memcpy(config.language, "fr", 2); + assets.setConfiguration(config, nullptr); + + const ResTable& table = assets.getResources(true); + + Res_value value; + ResTable_config selected_config; + uint32_t flags; + + while (state.KeepRunning()) { + table.getResource(kStringOkId, &value, false /* may_be_bag */, 0u /* density_override */, + &flags, &selected_config); + } +} +BENCHMARK(BM_AssetManagerGetResourceFrameworkLocaleOld); + +static void BM_AssetManagerGetBag(benchmark::State& state) { + std::unique_ptr<ApkAssets> apk = ApkAssets::Load(GetTestDataPath() + "/styles/styles.apk"); + if (apk == nullptr) { + state.SkipWithError("Failed to load assets"); + return; + } + + AssetManager2 assets; + assets.SetApkAssets({apk.get()}); + + while (state.KeepRunning()) { + const ResolvedBag* bag = assets.GetBag(app::R::style::StyleTwo); + const auto bag_end = end(bag); + for (auto iter = begin(bag); iter != bag_end; ++iter) { + uint32_t key = iter->key; + Res_value value = iter->value; + benchmark::DoNotOptimize(key); + benchmark::DoNotOptimize(value); + } + } +} +BENCHMARK(BM_AssetManagerGetBag); + +static void BM_AssetManagerGetBagOld(benchmark::State& state) { + AssetManager assets; + if (!assets.addAssetPath(String8((GetTestDataPath() + "/styles/styles.apk").data()), + nullptr /* cookie */, false /* appAsLib */, + false /* isSystemAssets */)) { + state.SkipWithError("Failed to load assets"); + return; + } + + const ResTable& table = assets.getResources(true); + + while (state.KeepRunning()) { + const ResTable::bag_entry* bag_begin; + const ssize_t N = table.lockBag(app::R::style::StyleTwo, &bag_begin); + const ResTable::bag_entry* const bag_end = bag_begin + N; + for (auto iter = bag_begin; iter != bag_end; ++iter) { + uint32_t key = iter->map.name.ident; + Res_value value = iter->map.value; + benchmark::DoNotOptimize(key); + benchmark::DoNotOptimize(value); + } + table.unlockBag(bag_begin); + } +} +BENCHMARK(BM_AssetManagerGetBagOld); + +} // namespace android diff --git a/libs/androidfw/tests/AssetManager2_test.cpp b/libs/androidfw/tests/AssetManager2_test.cpp new file mode 100644 index 000000000000..39c5381feb04 --- /dev/null +++ b/libs/androidfw/tests/AssetManager2_test.cpp @@ -0,0 +1,190 @@ +/* + * 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 "androidfw/AssetManager2.h" +#include "androidfw/AssetManager.h" + +#include "android-base/logging.h" + +#include "TestHelpers.h" +#include "data/basic/R.h" +#include "data/styles/R.h" + +namespace basic = com::android::basic; +namespace app = com::android::app; + +namespace android { + +class AssetManager2Test : public ::testing::Test { + public: + void SetUp() override { + basic_assets_ = ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk"); + ASSERT_NE(nullptr, basic_assets_); + + basic_de_fr_assets_ = ApkAssets::Load(GetTestDataPath() + "/basic/basic_de_fr.apk"); + ASSERT_NE(nullptr, basic_de_fr_assets_); + + style_assets_ = ApkAssets::Load(GetTestDataPath() + "/styles/styles.apk"); + ASSERT_NE(nullptr, style_assets_); + } + + protected: + std::unique_ptr<ApkAssets> basic_assets_; + std::unique_ptr<ApkAssets> basic_de_fr_assets_; + std::unique_ptr<ApkAssets> style_assets_; +}; + +TEST_F(AssetManager2Test, FindsResourcesFromSingleApkAssets) { + ResTable_config desired_config; + memset(&desired_config, 0, sizeof(desired_config)); + desired_config.language[0] = 'd'; + desired_config.language[1] = 'e'; + + AssetManager2 assetmanager; + assetmanager.SetConfiguration(desired_config); + assetmanager.SetApkAssets({basic_assets_.get()}); + + Res_value value; + ResTable_config selected_config; + uint32_t flags; + + ApkAssetsCookie cookie = + assetmanager.GetResource(basic::R::string::test1, false /*may_be_bag*/, + 0 /*density_override*/, &value, &selected_config, &flags); + ASSERT_NE(kInvalidCookie, cookie); + + // Came from our ApkAssets. + EXPECT_EQ(0, cookie); + + // It is the default config. + EXPECT_EQ(0, selected_config.language[0]); + EXPECT_EQ(0, selected_config.language[1]); + + // It is a string. + EXPECT_EQ(Res_value::TYPE_STRING, value.dataType); +} + +TEST_F(AssetManager2Test, FindsResourcesFromMultipleApkAssets) { + ResTable_config desired_config; + memset(&desired_config, 0, sizeof(desired_config)); + desired_config.language[0] = 'd'; + desired_config.language[1] = 'e'; + + AssetManager2 assetmanager; + assetmanager.SetConfiguration(desired_config); + assetmanager.SetApkAssets({basic_assets_.get(), basic_de_fr_assets_.get()}); + + Res_value value; + ResTable_config selected_config; + uint32_t flags; + + ApkAssetsCookie cookie = + assetmanager.GetResource(basic::R::string::test1, false /*may_be_bag*/, + 0 /*density_override*/, &value, &selected_config, &flags); + ASSERT_NE(kInvalidCookie, cookie); + + // Came from our de_fr ApkAssets. + EXPECT_EQ(1, cookie); + + // The configuration is german. + EXPECT_EQ('d', selected_config.language[0]); + EXPECT_EQ('e', selected_config.language[1]); + + // It is a string. + EXPECT_EQ(Res_value::TYPE_STRING, value.dataType); +} + +TEST_F(AssetManager2Test, FindsBagResourcesFromSingleApkAssets) { + AssetManager2 assetmanager; + assetmanager.SetApkAssets({basic_assets_.get()}); + + const ResolvedBag* bag = assetmanager.GetBag(basic::R::array::integerArray1); + ASSERT_NE(nullptr, bag); + ASSERT_EQ(3u, bag->entry_count); + + EXPECT_EQ(static_cast<uint8_t>(Res_value::TYPE_INT_DEC), bag->entries[0].value.dataType); + EXPECT_EQ(1u, bag->entries[0].value.data); + EXPECT_EQ(0, bag->entries[0].cookie); + + EXPECT_EQ(static_cast<uint8_t>(Res_value::TYPE_INT_DEC), bag->entries[1].value.dataType); + EXPECT_EQ(2u, bag->entries[1].value.data); + EXPECT_EQ(0, bag->entries[1].cookie); + + EXPECT_EQ(static_cast<uint8_t>(Res_value::TYPE_INT_DEC), bag->entries[2].value.dataType); + EXPECT_EQ(3u, bag->entries[2].value.data); + EXPECT_EQ(0, bag->entries[2].cookie); +} + +TEST_F(AssetManager2Test, MergesStylesWithParentFromSingleApkAssets) { + AssetManager2 assetmanager; + assetmanager.SetApkAssets({style_assets_.get()}); + + const ResolvedBag* bag_one = assetmanager.GetBag(app::R::style::StyleOne); + ASSERT_NE(nullptr, bag_one); + ASSERT_EQ(2u, bag_one->entry_count); + + EXPECT_EQ(app::R::attr::attr_one, bag_one->entries[0].key); + EXPECT_EQ(Res_value::TYPE_INT_DEC, bag_one->entries[0].value.dataType); + EXPECT_EQ(1u, bag_one->entries[0].value.data); + EXPECT_EQ(0, bag_one->entries[0].cookie); + + EXPECT_EQ(app::R::attr::attr_two, bag_one->entries[1].key); + EXPECT_EQ(Res_value::TYPE_INT_DEC, bag_one->entries[1].value.dataType); + EXPECT_EQ(2u, bag_one->entries[1].value.data); + EXPECT_EQ(0, bag_one->entries[1].cookie); + + const ResolvedBag* bag_two = assetmanager.GetBag(app::R::style::StyleTwo); + ASSERT_NE(nullptr, bag_two); + ASSERT_EQ(5u, bag_two->entry_count); + + // attr_one is inherited from StyleOne. + EXPECT_EQ(app::R::attr::attr_one, bag_two->entries[0].key); + EXPECT_EQ(Res_value::TYPE_INT_DEC, bag_two->entries[0].value.dataType); + EXPECT_EQ(1u, bag_two->entries[0].value.data); + EXPECT_EQ(0, bag_two->entries[0].cookie); + + // attr_two should be overridden from StyleOne by StyleTwo. + EXPECT_EQ(app::R::attr::attr_two, bag_two->entries[1].key); + EXPECT_EQ(Res_value::TYPE_STRING, bag_two->entries[1].value.dataType); + EXPECT_EQ(0, bag_two->entries[1].cookie); + EXPECT_EQ(std::string("string"), GetStringFromPool(assetmanager.GetStringPoolForCookie(0), + bag_two->entries[1].value.data)); + + // The rest are new attributes. + + EXPECT_EQ(app::R::attr::attr_three, bag_two->entries[2].key); + EXPECT_EQ(Res_value::TYPE_ATTRIBUTE, bag_two->entries[2].value.dataType); + EXPECT_EQ(app::R::attr::attr_indirect, bag_two->entries[2].value.data); + EXPECT_EQ(0, bag_two->entries[2].cookie); + + EXPECT_EQ(app::R::attr::attr_five, bag_two->entries[3].key); + EXPECT_EQ(Res_value::TYPE_REFERENCE, bag_two->entries[3].value.dataType); + EXPECT_EQ(app::R::string::string_one, bag_two->entries[3].value.data); + EXPECT_EQ(0, bag_two->entries[3].cookie); + + EXPECT_EQ(app::R::attr::attr_indirect, bag_two->entries[4].key); + EXPECT_EQ(Res_value::TYPE_INT_DEC, bag_two->entries[4].value.dataType); + EXPECT_EQ(3u, bag_two->entries[4].value.data); + EXPECT_EQ(0, bag_two->entries[4].cookie); +} + +TEST_F(AssetManager2Test, FindsBagResourcesFromMultipleApkAssets) {} + +TEST_F(AssetManager2Test, OpensFileFromSingleApkAssets) {} + +TEST_F(AssetManager2Test, OpensFileFromMultipleApkAssets) {} + +} // namespace android diff --git a/libs/androidfw/tests/Asset_test.cpp b/libs/androidfw/tests/Asset_test.cpp index 45c8cef92918..5018218066cb 100644 --- a/libs/androidfw/tests/Asset_test.cpp +++ b/libs/androidfw/tests/Asset_test.cpp @@ -14,24 +14,26 @@ * limitations under the License. */ -#include <androidfw/Asset.h> +#include "androidfw/Asset.h" -#include <gtest/gtest.h> +#include "gtest/gtest.h" -using namespace android; +namespace android { TEST(AssetTest, FileAssetRegistersItself) { - const int32_t count = Asset::getGlobalCount(); - Asset* asset = new _FileAsset(); - EXPECT_EQ(count + 1, Asset::getGlobalCount()); - delete asset; - EXPECT_EQ(count, Asset::getGlobalCount()); + const int32_t count = Asset::getGlobalCount(); + Asset* asset = new _FileAsset(); + EXPECT_EQ(count + 1, Asset::getGlobalCount()); + delete asset; + EXPECT_EQ(count, Asset::getGlobalCount()); } TEST(AssetTest, CompressedAssetRegistersItself) { - const int32_t count = Asset::getGlobalCount(); - Asset* asset = new _CompressedAsset(); - EXPECT_EQ(count + 1, Asset::getGlobalCount()); - delete asset; - EXPECT_EQ(count, Asset::getGlobalCount()); + const int32_t count = Asset::getGlobalCount(); + Asset* asset = new _CompressedAsset(); + EXPECT_EQ(count + 1, Asset::getGlobalCount()); + delete asset; + EXPECT_EQ(count, Asset::getGlobalCount()); } + +} // nameapce android diff --git a/libs/androidfw/tests/AttributeFinder_test.cpp b/libs/androidfw/tests/AttributeFinder_test.cpp index 5054624579ea..7264b812737c 100644 --- a/libs/androidfw/tests/AttributeFinder_test.cpp +++ b/libs/androidfw/tests/AttributeFinder_test.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014 The Android Open Source Project + * 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. @@ -14,115 +14,112 @@ * limitations under the License. */ -#include <androidfw/AttributeFinder.h> +#include "androidfw/AttributeFinder.h" -#include <gtest/gtest.h> +#include "android-base/macros.h" +#include "gtest/gtest.h" -using android::BackTrackingAttributeFinder; +namespace android { -class MockAttributeFinder : public BackTrackingAttributeFinder<MockAttributeFinder, int> { -public: - MockAttributeFinder(const uint32_t* attrs, int len) - : BackTrackingAttributeFinder(0, len) { - mAttrs = new uint32_t[len]; - memcpy(mAttrs, attrs, sizeof(*attrs) * len); - } +class MockAttributeFinder + : public BackTrackingAttributeFinder<MockAttributeFinder, int> { + public: + MockAttributeFinder(const uint32_t* attrs, int len) + : BackTrackingAttributeFinder(0, len) { + attrs_ = new uint32_t[len]; + memcpy(attrs_, attrs, sizeof(*attrs) * len); + } - ~MockAttributeFinder() { - delete mAttrs; - } + ~MockAttributeFinder() { delete attrs_; } - inline uint32_t getAttribute(const int index) const { - return mAttrs[index]; - } + inline uint32_t GetAttribute(const int index) const { return attrs_[index]; } -private: - uint32_t* mAttrs; + private: + uint32_t* attrs_; }; -static const uint32_t sortedAttributes[] = { - 0x01010000, 0x01010001, 0x01010002, 0x01010004, - 0x02010001, 0x02010010, 0x7f010001 -}; +static const uint32_t kSortedAttributes[] = {0x01010000, 0x01010001, 0x01010002, + 0x01010004, 0x02010001, 0x02010010, + 0x7f010001}; -static const uint32_t packageUnsortedAttributes[] = { - 0x02010001, 0x02010010, 0x01010000, 0x01010001, - 0x01010002, 0x01010004, 0x7f010001 -}; +static const uint32_t kPackageUnsortedAttributes[] = { + 0x02010001, 0x02010010, 0x01010000, 0x01010001, + 0x01010002, 0x01010004, 0x7f010001}; -static const uint32_t singlePackageAttributes[] = { - 0x7f010007, 0x7f01000a, 0x7f01000d, 0x00000000 -}; +static const uint32_t kSinglePackageAttributes[] = {0x7f010007, 0x7f01000a, + 0x7f01000d, 0x00000000}; TEST(AttributeFinderTest, IteratesSequentially) { - const int end = sizeof(sortedAttributes) / sizeof(*sortedAttributes); - MockAttributeFinder finder(sortedAttributes, end); - - EXPECT_EQ(0, finder.find(0x01010000)); - EXPECT_EQ(1, finder.find(0x01010001)); - EXPECT_EQ(2, finder.find(0x01010002)); - EXPECT_EQ(3, finder.find(0x01010004)); - EXPECT_EQ(4, finder.find(0x02010001)); - EXPECT_EQ(5, finder.find(0x02010010)); - EXPECT_EQ(6, finder.find(0x7f010001)); - EXPECT_EQ(end, finder.find(0x7f010002)); + const int end = arraysize(kSortedAttributes); + MockAttributeFinder finder(kSortedAttributes, end); + + EXPECT_EQ(0, finder.Find(0x01010000)); + EXPECT_EQ(1, finder.Find(0x01010001)); + EXPECT_EQ(2, finder.Find(0x01010002)); + EXPECT_EQ(3, finder.Find(0x01010004)); + EXPECT_EQ(4, finder.Find(0x02010001)); + EXPECT_EQ(5, finder.Find(0x02010010)); + EXPECT_EQ(6, finder.Find(0x7f010001)); + EXPECT_EQ(end, finder.Find(0x7f010002)); } TEST(AttributeFinderTest, PackagesAreOutOfOrder) { - const int end = sizeof(sortedAttributes) / sizeof(*sortedAttributes); - MockAttributeFinder finder(sortedAttributes, end); - - EXPECT_EQ(6, finder.find(0x7f010001)); - EXPECT_EQ(end, finder.find(0x7f010002)); - EXPECT_EQ(4, finder.find(0x02010001)); - EXPECT_EQ(5, finder.find(0x02010010)); - EXPECT_EQ(0, finder.find(0x01010000)); - EXPECT_EQ(1, finder.find(0x01010001)); - EXPECT_EQ(2, finder.find(0x01010002)); - EXPECT_EQ(3, finder.find(0x01010004)); + const int end = arraysize(kSortedAttributes); + MockAttributeFinder finder(kSortedAttributes, end); + + EXPECT_EQ(6, finder.Find(0x7f010001)); + EXPECT_EQ(end, finder.Find(0x7f010002)); + EXPECT_EQ(4, finder.Find(0x02010001)); + EXPECT_EQ(5, finder.Find(0x02010010)); + EXPECT_EQ(0, finder.Find(0x01010000)); + EXPECT_EQ(1, finder.Find(0x01010001)); + EXPECT_EQ(2, finder.Find(0x01010002)); + EXPECT_EQ(3, finder.Find(0x01010004)); } TEST(AttributeFinderTest, SomeAttributesAreNotFound) { - const int end = sizeof(sortedAttributes) / sizeof(*sortedAttributes); - MockAttributeFinder finder(sortedAttributes, end); - - EXPECT_EQ(0, finder.find(0x01010000)); - EXPECT_EQ(1, finder.find(0x01010001)); - EXPECT_EQ(2, finder.find(0x01010002)); - EXPECT_EQ(end, finder.find(0x01010003)); - EXPECT_EQ(3, finder.find(0x01010004)); - EXPECT_EQ(end, finder.find(0x01010005)); - EXPECT_EQ(end, finder.find(0x01010006)); - EXPECT_EQ(4, finder.find(0x02010001)); - EXPECT_EQ(end, finder.find(0x02010002)); + const int end = arraysize(kSortedAttributes); + MockAttributeFinder finder(kSortedAttributes, end); + + EXPECT_EQ(0, finder.Find(0x01010000)); + EXPECT_EQ(1, finder.Find(0x01010001)); + EXPECT_EQ(2, finder.Find(0x01010002)); + EXPECT_EQ(end, finder.Find(0x01010003)); + EXPECT_EQ(3, finder.Find(0x01010004)); + EXPECT_EQ(end, finder.Find(0x01010005)); + EXPECT_EQ(end, finder.Find(0x01010006)); + EXPECT_EQ(4, finder.Find(0x02010001)); + EXPECT_EQ(end, finder.Find(0x02010002)); } TEST(AttributeFinderTest, FindAttributesInPackageUnsortedAttributeList) { - const int end = sizeof(packageUnsortedAttributes) / sizeof(*packageUnsortedAttributes); - MockAttributeFinder finder(packageUnsortedAttributes, end); - - EXPECT_EQ(2, finder.find(0x01010000)); - EXPECT_EQ(3, finder.find(0x01010001)); - EXPECT_EQ(4, finder.find(0x01010002)); - EXPECT_EQ(end, finder.find(0x01010003)); - EXPECT_EQ(5, finder.find(0x01010004)); - EXPECT_EQ(end, finder.find(0x01010005)); - EXPECT_EQ(end, finder.find(0x01010006)); - EXPECT_EQ(0, finder.find(0x02010001)); - EXPECT_EQ(end, finder.find(0x02010002)); - EXPECT_EQ(1, finder.find(0x02010010)); - EXPECT_EQ(6, finder.find(0x7f010001)); + const int end = arraysize(kPackageUnsortedAttributes); + MockAttributeFinder finder(kPackageUnsortedAttributes, end); + + EXPECT_EQ(2, finder.Find(0x01010000)); + EXPECT_EQ(3, finder.Find(0x01010001)); + EXPECT_EQ(4, finder.Find(0x01010002)); + EXPECT_EQ(end, finder.Find(0x01010003)); + EXPECT_EQ(5, finder.Find(0x01010004)); + EXPECT_EQ(end, finder.Find(0x01010005)); + EXPECT_EQ(end, finder.Find(0x01010006)); + EXPECT_EQ(0, finder.Find(0x02010001)); + EXPECT_EQ(end, finder.Find(0x02010002)); + EXPECT_EQ(1, finder.Find(0x02010010)); + EXPECT_EQ(6, finder.Find(0x7f010001)); } TEST(AttributeFinderTest, FindAttributesInSinglePackageAttributeList) { - const int end = sizeof(singlePackageAttributes) / sizeof(*singlePackageAttributes); - MockAttributeFinder finder(singlePackageAttributes, end); - - EXPECT_EQ(end, finder.find(0x010100f4)); - EXPECT_EQ(end, finder.find(0x010100f5)); - EXPECT_EQ(end, finder.find(0x010100f6)); - EXPECT_EQ(end, finder.find(0x010100f7)); - EXPECT_EQ(end, finder.find(0x010100f8)); - EXPECT_EQ(end, finder.find(0x010100fa)); - EXPECT_EQ(0, finder.find(0x7f010007)); + const int end = arraysize(kSinglePackageAttributes); + MockAttributeFinder finder(kSinglePackageAttributes, end); + + EXPECT_EQ(end, finder.Find(0x010100f4)); + EXPECT_EQ(end, finder.Find(0x010100f5)); + EXPECT_EQ(end, finder.Find(0x010100f6)); + EXPECT_EQ(end, finder.Find(0x010100f7)); + EXPECT_EQ(end, finder.Find(0x010100f8)); + EXPECT_EQ(end, finder.Find(0x010100fa)); + EXPECT_EQ(0, finder.Find(0x7f010007)); } + +} // namespace android diff --git a/libs/androidfw/tests/AttributeResolution_test.cpp b/libs/androidfw/tests/AttributeResolution_test.cpp new file mode 100644 index 000000000000..1ff2ed40cfc4 --- /dev/null +++ b/libs/androidfw/tests/AttributeResolution_test.cpp @@ -0,0 +1,209 @@ +/* + * 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 "androidfw/AttributeResolution.h" + +#include <array> + +#include "android-base/file.h" +#include "android-base/logging.h" +#include "android-base/macros.h" + +#include "TestHelpers.h" +#include "data/styles/R.h" + +using com::android::app::R; + +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*/)); + } + + protected: + ResTable table_; +}; + +class AttributeResolutionXmlTest : public AttributeResolutionTest { + public: + virtual void SetUp() override { + AttributeResolutionTest::SetUp(); + + std::string contents; + ASSERT_TRUE( + ReadFileFromZipToString(GetTestDataPath() + "/styles/styles.apk", + "res/layout/layout.xml", &contents)); + + ASSERT_EQ(NO_ERROR, xml_parser_.setTo(contents.data(), contents.size(), + true /*copyData*/)); + + // Skip to the first tag. + while (xml_parser_.next() != ResXMLParser::START_TAG) { + } + } + + protected: + ResXMLTree xml_parser_; +}; + +TEST_F(AttributeResolutionTest, Theme) { + ResTable::Theme theme(table_); + ASSERT_EQ(NO_ERROR, theme.applyStyle(R::style::StyleTwo)); + + std::array<uint32_t, 4> attrs{ + {R::attr::attr_one, R::attr::attr_two, R::attr::attr_three, R::attr::attr_four}}; + std::array<uint32_t, attrs.size() * STYLE_NUM_ENTRIES> values; + + ASSERT_TRUE(ResolveAttrs(&theme, 0 /*def_style_attr*/, 0 /*def_style_res*/, + nullptr /*src_values*/, 0 /*src_values_length*/, attrs.data(), + attrs.size(), values.data(), nullptr /*out_indices*/)); + + const uint32_t public_flag = ResTable_typeSpec::SPEC_PUBLIC; + + const uint32_t* values_cursor = values.data(); + EXPECT_EQ(Res_value::TYPE_INT_DEC, values_cursor[STYLE_TYPE]); + EXPECT_EQ(1u, values_cursor[STYLE_DATA]); + EXPECT_EQ(0u, values_cursor[STYLE_RESOURCE_ID]); + EXPECT_EQ(1u, values_cursor[STYLE_ASSET_COOKIE]); + EXPECT_EQ(0u, values_cursor[STYLE_DENSITY]); + EXPECT_EQ(public_flag, values_cursor[STYLE_CHANGING_CONFIGURATIONS]); + + values_cursor += STYLE_NUM_ENTRIES; + EXPECT_EQ(Res_value::TYPE_STRING, values_cursor[STYLE_TYPE]); + EXPECT_EQ(0u, values_cursor[STYLE_RESOURCE_ID]); + EXPECT_EQ(1u, values_cursor[STYLE_ASSET_COOKIE]); + EXPECT_EQ(0u, values_cursor[STYLE_DENSITY]); + EXPECT_EQ(public_flag, values_cursor[STYLE_CHANGING_CONFIGURATIONS]); + + values_cursor += STYLE_NUM_ENTRIES; + EXPECT_EQ(Res_value::TYPE_INT_DEC, values_cursor[STYLE_TYPE]); + EXPECT_EQ(3u, values_cursor[STYLE_DATA]); + EXPECT_EQ(0u, values_cursor[STYLE_RESOURCE_ID]); + EXPECT_EQ(1u, values_cursor[STYLE_ASSET_COOKIE]); + EXPECT_EQ(0u, values_cursor[STYLE_DENSITY]); + EXPECT_EQ(public_flag, values_cursor[STYLE_CHANGING_CONFIGURATIONS]); + + values_cursor += STYLE_NUM_ENTRIES; + EXPECT_EQ(Res_value::TYPE_NULL, values_cursor[STYLE_TYPE]); + EXPECT_EQ(Res_value::DATA_NULL_UNDEFINED, values_cursor[STYLE_DATA]); + EXPECT_EQ(0u, values_cursor[STYLE_RESOURCE_ID]); + EXPECT_EQ(uint32_t(-1), values_cursor[STYLE_ASSET_COOKIE]); + EXPECT_EQ(0u, values_cursor[STYLE_DENSITY]); + EXPECT_EQ(0u, values_cursor[STYLE_CHANGING_CONFIGURATIONS]); +} + +TEST_F(AttributeResolutionXmlTest, XmlParser) { + std::array<uint32_t, 4> attrs{ + {R::attr::attr_one, R::attr::attr_two, R::attr::attr_three, R::attr::attr_four}}; + 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*/)); + + uint32_t* values_cursor = values.data(); + EXPECT_EQ(Res_value::TYPE_NULL, values_cursor[STYLE_TYPE]); + EXPECT_EQ(0u, values_cursor[STYLE_DATA]); + EXPECT_EQ(0u, values_cursor[STYLE_RESOURCE_ID]); + EXPECT_EQ(uint32_t(-1), values_cursor[STYLE_ASSET_COOKIE]); + EXPECT_EQ(0u, values_cursor[STYLE_DENSITY]); + EXPECT_EQ(0u, values_cursor[STYLE_CHANGING_CONFIGURATIONS]); + + values_cursor += STYLE_NUM_ENTRIES; + EXPECT_EQ(Res_value::TYPE_NULL, values_cursor[STYLE_TYPE]); + EXPECT_EQ(0u, values_cursor[STYLE_DATA]); + EXPECT_EQ(0u, values_cursor[STYLE_RESOURCE_ID]); + EXPECT_EQ(uint32_t(-1), values_cursor[STYLE_ASSET_COOKIE]); + EXPECT_EQ(0u, values_cursor[STYLE_DENSITY]); + EXPECT_EQ(0u, values_cursor[STYLE_CHANGING_CONFIGURATIONS]); + + values_cursor += STYLE_NUM_ENTRIES; + EXPECT_EQ(Res_value::TYPE_INT_DEC, values_cursor[STYLE_TYPE]); + EXPECT_EQ(10u, values_cursor[STYLE_DATA]); + EXPECT_EQ(0u, values_cursor[STYLE_RESOURCE_ID]); + EXPECT_EQ(uint32_t(-1), values_cursor[STYLE_ASSET_COOKIE]); + EXPECT_EQ(0u, values_cursor[STYLE_DENSITY]); + EXPECT_EQ(0u, values_cursor[STYLE_CHANGING_CONFIGURATIONS]); + + values_cursor += STYLE_NUM_ENTRIES; + EXPECT_EQ(Res_value::TYPE_ATTRIBUTE, values_cursor[STYLE_TYPE]); + EXPECT_EQ(R::attr::attr_indirect, values_cursor[STYLE_DATA]); + EXPECT_EQ(0u, values_cursor[STYLE_RESOURCE_ID]); + EXPECT_EQ(uint32_t(-1), values_cursor[STYLE_ASSET_COOKIE]); + EXPECT_EQ(0u, values_cursor[STYLE_DENSITY]); + EXPECT_EQ(0u, values_cursor[STYLE_CHANGING_CONFIGURATIONS]); +} + +TEST_F(AttributeResolutionXmlTest, ThemeAndXmlParser) { + ResTable::Theme theme(table_); + ASSERT_EQ(NO_ERROR, 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_five}}; + std::array<uint32_t, attrs.size() * STYLE_NUM_ENTRIES> values; + std::array<uint32_t, attrs.size()> indices; + + ApplyStyle(&theme, &xml_parser_, 0 /*def_style_attr*/, 0 /*def_style_res*/, attrs.data(), + attrs.size(), values.data(), indices.data()); + + const uint32_t public_flag = ResTable_typeSpec::SPEC_PUBLIC; + + uint32_t* values_cursor = values.data(); + EXPECT_EQ(Res_value::TYPE_INT_DEC, values_cursor[STYLE_TYPE]); + EXPECT_EQ(1u, values_cursor[STYLE_DATA]); + EXPECT_EQ(0u, values_cursor[STYLE_RESOURCE_ID]); + EXPECT_EQ(1u, values_cursor[STYLE_ASSET_COOKIE]); + EXPECT_EQ(0u, values_cursor[STYLE_DENSITY]); + EXPECT_EQ(public_flag, values_cursor[STYLE_CHANGING_CONFIGURATIONS]); + + values_cursor += STYLE_NUM_ENTRIES; + EXPECT_EQ(Res_value::TYPE_STRING, values_cursor[STYLE_TYPE]); + EXPECT_EQ(0u, values_cursor[STYLE_RESOURCE_ID]); + EXPECT_EQ(1u, values_cursor[STYLE_ASSET_COOKIE]); + EXPECT_EQ(0u, values_cursor[STYLE_DENSITY]); + EXPECT_EQ(public_flag, values_cursor[STYLE_CHANGING_CONFIGURATIONS]); + + values_cursor += STYLE_NUM_ENTRIES; + EXPECT_EQ(Res_value::TYPE_INT_DEC, values_cursor[STYLE_TYPE]); + EXPECT_EQ(10u, values_cursor[STYLE_DATA]); + EXPECT_EQ(0u, values_cursor[STYLE_RESOURCE_ID]); + EXPECT_EQ(uint32_t(-1), values_cursor[STYLE_ASSET_COOKIE]); + EXPECT_EQ(0u, values_cursor[STYLE_DENSITY]); + EXPECT_EQ(0u, values_cursor[STYLE_CHANGING_CONFIGURATIONS]); + + values_cursor += STYLE_NUM_ENTRIES; + EXPECT_EQ(Res_value::TYPE_INT_DEC, values_cursor[STYLE_TYPE]); + EXPECT_EQ(3u, values_cursor[STYLE_DATA]); + EXPECT_EQ(0u, values_cursor[STYLE_RESOURCE_ID]); + EXPECT_EQ(1u, values_cursor[STYLE_ASSET_COOKIE]); + EXPECT_EQ(0u, values_cursor[STYLE_DENSITY]); + EXPECT_EQ(public_flag, values_cursor[STYLE_CHANGING_CONFIGURATIONS]); + + values_cursor += STYLE_NUM_ENTRIES; + EXPECT_EQ(Res_value::TYPE_STRING, values_cursor[STYLE_TYPE]); + EXPECT_EQ(R::string::string_one, values_cursor[STYLE_RESOURCE_ID]); + EXPECT_EQ(1u, values_cursor[STYLE_ASSET_COOKIE]); + EXPECT_EQ(0u, values_cursor[STYLE_DENSITY]); + EXPECT_EQ(public_flag, values_cursor[STYLE_CHANGING_CONFIGURATIONS]); +} + +} // namespace android + diff --git a/libs/androidfw/tests/BenchMain.cpp b/libs/androidfw/tests/BenchMain.cpp new file mode 100644 index 000000000000..105c5f9551b7 --- /dev/null +++ b/libs/androidfw/tests/BenchMain.cpp @@ -0,0 +1,31 @@ +/* + * 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 <iostream> + +#include "benchmark/benchmark.h" + +#include "TestHelpers.h" + +int main(int argc, char** argv) { + ::benchmark::Initialize(&argc, argv); + ::android::InitializeTest(&argc, argv); + + std::cerr << "using --testdata=" << ::android::GetTestDataPath() << "\n"; + + size_t result = ::benchmark::RunSpecifiedBenchmarks(); + return result == 0 ? 1 : 0; +} diff --git a/libs/androidfw/tests/ByteBucketArray_test.cpp b/libs/androidfw/tests/ByteBucketArray_test.cpp index 376e79c6e7cb..5d464c7dc0f7 100644 --- a/libs/androidfw/tests/ByteBucketArray_test.cpp +++ b/libs/androidfw/tests/ByteBucketArray_test.cpp @@ -14,28 +14,42 @@ * limitations under the License. */ -#include <androidfw/ByteBucketArray.h> +#include "androidfw/ByteBucketArray.h" -#include <gtest/gtest.h> +#include "gtest/gtest.h" -using android::ByteBucketArray; +namespace android { TEST(ByteBucketArrayTest, TestSparseInsertion) { - ByteBucketArray<int> bba; - ASSERT_TRUE(bba.set(0, 1)); - ASSERT_TRUE(bba.set(10, 2)); - ASSERT_TRUE(bba.set(26, 3)); - ASSERT_TRUE(bba.set(129, 4)); - ASSERT_TRUE(bba.set(234, 5)); + ByteBucketArray<int> bba; + ASSERT_TRUE(bba.set(0, 1)); + ASSERT_TRUE(bba.set(10, 2)); + ASSERT_TRUE(bba.set(26, 3)); + ASSERT_TRUE(bba.set(129, 4)); + ASSERT_TRUE(bba.set(234, 5)); - for (size_t i = 0; i < bba.size(); i++) { - switch (i) { - case 0: EXPECT_EQ(1, bba[i]); break; - case 10: EXPECT_EQ(2, bba[i]); break; - case 26: EXPECT_EQ(3, bba[i]); break; - case 129: EXPECT_EQ(4, bba[i]); break; - case 234: EXPECT_EQ(5, bba[i]); break; - default: EXPECT_EQ(0, bba[i]); break; - } + for (size_t i = 0; i < bba.size(); i++) { + switch (i) { + case 0: + EXPECT_EQ(1, bba[i]); + break; + case 10: + EXPECT_EQ(2, bba[i]); + break; + case 26: + EXPECT_EQ(3, bba[i]); + break; + case 129: + EXPECT_EQ(4, bba[i]); + break; + case 234: + EXPECT_EQ(5, bba[i]); + break; + default: + EXPECT_EQ(0, bba[i]); + break; } + } } + +} // namespace android diff --git a/libs/androidfw/tests/ConfigLocale_test.cpp b/libs/androidfw/tests/ConfigLocale_test.cpp index 10f4d46058ec..86a627e1485d 100644 --- a/libs/androidfw/tests/ConfigLocale_test.cpp +++ b/libs/androidfw/tests/ConfigLocale_test.cpp @@ -279,6 +279,23 @@ TEST(ConfigLocaleTest, getBcp47Locale_script) { EXPECT_EQ(0, strcmp("en", out)); } +TEST(ConfigLocaleTest, getBcp47Locale_canonicalize) { + ResTable_config config; + char out[RESTABLE_MAX_LOCALE_LEN]; + + fillIn("tl", NULL, NULL, NULL, &config); + config.getBcp47Locale(out); + EXPECT_EQ(0, strcmp("tl", out)); + config.getBcp47Locale(out, true /* canonicalize */); + EXPECT_EQ(0, strcmp("fil", out)); + + fillIn("tl", "PH", NULL, NULL, &config); + config.getBcp47Locale(out); + EXPECT_EQ(0, strcmp("tl-PH", out)); + config.getBcp47Locale(out, true /* canonicalize */); + EXPECT_EQ(0, strcmp("fil-PH", out)); +} + TEST(ConfigLocaleTest, match) { ResTable_config supported, requested; @@ -292,6 +309,11 @@ TEST(ConfigLocaleTest, match) { // Different languages don't match. EXPECT_FALSE(supported.match(requested)); + fillIn("tl", "PH", NULL, NULL, &supported); + fillIn("fil", "PH", NULL, NULL, &requested); + // Equivalent languages match. + EXPECT_TRUE(supported.match(requested)); + fillIn("qaa", "FR", NULL, NULL, &supported); fillIn("qaa", "CA", NULL, NULL, &requested); // If we can't infer the scripts, different regions don't match. @@ -406,6 +428,12 @@ TEST(ConfigLocaleTest, isLocaleBetterThan_basics) { EXPECT_FALSE(config2.isLocaleBetterThan(config1, &request)); fillIn("de", "DE", NULL, NULL, &request); + fillIn("de", "DE", NULL, NULL, &config1); + fillIn("de", "DE", NULL, "1901", &config2); + EXPECT_TRUE(config1.isLocaleBetterThan(config2, &request)); + EXPECT_FALSE(config2.isLocaleBetterThan(config1, &request)); + + fillIn("de", "DE", NULL, NULL, &request); fillIn("de", "DE", NULL, "1901", &config1); fillIn("de", "DE", NULL, "1996", &config2); EXPECT_FALSE(config1.isLocaleBetterThan(config2, &request)); @@ -422,6 +450,24 @@ TEST(ConfigLocaleTest, isLocaleBetterThan_basics) { fillIn("de", "DE", NULL, NULL, &config2); EXPECT_FALSE(config1.isLocaleBetterThan(config2, &request)); EXPECT_FALSE(config2.isLocaleBetterThan(config1, &request)); + + fillIn("fil", "PH", NULL, NULL, &request); + fillIn("tl", "PH", NULL, NULL, &config1); + fillIn("fil", "US", NULL, NULL, &config2); + EXPECT_TRUE(config1.isLocaleBetterThan(config2, &request)); + EXPECT_FALSE(config2.isLocaleBetterThan(config1, &request)); + + fillIn("fil", "PH", NULL, "fonipa", &request); + fillIn("tl", "PH", NULL, "fonipa", &config1); + fillIn("fil", "PH", NULL, NULL, &config2); + EXPECT_TRUE(config1.isLocaleBetterThan(config2, &request)); + EXPECT_FALSE(config2.isLocaleBetterThan(config1, &request)); + + fillIn("fil", "PH", NULL, NULL, &request); + fillIn("fil", "PH", NULL, NULL, &config1); + fillIn("tl", "PH", NULL, NULL, &config2); + EXPECT_TRUE(config1.isLocaleBetterThan(config2, &request)); + EXPECT_FALSE(config2.isLocaleBetterThan(config1, &request)); } TEST(ConfigLocaleTest, isLocaleBetterThan_regionComparison) { diff --git a/libs/androidfw/tests/Config_test.cpp b/libs/androidfw/tests/Config_test.cpp index 738947a46200..778c7bfb2053 100644 --- a/libs/androidfw/tests/Config_test.cpp +++ b/libs/androidfw/tests/Config_test.cpp @@ -14,168 +14,172 @@ * limitations under the License. */ -#include <androidfw/ResourceTypes.h> -#include <utils/Log.h> -#include <utils/String8.h> -#include <utils/Vector.h> +#include "androidfw/ResourceTypes.h" + +#include "utils/Log.h" +#include "utils/String8.h" +#include "utils/Vector.h" #include "TestHelpers.h" -#include <gtest/gtest.h> +#include "gtest/gtest.h" namespace android { static ResTable_config selectBest(const ResTable_config& target, - const Vector<ResTable_config>& configs) { - ResTable_config bestConfig; - memset(&bestConfig, 0, sizeof(bestConfig)); - const size_t configCount = configs.size(); - for (size_t i = 0; i < configCount; i++) { - const ResTable_config& thisConfig = configs[i]; - if (!thisConfig.match(target)) { - continue; - } - - if (thisConfig.isBetterThan(bestConfig, &target)) { - bestConfig = thisConfig; - } + const Vector<ResTable_config>& configs) { + ResTable_config bestConfig; + memset(&bestConfig, 0, sizeof(bestConfig)); + const size_t configCount = configs.size(); + for (size_t i = 0; i < configCount; i++) { + const ResTable_config& thisConfig = configs[i]; + if (!thisConfig.match(target)) { + continue; + } + + if (thisConfig.isBetterThan(bestConfig, &target)) { + bestConfig = thisConfig; } - return bestConfig; + } + return bestConfig; } static ResTable_config buildDensityConfig(int density) { - ResTable_config config; - memset(&config, 0, sizeof(config)); - config.density = uint16_t(density); - config.sdkVersion = 4; - return config; + ResTable_config config; + memset(&config, 0, sizeof(config)); + config.density = uint16_t(density); + config.sdkVersion = 4; + return config; } TEST(ConfigTest, shouldSelectBestDensity) { - ResTable_config deviceConfig; - memset(&deviceConfig, 0, sizeof(deviceConfig)); - deviceConfig.density = ResTable_config::DENSITY_XHIGH; - deviceConfig.sdkVersion = 21; + ResTable_config deviceConfig; + memset(&deviceConfig, 0, sizeof(deviceConfig)); + deviceConfig.density = ResTable_config::DENSITY_XHIGH; + deviceConfig.sdkVersion = 21; - Vector<ResTable_config> configs; + Vector<ResTable_config> configs; - ResTable_config expectedBest = buildDensityConfig(ResTable_config::DENSITY_HIGH); - configs.add(expectedBest); - ASSERT_EQ(expectedBest, selectBest(deviceConfig, configs)); + ResTable_config expectedBest = + buildDensityConfig(ResTable_config::DENSITY_HIGH); + configs.add(expectedBest); + ASSERT_EQ(expectedBest, selectBest(deviceConfig, configs)); - expectedBest = buildDensityConfig(ResTable_config::DENSITY_XXHIGH); - configs.add(expectedBest); - ASSERT_EQ(expectedBest, selectBest(deviceConfig, configs)); + expectedBest = buildDensityConfig(ResTable_config::DENSITY_XXHIGH); + configs.add(expectedBest); + ASSERT_EQ(expectedBest, selectBest(deviceConfig, configs)); - expectedBest = buildDensityConfig(int(ResTable_config::DENSITY_XXHIGH) - 20); - configs.add(expectedBest); - ASSERT_EQ(expectedBest, selectBest(deviceConfig, configs)); + expectedBest = buildDensityConfig(int(ResTable_config::DENSITY_XXHIGH) - 20); + configs.add(expectedBest); + ASSERT_EQ(expectedBest, selectBest(deviceConfig, configs)); - configs.add(buildDensityConfig(int(ResTable_config::DENSITY_HIGH) + 20)); - ASSERT_EQ(expectedBest, selectBest(deviceConfig, configs)); + configs.add(buildDensityConfig(int(ResTable_config::DENSITY_HIGH) + 20)); + ASSERT_EQ(expectedBest, selectBest(deviceConfig, configs)); - expectedBest = buildDensityConfig(ResTable_config::DENSITY_XHIGH); - configs.add(expectedBest); - ASSERT_EQ(expectedBest, selectBest(deviceConfig, configs)); + expectedBest = buildDensityConfig(ResTable_config::DENSITY_XHIGH); + configs.add(expectedBest); + ASSERT_EQ(expectedBest, selectBest(deviceConfig, configs)); - expectedBest = buildDensityConfig(ResTable_config::DENSITY_ANY); - expectedBest.sdkVersion = 21; - configs.add(expectedBest); - ASSERT_EQ(expectedBest, selectBest(deviceConfig, configs)); + expectedBest = buildDensityConfig(ResTable_config::DENSITY_ANY); + expectedBest.sdkVersion = 21; + configs.add(expectedBest); + ASSERT_EQ(expectedBest, selectBest(deviceConfig, configs)); } TEST(ConfigTest, shouldSelectBestDensityWhenNoneSpecified) { - ResTable_config deviceConfig; - memset(&deviceConfig, 0, sizeof(deviceConfig)); - deviceConfig.sdkVersion = 21; + ResTable_config deviceConfig; + memset(&deviceConfig, 0, sizeof(deviceConfig)); + deviceConfig.sdkVersion = 21; - Vector<ResTable_config> configs; - configs.add(buildDensityConfig(ResTable_config::DENSITY_HIGH)); + Vector<ResTable_config> configs; + configs.add(buildDensityConfig(ResTable_config::DENSITY_HIGH)); - ResTable_config expectedBest = buildDensityConfig(ResTable_config::DENSITY_MEDIUM); - configs.add(expectedBest); - ASSERT_EQ(expectedBest, selectBest(deviceConfig, configs)); + ResTable_config expectedBest = + buildDensityConfig(ResTable_config::DENSITY_MEDIUM); + configs.add(expectedBest); + ASSERT_EQ(expectedBest, selectBest(deviceConfig, configs)); - expectedBest = buildDensityConfig(ResTable_config::DENSITY_ANY); - configs.add(expectedBest); - ASSERT_EQ(expectedBest, selectBest(deviceConfig, configs)); + expectedBest = buildDensityConfig(ResTable_config::DENSITY_ANY); + configs.add(expectedBest); + ASSERT_EQ(expectedBest, selectBest(deviceConfig, configs)); } TEST(ConfigTest, shouldMatchRoundQualifier) { - ResTable_config deviceConfig; - memset(&deviceConfig, 0, sizeof(deviceConfig)); + ResTable_config deviceConfig; + memset(&deviceConfig, 0, sizeof(deviceConfig)); - ResTable_config roundConfig; - memset(&roundConfig, 0, sizeof(roundConfig)); - roundConfig.screenLayout2 = ResTable_config::SCREENROUND_YES; + ResTable_config roundConfig; + memset(&roundConfig, 0, sizeof(roundConfig)); + roundConfig.screenLayout2 = ResTable_config::SCREENROUND_YES; - EXPECT_FALSE(roundConfig.match(deviceConfig)); + EXPECT_FALSE(roundConfig.match(deviceConfig)); - deviceConfig.screenLayout2 = ResTable_config::SCREENROUND_YES; + deviceConfig.screenLayout2 = ResTable_config::SCREENROUND_YES; - EXPECT_TRUE(roundConfig.match(deviceConfig)); + EXPECT_TRUE(roundConfig.match(deviceConfig)); - deviceConfig.screenLayout2 = ResTable_config::SCREENROUND_NO; + deviceConfig.screenLayout2 = ResTable_config::SCREENROUND_NO; - EXPECT_FALSE(roundConfig.match(deviceConfig)); + EXPECT_FALSE(roundConfig.match(deviceConfig)); - ResTable_config notRoundConfig; - memset(¬RoundConfig, 0, sizeof(notRoundConfig)); - notRoundConfig.screenLayout2 = ResTable_config::SCREENROUND_NO; + ResTable_config notRoundConfig; + memset(¬RoundConfig, 0, sizeof(notRoundConfig)); + notRoundConfig.screenLayout2 = ResTable_config::SCREENROUND_NO; - EXPECT_TRUE(notRoundConfig.match(deviceConfig)); + EXPECT_TRUE(notRoundConfig.match(deviceConfig)); } TEST(ConfigTest, RoundQualifierShouldHaveStableSortOrder) { - ResTable_config defaultConfig; - memset(&defaultConfig, 0, sizeof(defaultConfig)); + ResTable_config defaultConfig; + memset(&defaultConfig, 0, sizeof(defaultConfig)); - ResTable_config longConfig = defaultConfig; - longConfig.screenLayout = ResTable_config::SCREENLONG_YES; + ResTable_config longConfig = defaultConfig; + longConfig.screenLayout = ResTable_config::SCREENLONG_YES; - ResTable_config longRoundConfig = longConfig; - longRoundConfig.screenLayout2 = ResTable_config::SCREENROUND_YES; + ResTable_config longRoundConfig = longConfig; + longRoundConfig.screenLayout2 = ResTable_config::SCREENROUND_YES; - ResTable_config longRoundPortConfig = longConfig; - longRoundPortConfig.orientation = ResTable_config::ORIENTATION_PORT; + ResTable_config longRoundPortConfig = longConfig; + longRoundPortConfig.orientation = ResTable_config::ORIENTATION_PORT; - EXPECT_TRUE(longConfig.compare(longRoundConfig) < 0); - EXPECT_TRUE(longConfig.compareLogical(longRoundConfig) < 0); - EXPECT_TRUE(longRoundConfig.compare(longConfig) > 0); - EXPECT_TRUE(longRoundConfig.compareLogical(longConfig) > 0); + EXPECT_TRUE(longConfig.compare(longRoundConfig) < 0); + EXPECT_TRUE(longConfig.compareLogical(longRoundConfig) < 0); + EXPECT_TRUE(longRoundConfig.compare(longConfig) > 0); + EXPECT_TRUE(longRoundConfig.compareLogical(longConfig) > 0); - EXPECT_TRUE(longRoundConfig.compare(longRoundPortConfig) < 0); - EXPECT_TRUE(longRoundConfig.compareLogical(longRoundPortConfig) < 0); - EXPECT_TRUE(longRoundPortConfig.compare(longRoundConfig) > 0); - EXPECT_TRUE(longRoundPortConfig.compareLogical(longRoundConfig) > 0); + EXPECT_TRUE(longRoundConfig.compare(longRoundPortConfig) < 0); + EXPECT_TRUE(longRoundConfig.compareLogical(longRoundPortConfig) < 0); + EXPECT_TRUE(longRoundPortConfig.compare(longRoundConfig) > 0); + EXPECT_TRUE(longRoundPortConfig.compareLogical(longRoundConfig) > 0); } TEST(ConfigTest, ScreenShapeHasCorrectDiff) { - ResTable_config defaultConfig; - memset(&defaultConfig, 0, sizeof(defaultConfig)); + ResTable_config defaultConfig; + memset(&defaultConfig, 0, sizeof(defaultConfig)); - ResTable_config roundConfig = defaultConfig; - roundConfig.screenLayout2 = ResTable_config::SCREENROUND_YES; + ResTable_config roundConfig = defaultConfig; + roundConfig.screenLayout2 = ResTable_config::SCREENROUND_YES; - EXPECT_EQ(defaultConfig.diff(roundConfig), ResTable_config::CONFIG_SCREEN_ROUND); + EXPECT_EQ(defaultConfig.diff(roundConfig), + ResTable_config::CONFIG_SCREEN_ROUND); } TEST(ConfigTest, RoundIsMoreSpecific) { - ResTable_config deviceConfig; - memset(&deviceConfig, 0, sizeof(deviceConfig)); - deviceConfig.screenLayout2 = ResTable_config::SCREENROUND_YES; - deviceConfig.screenLayout = ResTable_config::SCREENLONG_YES; + ResTable_config deviceConfig; + memset(&deviceConfig, 0, sizeof(deviceConfig)); + deviceConfig.screenLayout2 = ResTable_config::SCREENROUND_YES; + deviceConfig.screenLayout = ResTable_config::SCREENLONG_YES; - ResTable_config targetConfigA; - memset(&targetConfigA, 0, sizeof(targetConfigA)); + ResTable_config targetConfigA; + memset(&targetConfigA, 0, sizeof(targetConfigA)); - ResTable_config targetConfigB = targetConfigA; - targetConfigB.screenLayout = ResTable_config::SCREENLONG_YES; + ResTable_config targetConfigB = targetConfigA; + targetConfigB.screenLayout = ResTable_config::SCREENLONG_YES; - ResTable_config targetConfigC = targetConfigB; - targetConfigC.screenLayout2 = ResTable_config::SCREENROUND_YES; + ResTable_config targetConfigC = targetConfigB; + targetConfigC.screenLayout2 = ResTable_config::SCREENROUND_YES; - EXPECT_TRUE(targetConfigB.isBetterThan(targetConfigA, &deviceConfig)); - EXPECT_TRUE(targetConfigC.isBetterThan(targetConfigB, &deviceConfig)); + EXPECT_TRUE(targetConfigB.isBetterThan(targetConfigA, &deviceConfig)); + EXPECT_TRUE(targetConfigC.isBetterThan(targetConfigB, &deviceConfig)); } } // namespace android. diff --git a/libs/androidfw/tests/Idmap_test.cpp b/libs/androidfw/tests/Idmap_test.cpp index f50c17890b1e..0928b1b976c3 100644 --- a/libs/androidfw/tests/Idmap_test.cpp +++ b/libs/androidfw/tests/Idmap_test.cpp @@ -14,102 +14,102 @@ * limitations under the License. */ -#include <androidfw/ResourceTypes.h> +#include "androidfw/ResourceTypes.h" + +#include "utils/String16.h" +#include "utils/String8.h" -#include <utils/String8.h> -#include <utils/String16.h> #include "TestHelpers.h" #include "data/basic/R.h" -#include <gtest/gtest.h> - -using namespace android; - -namespace { - -/** - * Include a binary resource table. - * - * Package: com.android.test.basic - */ -#include "data/basic/basic_arsc.h" - -/** - * Include a binary resource table. - * This table is an overlay. - * - * Package: com.android.test.basic - */ -#include "data/overlay/overlay_arsc.h" +using com::android::basic::R; -enum { MAY_NOT_BE_BAG = false }; +namespace android { class IdmapTest : public ::testing::Test { -protected: - virtual void SetUp() { - ASSERT_EQ(NO_ERROR, mTargetTable.add(basic_arsc, basic_arsc_len)); - ASSERT_EQ(NO_ERROR, mOverlayTable.add(overlay_arsc, overlay_arsc_len)); - char targetName[256] = "com.android.test.basic"; - ASSERT_EQ(NO_ERROR, mTargetTable.createIdmap(mOverlayTable, 0, 0, - targetName, targetName, &mData, &mDataSize)); - } - - virtual void TearDown() { - free(mData); - } - - ResTable mTargetTable; - ResTable mOverlayTable; - void* mData; - size_t mDataSize; + protected: + void SetUp() override { + std::string contents; + ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk", + "resources.arsc", &contents)); + ASSERT_EQ(NO_ERROR, + target_table_.add(contents.data(), contents.size(), 0, true)); + + ASSERT_TRUE( + ReadFileFromZipToString(GetTestDataPath() + "/overlay/overlay.apk", + "resources.arsc", &overlay_data_)); + ResTable overlay_table; + ASSERT_EQ(NO_ERROR, + overlay_table.add(overlay_data_.data(), overlay_data_.size())); + + char target_name[256] = "com.android.basic"; + ASSERT_EQ(NO_ERROR, + target_table_.createIdmap(overlay_table, 0, 0, target_name, + target_name, &data_, &data_size_)); + } + + void TearDown() override { ::free(data_); } + + ResTable target_table_; + std::string overlay_data_; + void* data_ = nullptr; + size_t data_size_ = 0; }; TEST_F(IdmapTest, canLoadIdmap) { - ASSERT_EQ(NO_ERROR, mTargetTable.add(overlay_arsc, overlay_arsc_len, mData, mDataSize)); + ASSERT_EQ(NO_ERROR, + target_table_.add(overlay_data_.data(), overlay_data_.size(), data_, + data_size_)); } TEST_F(IdmapTest, overlayOverridesResourceValue) { - Res_value val; - ssize_t block = mTargetTable.getResource(base::R::string::test2, &val, false); - ASSERT_GE(block, 0); - ASSERT_EQ(Res_value::TYPE_STRING, val.dataType); - const ResStringPool* pool = mTargetTable.getTableStringBlock(block); - ASSERT_TRUE(pool != NULL); - ASSERT_LT(val.data, pool->size()); - - size_t strLen; - const char16_t* targetStr16 = pool->stringAt(val.data, &strLen); - ASSERT_TRUE(targetStr16 != NULL); - ASSERT_EQ(String16("test2"), String16(targetStr16, strLen)); - - ASSERT_EQ(NO_ERROR, mTargetTable.add(overlay_arsc, overlay_arsc_len, mData, mDataSize)); - - ssize_t newBlock = mTargetTable.getResource(base::R::string::test2, &val, false); - ASSERT_GE(newBlock, 0); - ASSERT_NE(block, newBlock); - ASSERT_EQ(Res_value::TYPE_STRING, val.dataType); - pool = mTargetTable.getTableStringBlock(newBlock); - ASSERT_TRUE(pool != NULL); - ASSERT_LT(val.data, pool->size()); - - targetStr16 = pool->stringAt(val.data, &strLen); - ASSERT_TRUE(targetStr16 != NULL); - ASSERT_EQ(String16("test2-overlay"), String16(targetStr16, strLen)); + Res_value val; + ssize_t block = target_table_.getResource(R::string::test2, &val, false); + ASSERT_GE(block, 0); + ASSERT_EQ(Res_value::TYPE_STRING, val.dataType); + const ResStringPool* pool = target_table_.getTableStringBlock(block); + ASSERT_TRUE(pool != NULL); + ASSERT_LT(val.data, pool->size()); + + size_t strLen; + const char16_t* targetStr16 = pool->stringAt(val.data, &strLen); + ASSERT_TRUE(targetStr16 != NULL); + ASSERT_EQ(String16("test2"), String16(targetStr16, strLen)); + + ASSERT_EQ(NO_ERROR, + target_table_.add(overlay_data_.data(), overlay_data_.size(), data_, + data_size_)); + + ssize_t newBlock = target_table_.getResource(R::string::test2, &val, false); + ASSERT_GE(newBlock, 0); + ASSERT_NE(block, newBlock); + ASSERT_EQ(Res_value::TYPE_STRING, val.dataType); + pool = target_table_.getTableStringBlock(newBlock); + ASSERT_TRUE(pool != NULL); + ASSERT_LT(val.data, pool->size()); + + targetStr16 = pool->stringAt(val.data, &strLen); + ASSERT_TRUE(targetStr16 != NULL); + ASSERT_EQ(String16("test2-overlay"), String16(targetStr16, strLen)); } TEST_F(IdmapTest, overlaidResourceHasSameName) { - ASSERT_EQ(NO_ERROR, mTargetTable.add(overlay_arsc, overlay_arsc_len, mData, mDataSize)); - - ResTable::resource_name resName; - ASSERT_TRUE(mTargetTable.getResourceName(base::R::array::integerArray1, false, &resName)); - - ASSERT_TRUE(resName.package != NULL); - ASSERT_TRUE(resName.type != NULL); - ASSERT_TRUE(resName.name != NULL); - - EXPECT_EQ(String16("com.android.test.basic"), String16(resName.package, resName.packageLen)); - EXPECT_EQ(String16("array"), String16(resName.type, resName.typeLen)); - EXPECT_EQ(String16("integerArray1"), String16(resName.name, resName.nameLen)); + ASSERT_EQ(NO_ERROR, + target_table_.add(overlay_data_.data(), overlay_data_.size(), data_, + data_size_)); + + ResTable::resource_name resName; + ASSERT_TRUE( + target_table_.getResourceName(R::array::integerArray1, false, &resName)); + + ASSERT_TRUE(resName.package != NULL); + ASSERT_TRUE(resName.type != NULL); + ASSERT_TRUE(resName.name != NULL); + + EXPECT_EQ(String16("com.android.basic"), + String16(resName.package, resName.packageLen)); + EXPECT_EQ(String16("array"), String16(resName.type, resName.typeLen)); + EXPECT_EQ(String16("integerArray1"), String16(resName.name, resName.nameLen)); } -} // namespace +} // namespace diff --git a/libs/androidfw/tests/LoadedArsc_test.cpp b/libs/androidfw/tests/LoadedArsc_test.cpp new file mode 100644 index 000000000000..47b3894f0398 --- /dev/null +++ b/libs/androidfw/tests/LoadedArsc_test.cpp @@ -0,0 +1,82 @@ +/* + * 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 "androidfw/LoadedArsc.h" + +#include "android-base/file.h" +#include "android-base/logging.h" +#include "android-base/macros.h" + +#include "TestHelpers.h" +#include "data/basic/R.h" +#include "data/styles/R.h" + +namespace app = com::android::app; +namespace basic = com::android::basic; + +namespace android { + +TEST(LoadedArscTest, LoadSinglePackageArsc) { + base::ScopedLogSeverity _log(base::LogSeverity::DEBUG); + std::string contents; + ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/styles/styles.apk", "resources.arsc", + &contents)); + + std::unique_ptr<LoadedArsc> loaded_arsc = LoadedArsc::Load(contents.data(), contents.size()); + ASSERT_NE(nullptr, loaded_arsc); + + ResTable_config config; + memset(&config, 0, sizeof(config)); + config.sdkVersion = 24; + + LoadedArsc::Entry entry; + ResTable_config selected_config; + uint32_t flags; + + ASSERT_TRUE( + loaded_arsc->FindEntry(app::R::string::string_one, config, &entry, &selected_config, &flags)); + ASSERT_NE(nullptr, entry.entry); +} + +TEST(LoadedArscTest, FindDefaultEntry) { + base::ScopedLogSeverity _log(base::LogSeverity::DEBUG); + std::string contents; + ASSERT_TRUE( + ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk", "resources.arsc", &contents)); + + std::unique_ptr<LoadedArsc> loaded_arsc = LoadedArsc::Load(contents.data(), contents.size()); + ASSERT_NE(nullptr, loaded_arsc); + + ResTable_config desired_config; + memset(&desired_config, 0, sizeof(desired_config)); + desired_config.language[0] = 'd'; + desired_config.language[1] = 'e'; + + LoadedArsc::Entry entry; + ResTable_config selected_config; + uint32_t flags; + + ASSERT_TRUE(loaded_arsc->FindEntry(basic::R::string::test1, desired_config, &entry, + &selected_config, &flags)); + ASSERT_NE(nullptr, entry.entry); +} + +// structs with size fields (like Res_value, ResTable_entry) should be +// backwards and forwards compatible (aka checking the size field against +// sizeof(Res_value) might not be backwards compatible. +TEST(LoadedArscTest, LoadingShouldBeForwardsAndBackwardsCompatible) { ASSERT_TRUE(false); } + +} // namespace android diff --git a/libs/androidfw/tests/ResTable_test.cpp b/libs/androidfw/tests/ResTable_test.cpp index 9e53dd279195..b151f3f96496 100644 --- a/libs/androidfw/tests/ResTable_test.cpp +++ b/libs/androidfw/tests/ResTable_test.cpp @@ -14,352 +14,386 @@ * limitations under the License. */ -#include <androidfw/ResourceTypes.h> +#include "androidfw/ResourceTypes.h" #include <codecvt> #include <locale> #include <string> -#include <utils/String8.h> -#include <utils/String16.h> +#include "utils/String16.h" +#include "utils/String8.h" + #include "TestHelpers.h" #include "data/basic/R.h" #include "data/lib/R.h" -#include <gtest/gtest.h> +namespace basic = com::android::basic; +namespace lib = com::android::lib; -using namespace android; +namespace android { -namespace { +TEST(ResTableTest, ShouldLoadSuccessfully) { + std::string contents; + ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk", + "resources.arsc", &contents)); -/** - * Include a binary resource table. - * - * Package: com.android.test.basic - */ -#include "data/basic/basic_arsc.h" + ResTable table; + ASSERT_EQ(NO_ERROR, table.add(contents.data(), contents.size())); +} -/** - * Include a binary library resource table. - * - * Package: com.android.test.basic - */ -#include "data/lib/lib_arsc.h" +TEST(ResTableTest, SimpleTypeIsRetrievedCorrectly) { + std::string contents; + ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk", + "resources.arsc", &contents)); -/** - * Include a system resource table. - * - * Package: android - */ -#include "data/system/system_arsc.h" + ResTable table; + ASSERT_EQ(NO_ERROR, table.add(contents.data(), contents.size())); -TEST(ResTableTest, shouldLoadSuccessfully) { - ResTable table; - ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len)); + EXPECT_TRUE(IsStringEqual(table, basic::R::string::test1, "test1")); } -TEST(ResTableTest, simpleTypeIsRetrievedCorrectly) { - ResTable table; - ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len)); - - EXPECT_TRUE(IsStringEqual(table, base::R::string::test1, "test1")); +TEST(ResTableTest, ResourceNameIsResolved) { + std::string contents; + ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk", + "resources.arsc", &contents)); + + ResTable table; + ASSERT_EQ(NO_ERROR, table.add(contents.data(), contents.size())); + + String16 defPackage("com.android.basic"); + String16 testName("@string/test1"); + uint32_t resID = + table.identifierForName(testName.string(), testName.size(), 0, 0, + defPackage.string(), defPackage.size()); + ASSERT_NE(uint32_t(0x00000000), resID); + ASSERT_EQ(basic::R::string::test1, resID); } -TEST(ResTableTest, resourceNameIsResolved) { - ResTable table; - ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len)); - - String16 defPackage("com.android.test.basic"); - String16 testName("@string/test1"); - uint32_t resID = table.identifierForName(testName.string(), testName.size(), - 0, 0, - defPackage.string(), defPackage.size()); - ASSERT_NE(uint32_t(0x00000000), resID); - ASSERT_EQ(base::R::string::test1, resID); -} +TEST(ResTableTest, NoParentThemeIsAppliedCorrectly) { + std::string contents; + ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk", + "resources.arsc", &contents)); -TEST(ResTableTest, noParentThemeIsAppliedCorrectly) { - ResTable table; - ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len)); + ResTable table; + ASSERT_EQ(NO_ERROR, table.add(contents.data(), contents.size())); - ResTable::Theme theme(table); - ASSERT_EQ(NO_ERROR, theme.applyStyle(base::R::style::Theme1)); + ResTable::Theme theme(table); + ASSERT_EQ(NO_ERROR, theme.applyStyle(basic::R::style::Theme1)); - Res_value val; - uint32_t specFlags = 0; - ssize_t index = theme.getAttribute(base::R::attr::attr1, &val, &specFlags); - ASSERT_GE(index, 0); - ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType); - ASSERT_EQ(uint32_t(100), val.data); + Res_value val; + uint32_t specFlags = 0; + ssize_t index = theme.getAttribute(basic::R::attr::attr1, &val, &specFlags); + ASSERT_GE(index, 0); + ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType); + ASSERT_EQ(uint32_t(100), val.data); - index = theme.getAttribute(base::R::attr::attr2, &val, &specFlags); - ASSERT_GE(index, 0); - ASSERT_EQ(Res_value::TYPE_REFERENCE, val.dataType); - ASSERT_EQ(base::R::integer::number1, val.data); + index = theme.getAttribute(basic::R::attr::attr2, &val, &specFlags); + ASSERT_GE(index, 0); + ASSERT_EQ(Res_value::TYPE_REFERENCE, val.dataType); + ASSERT_EQ(basic::R::integer::number1, val.data); } -TEST(ResTableTest, parentThemeIsAppliedCorrectly) { - ResTable table; - ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len)); +TEST(ResTableTest, ParentThemeIsAppliedCorrectly) { + std::string contents; + ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk", + "resources.arsc", &contents)); - ResTable::Theme theme(table); - ASSERT_EQ(NO_ERROR, theme.applyStyle(base::R::style::Theme2)); + ResTable table; + ASSERT_EQ(NO_ERROR, table.add(contents.data(), contents.size())); - Res_value val; - uint32_t specFlags = 0; - ssize_t index = theme.getAttribute(base::R::attr::attr1, &val, &specFlags); - ASSERT_GE(index, 0); - ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType); - ASSERT_EQ(uint32_t(300), val.data); + ResTable::Theme theme(table); + ASSERT_EQ(NO_ERROR, theme.applyStyle(basic::R::style::Theme2)); - index = theme.getAttribute(base::R::attr::attr2, &val, &specFlags); - ASSERT_GE(index, 0); - ASSERT_EQ(Res_value::TYPE_REFERENCE, val.dataType); - ASSERT_EQ(base::R::integer::number1, val.data); -} + Res_value val; + uint32_t specFlags = 0; + ssize_t index = theme.getAttribute(basic::R::attr::attr1, &val, &specFlags); + ASSERT_GE(index, 0); + ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType); + ASSERT_EQ(uint32_t(300), val.data); -TEST(ResTableTest, libraryThemeIsAppliedCorrectly) { - ResTable table; - ASSERT_EQ(NO_ERROR, table.add(lib_arsc, lib_arsc_len)); - - ResTable::Theme theme(table); - ASSERT_EQ(NO_ERROR, theme.applyStyle(lib::R::style::Theme)); - - Res_value val; - uint32_t specFlags = 0; - ssize_t index = theme.getAttribute(lib::R::attr::attr1, &val, &specFlags); - ASSERT_GE(index, 0); - ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType); - ASSERT_EQ(uint32_t(700), val.data); - - index = theme.getAttribute(lib::R::attr::attr2, &val, &specFlags); - ASSERT_GE(index, 0); - ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType); - ASSERT_EQ(uint32_t(700), val.data); + index = theme.getAttribute(basic::R::attr::attr2, &val, &specFlags); + ASSERT_GE(index, 0); + ASSERT_EQ(Res_value::TYPE_REFERENCE, val.dataType); + ASSERT_EQ(basic::R::integer::number1, val.data); } -TEST(ResTableTest, referenceToBagIsNotResolved) { - ResTable table; - ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len)); - - Res_value val; - ssize_t block = table.getResource(base::R::integer::number2, &val, MAY_NOT_BE_BAG); - ASSERT_GE(block, 0); - ASSERT_EQ(Res_value::TYPE_REFERENCE, val.dataType); - ASSERT_EQ(base::R::array::integerArray1, val.data); - - ssize_t newBlock = table.resolveReference(&val, block); - EXPECT_EQ(block, newBlock); - EXPECT_EQ(Res_value::TYPE_REFERENCE, val.dataType); - EXPECT_EQ(base::R::array::integerArray1, val.data); -} +TEST(ResTableTest, LibraryThemeIsAppliedCorrectly) { + std::string contents; + ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/lib/lib.apk", + "resources.arsc", &contents)); -TEST(ResTableTest, resourcesStillAccessibleAfterParameterChange) { - ResTable table; - ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len)); + ResTable table; + ASSERT_EQ(NO_ERROR, table.add(contents.data(), contents.size())); - Res_value val; - ssize_t block = table.getResource(base::R::integer::number1, &val, MAY_NOT_BE_BAG); - ASSERT_GE(block, 0); - ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType); + ResTable::Theme theme(table); + ASSERT_EQ(NO_ERROR, theme.applyStyle(lib::R::style::Theme)); - const ResTable::bag_entry* entry; - ssize_t count = table.lockBag(base::R::array::integerArray1, &entry); - ASSERT_GE(count, 0); - table.unlockBag(entry); + Res_value val; + uint32_t specFlags = 0; + ssize_t index = theme.getAttribute(lib::R::attr::attr1, &val, &specFlags); + ASSERT_GE(index, 0); + ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType); + ASSERT_EQ(uint32_t(700), val.data); - ResTable_config param; - memset(¶m, 0, sizeof(param)); - param.density = 320; - table.setParameters(¶m); + index = theme.getAttribute(lib::R::attr::attr2, &val, &specFlags); + ASSERT_GE(index, 0); + ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType); + ASSERT_EQ(uint32_t(700), val.data); +} - block = table.getResource(base::R::integer::number1, &val, MAY_NOT_BE_BAG); - ASSERT_GE(block, 0); - ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType); +TEST(ResTableTest, ReferenceToBagIsNotResolved) { + std::string contents; + ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk", + "resources.arsc", &contents)); + + ResTable table; + ASSERT_EQ(NO_ERROR, table.add(contents.data(), contents.size())); + + Res_value val; + ssize_t block = + table.getResource(basic::R::integer::number2, &val, MAY_NOT_BE_BAG); + ASSERT_GE(block, 0); + ASSERT_EQ(Res_value::TYPE_REFERENCE, val.dataType); + ASSERT_EQ(basic::R::array::integerArray1, val.data); + + ssize_t newBlock = table.resolveReference(&val, block); + EXPECT_EQ(block, newBlock); + EXPECT_EQ(Res_value::TYPE_REFERENCE, val.dataType); + EXPECT_EQ(basic::R::array::integerArray1, val.data); +} - count = table.lockBag(base::R::array::integerArray1, &entry); - ASSERT_GE(count, 0); - table.unlockBag(entry); +TEST(ResTableTest, ResourcesStillAccessibleAfterParameterChange) { + std::string contents; + ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk", + "resources.arsc", &contents)); + + ResTable table; + ASSERT_EQ(NO_ERROR, table.add(contents.data(), contents.size())); + + Res_value val; + ssize_t block = + table.getResource(basic::R::integer::number1, &val, MAY_NOT_BE_BAG); + ASSERT_GE(block, 0); + ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType); + + const ResTable::bag_entry* entry; + ssize_t count = table.lockBag(basic::R::array::integerArray1, &entry); + ASSERT_GE(count, 0); + table.unlockBag(entry); + + ResTable_config param; + memset(¶m, 0, sizeof(param)); + param.density = 320; + table.setParameters(¶m); + + block = table.getResource(basic::R::integer::number1, &val, MAY_NOT_BE_BAG); + ASSERT_GE(block, 0); + ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType); + + count = table.lockBag(basic::R::array::integerArray1, &entry); + ASSERT_GE(count, 0); + table.unlockBag(entry); } -TEST(ResTableTest, resourceIsOverridenWithBetterConfig) { - ResTable table; - ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len)); - - Res_value val; - ssize_t block = table.getResource(base::R::integer::number1, &val, MAY_NOT_BE_BAG); - ASSERT_GE(block, 0); - ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType); - ASSERT_EQ(uint32_t(200), val.data); - - ResTable_config param; - memset(¶m, 0, sizeof(param)); - param.language[0] = 's'; - param.language[1] = 'v'; - param.country[0] = 'S'; - param.country[1] = 'E'; - table.setParameters(¶m); - - block = table.getResource(base::R::integer::number1, &val, MAY_NOT_BE_BAG); - ASSERT_GE(block, 0); - ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType); - ASSERT_EQ(uint32_t(400), val.data); +TEST(ResTableTest, ResourceIsOverridenWithBetterConfig) { + std::string contents; + ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk", + "resources.arsc", &contents)); + + ResTable table; + ASSERT_EQ(NO_ERROR, table.add(contents.data(), contents.size())); + + Res_value val; + ssize_t block = + table.getResource(basic::R::integer::number1, &val, MAY_NOT_BE_BAG); + ASSERT_GE(block, 0); + ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType); + ASSERT_EQ(uint32_t(200), val.data); + + ResTable_config param; + memset(¶m, 0, sizeof(param)); + param.language[0] = 's'; + param.language[1] = 'v'; + param.country[0] = 'S'; + param.country[1] = 'E'; + table.setParameters(¶m); + + block = table.getResource(basic::R::integer::number1, &val, MAY_NOT_BE_BAG); + ASSERT_GE(block, 0); + ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType); + ASSERT_EQ(uint32_t(400), val.data); } TEST(ResTableTest, emptyTableHasSensibleDefaults) { - const int32_t assetCookie = 1; + const int32_t assetCookie = 1; - ResTable table; - ASSERT_EQ(NO_ERROR, table.addEmpty(assetCookie)); + ResTable table; + ASSERT_EQ(NO_ERROR, table.addEmpty(assetCookie)); - // Adding an empty table gives us one table! - ASSERT_EQ(uint32_t(1), table.getTableCount()); + // Adding an empty table gives us one table! + ASSERT_EQ(uint32_t(1), table.getTableCount()); - // Adding an empty table doesn't mean we get packages. - ASSERT_EQ(uint32_t(0), table.getBasePackageCount()); + // Adding an empty table doesn't mean we get packages. + ASSERT_EQ(uint32_t(0), table.getBasePackageCount()); - Res_value val; - ASSERT_LT(table.getResource(base::R::integer::number1, &val, MAY_NOT_BE_BAG), 0); + Res_value val; + ASSERT_LT(table.getResource(basic::R::integer::number1, &val, MAY_NOT_BE_BAG), + 0); } void testU16StringToInt(const char16_t* str, uint32_t expectedValue, bool expectSuccess, bool expectHex) { - size_t len = std::char_traits<char16_t>::length(str); + size_t len = std::char_traits<char16_t>::length(str); - // Gtest can't print UTF-16 strings, so we have to convert to UTF-8 :( - std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> convert; - std::string s = convert.to_bytes(std::u16string(str, len)); + // Gtest can't print UTF-16 strings, so we have to convert to UTF-8 :( + std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> convert; + std::string s = convert.to_bytes(std::u16string(str, len)); - Res_value out = {}; - ASSERT_EQ(expectSuccess, U16StringToInt(str, len, &out)) - << "Failed with " << s; + Res_value out = {}; + ASSERT_EQ(expectSuccess, U16StringToInt(str, len, &out)) << "Failed with " + << s; - if (!expectSuccess) { - ASSERT_EQ(out.TYPE_NULL, out.dataType) << "Failed with " << s; - return; - } + if (!expectSuccess) { + ASSERT_EQ(out.TYPE_NULL, out.dataType) << "Failed with " << s; + return; + } - if (expectHex) { - ASSERT_EQ(out.TYPE_INT_HEX, out.dataType) << "Failed with " << s; - } else { - ASSERT_EQ(out.TYPE_INT_DEC, out.dataType) << "Failed with " << s; - } + if (expectHex) { + ASSERT_EQ(out.TYPE_INT_HEX, out.dataType) << "Failed with " << s; + } else { + ASSERT_EQ(out.TYPE_INT_DEC, out.dataType) << "Failed with " << s; + } - ASSERT_EQ(expectedValue, out.data) << "Failed with " << s; + ASSERT_EQ(expectedValue, out.data) << "Failed with " << s; } TEST(ResTableTest, U16StringToInt) { - testU16StringToInt(u"", 0U, false, false); - testU16StringToInt(u" ", 0U, false, false); - testU16StringToInt(u"\t\n", 0U, false, false); - - testU16StringToInt(u"abcd", 0U, false, false); - testU16StringToInt(u"10abcd", 0U, false, false); - testU16StringToInt(u"42 42", 0U, false, false); - testU16StringToInt(u"- 42", 0U, false, false); - testU16StringToInt(u"-", 0U, false, false); - - testU16StringToInt(u"0x", 0U, false, true); - testU16StringToInt(u"0xnope", 0U, false, true); - testU16StringToInt(u"0X42", 0U, false, true); - testU16StringToInt(u"0x42 0x42", 0U, false, true); - testU16StringToInt(u"-0x0", 0U, false, true); - testU16StringToInt(u"-0x42", 0U, false, true); - testU16StringToInt(u"- 0x42", 0U, false, true); - - // Note that u" 42" would pass. This preserves the old behavior, but it may - // not be desired. - testU16StringToInt(u"42 ", 0U, false, false); - testU16StringToInt(u"0x42 ", 0U, false, true); - - // Decimal cases. - testU16StringToInt(u"0", 0U, true, false); - testU16StringToInt(u"-0", 0U, true, false); - testU16StringToInt(u"42", 42U, true, false); - testU16StringToInt(u" 42", 42U, true, false); - testU16StringToInt(u"-42", static_cast<uint32_t>(-42), true, false); - testU16StringToInt(u" -42", static_cast<uint32_t>(-42), true, false); - testU16StringToInt(u"042", 42U, true, false); - testU16StringToInt(u"-042", static_cast<uint32_t>(-42), true, false); - - // Hex cases. - testU16StringToInt(u"0x0", 0x0, true, true); - testU16StringToInt(u"0x42", 0x42, true, true); - testU16StringToInt(u" 0x42", 0x42, true, true); - - // Just before overflow cases: - testU16StringToInt(u"2147483647", INT_MAX, true, false); - testU16StringToInt(u"-2147483648", static_cast<uint32_t>(INT_MIN), true, - false); - testU16StringToInt(u"0xffffffff", UINT_MAX, true, true); - - // Overflow cases: - testU16StringToInt(u"2147483648", 0U, false, false); - testU16StringToInt(u"-2147483649", 0U, false, false); - testU16StringToInt(u"0x1ffffffff", 0U, false, true); + testU16StringToInt(u"", 0U, false, false); + testU16StringToInt(u" ", 0U, false, false); + testU16StringToInt(u"\t\n", 0U, false, false); + + testU16StringToInt(u"abcd", 0U, false, false); + testU16StringToInt(u"10abcd", 0U, false, false); + testU16StringToInt(u"42 42", 0U, false, false); + testU16StringToInt(u"- 42", 0U, false, false); + testU16StringToInt(u"-", 0U, false, false); + + testU16StringToInt(u"0x", 0U, false, true); + testU16StringToInt(u"0xnope", 0U, false, true); + testU16StringToInt(u"0X42", 0U, false, true); + testU16StringToInt(u"0x42 0x42", 0U, false, true); + testU16StringToInt(u"-0x0", 0U, false, true); + testU16StringToInt(u"-0x42", 0U, false, true); + testU16StringToInt(u"- 0x42", 0U, false, true); + + // Note that u" 42" would pass. This preserves the old behavior, but it may + // not be desired. + testU16StringToInt(u"42 ", 0U, false, false); + testU16StringToInt(u"0x42 ", 0U, false, true); + + // Decimal cases. + testU16StringToInt(u"0", 0U, true, false); + testU16StringToInt(u"-0", 0U, true, false); + testU16StringToInt(u"42", 42U, true, false); + testU16StringToInt(u" 42", 42U, true, false); + testU16StringToInt(u"-42", static_cast<uint32_t>(-42), true, false); + testU16StringToInt(u" -42", static_cast<uint32_t>(-42), true, false); + testU16StringToInt(u"042", 42U, true, false); + testU16StringToInt(u"-042", static_cast<uint32_t>(-42), true, false); + + // Hex cases. + testU16StringToInt(u"0x0", 0x0, true, true); + testU16StringToInt(u"0x42", 0x42, true, true); + testU16StringToInt(u" 0x42", 0x42, true, true); + + // Just before overflow cases: + testU16StringToInt(u"2147483647", INT_MAX, true, false); + testU16StringToInt(u"-2147483648", static_cast<uint32_t>(INT_MIN), true, + false); + testU16StringToInt(u"0xffffffff", UINT_MAX, true, true); + + // Overflow cases: + testU16StringToInt(u"2147483648", 0U, false, false); + testU16StringToInt(u"-2147483649", 0U, false, false); + testU16StringToInt(u"0x1ffffffff", 0U, false, true); } TEST(ResTableTest, ShareButDontModifyResTable) { - ResTable sharedTable; - ASSERT_EQ(NO_ERROR, sharedTable.add(basic_arsc, basic_arsc_len)); - - ResTable_config param; - memset(¶m, 0, sizeof(param)); - param.language[0] = 'v'; - param.language[1] = 's'; - sharedTable.setParameters(¶m); - - // Check that we get the default value for @integer:number1 - Res_value val; - ssize_t block = sharedTable.getResource(base::R::integer::number1, &val, MAY_NOT_BE_BAG); - ASSERT_GE(block, 0); - ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType); - ASSERT_EQ(uint32_t(600), val.data); - - // Create a new table that shares the entries of the shared table. - ResTable table; - ASSERT_EQ(NO_ERROR, table.add(&sharedTable, false)); - - // Set a new configuration on the new table. - memset(¶m, 0, sizeof(param)); - param.language[0] = 's'; - param.language[1] = 'v'; - param.country[0] = 'S'; - param.country[1] = 'E'; - table.setParameters(¶m); - - // Check that we get a new value in the new table. - block = table.getResource(base::R::integer::number1, &val, MAY_NOT_BE_BAG); - ASSERT_GE(block, 0); - ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType); - ASSERT_EQ(uint32_t(400), val.data); - - // Check that we still get the old value in the shared table. - block = sharedTable.getResource(base::R::integer::number1, &val, MAY_NOT_BE_BAG); - ASSERT_GE(block, 0); - ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType); - ASSERT_EQ(uint32_t(600), val.data); + std::string contents; + ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk", + "resources.arsc", &contents)); + + ResTable sharedTable; + ASSERT_EQ(NO_ERROR, sharedTable.add(contents.data(), contents.size())); + + ResTable_config param; + memset(¶m, 0, sizeof(param)); + param.language[0] = 'v'; + param.language[1] = 's'; + sharedTable.setParameters(¶m); + + // Check that we get the default value for @integer:number1 + Res_value val; + ssize_t block = + sharedTable.getResource(basic::R::integer::number1, &val, MAY_NOT_BE_BAG); + ASSERT_GE(block, 0); + ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType); + ASSERT_EQ(uint32_t(600), val.data); + + // Create a new table that shares the entries of the shared table. + ResTable table; + ASSERT_EQ(NO_ERROR, table.add(&sharedTable, false)); + + // Set a new configuration on the new table. + memset(¶m, 0, sizeof(param)); + param.language[0] = 's'; + param.language[1] = 'v'; + param.country[0] = 'S'; + param.country[1] = 'E'; + table.setParameters(¶m); + + // Check that we get a new value in the new table. + block = table.getResource(basic::R::integer::number1, &val, MAY_NOT_BE_BAG); + ASSERT_GE(block, 0); + ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType); + ASSERT_EQ(uint32_t(400), val.data); + + // Check that we still get the old value in the shared table. + block = + sharedTable.getResource(basic::R::integer::number1, &val, MAY_NOT_BE_BAG); + ASSERT_GE(block, 0); + ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType); + ASSERT_EQ(uint32_t(600), val.data); } TEST(ResTableTest, GetConfigurationsReturnsUniqueList) { - ResTable table; - ASSERT_EQ(NO_ERROR, table.add(system_arsc, system_arsc_len)); - ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len)); + std::string contents; + ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk", + "resources.arsc", &contents)); + + std::string system_contents; + ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/system/system.apk", + "resources.arsc", &system_contents)); + + ResTable table; + ASSERT_EQ(NO_ERROR, + table.add(system_contents.data(), system_contents.size())); + ASSERT_EQ(NO_ERROR, table.add(contents.data(), contents.size())); - ResTable_config configSv; - memset(&configSv, 0, sizeof(configSv)); - configSv.language[0] = 's'; - configSv.language[1] = 'v'; + ResTable_config configSv; + memset(&configSv, 0, sizeof(configSv)); + configSv.language[0] = 's'; + configSv.language[1] = 'v'; - Vector<ResTable_config> configs; - table.getConfigurations(&configs); + Vector<ResTable_config> configs; + table.getConfigurations(&configs); - EXPECT_EQ(1, std::count(configs.begin(), configs.end(), configSv)); + EXPECT_EQ(1, std::count(configs.begin(), configs.end(), configSv)); - Vector<String8> locales; - table.getLocales(&locales); + Vector<String8> locales; + table.getLocales(&locales); - EXPECT_EQ(1, std::count(locales.begin(), locales.end(), String8("sv"))); + EXPECT_EQ(1, std::count(locales.begin(), locales.end(), String8("sv"))); } -} // namespace +} // namespace android diff --git a/libs/androidfw/tests/Split_test.cpp b/libs/androidfw/tests/Split_test.cpp index b69d68572e6a..2c242dbd3e28 100644 --- a/libs/androidfw/tests/Split_test.cpp +++ b/libs/androidfw/tests/Split_test.cpp @@ -14,233 +14,255 @@ * limitations under the License. */ -#include <androidfw/ResourceTypes.h> +#include "androidfw/ResourceTypes.h" + +#include "utils/String16.h" +#include "utils/String8.h" -#include <utils/String8.h> -#include <utils/String16.h> #include "TestHelpers.h" #include "data/basic/R.h" -#include <gtest/gtest.h> - -using namespace android; - -namespace { - -/** - * Include a binary resource table. This table - * is a base table for an APK split. - * - * Package: com.android.test.basic - */ -#include "data/basic/basic_arsc.h" - -/** - * Include a binary resource table. This table - * is a configuration split table for an APK split. - * - * Package: com.android.test.basic - */ -#include "data/basic/split_de_fr_arsc.h" -#include "data/basic/split_hdpi_v4_arsc.h" -#include "data/basic/split_xhdpi_v4_arsc.h" -#include "data/basic/split_xxhdpi_v4_arsc.h" - -/** - * Include a binary resource table. This table - * is a feature split table for an APK split. - * - * Package: com.android.test.basic - */ -#include "data/feature/feature_arsc.h" +using com::android::basic::R; -enum { MAY_NOT_BE_BAG = false }; +namespace android { -void makeConfigFrench(ResTable_config* config) { - memset(config, 0, sizeof(*config)); - config->language[0] = 'f'; - config->language[1] = 'r'; +static void makeConfigFrench(ResTable_config* config) { + memset(config, 0, sizeof(*config)); + config->language[0] = 'f'; + config->language[1] = 'r'; } -TEST(SplitTest, TestLoadBase) { - ResTable table; - ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len)); +class SplitTest : public ::testing::Test { + public: + void SetUp() override { + ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk", + "resources.arsc", &basic_contents_)); + ASSERT_TRUE( + ReadFileFromZipToString(GetTestDataPath() + "/basic/basic_de_fr.apk", + "resources.arsc", &basic_de_fr_contents_)); + ASSERT_TRUE( + ReadFileFromZipToString(GetTestDataPath() + "/basic/basic_hdpi-v4.apk", + "resources.arsc", &basic_hdpi_contents_)); + ASSERT_TRUE( + ReadFileFromZipToString(GetTestDataPath() + "/basic/basic_xhdpi-v4.apk", + "resources.arsc", &basic_xhdpi_contents_)); + ASSERT_TRUE(ReadFileFromZipToString( + GetTestDataPath() + "/basic/basic_xxhdpi-v4.apk", "resources.arsc", + &basic_xxhdpi_contents_)); + ASSERT_TRUE( + ReadFileFromZipToString(GetTestDataPath() + "/feature/feature.apk", + "resources.arsc", &feature_contents_)); + } + + protected: + std::string basic_contents_; + std::string basic_de_fr_contents_; + std::string basic_hdpi_contents_; + std::string basic_xhdpi_contents_; + std::string basic_xxhdpi_contents_; + std::string feature_contents_; +}; + +TEST_F(SplitTest, TestLoadBase) { + ResTable table; + ASSERT_EQ(NO_ERROR, + table.add(basic_contents_.data(), basic_contents_.size())); } -TEST(SplitTest, TestGetResourceFromBase) { - ResTable_config frenchConfig; - makeConfigFrench(&frenchConfig); +TEST_F(SplitTest, TestGetResourceFromBase) { + ResTable_config frenchConfig; + makeConfigFrench(&frenchConfig); - ResTable table; - table.setParameters(&frenchConfig); + ResTable table; + table.setParameters(&frenchConfig); - ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len)); + ASSERT_EQ(NO_ERROR, + table.add(basic_contents_.data(), basic_contents_.size())); - ResTable_config expectedConfig; - memset(&expectedConfig, 0, sizeof(expectedConfig)); + ResTable_config expectedConfig; + memset(&expectedConfig, 0, sizeof(expectedConfig)); - Res_value val; - ResTable_config config; - ssize_t block = table.getResource(base::R::string::test1, &val, MAY_NOT_BE_BAG, 0, NULL, &config); + Res_value val; + ResTable_config config; + ssize_t block = table.getResource(R::string::test1, &val, MAY_NOT_BE_BAG, 0, + NULL, &config); - // The returned block should tell us which string pool to get the value, if it is a string. - EXPECT_GE(block, 0); + // The returned block should tell us which string pool to get the value, if it + // is a string. + EXPECT_GE(block, 0); - // We expect the default resource to be selected since it is the only resource configuration. - EXPECT_EQ(0, expectedConfig.compare(config)); + // We expect the default resource to be selected since it is the only resource + // configuration. + EXPECT_EQ(0, expectedConfig.compare(config)); - EXPECT_EQ(Res_value::TYPE_STRING, val.dataType); + EXPECT_EQ(Res_value::TYPE_STRING, val.dataType); } -TEST(SplitTest, TestGetResourceFromSplit) { - ResTable_config expectedConfig; - makeConfigFrench(&expectedConfig); +TEST_F(SplitTest, TestGetResourceFromSplit) { + ResTable_config expectedConfig; + makeConfigFrench(&expectedConfig); - ResTable table; - table.setParameters(&expectedConfig); + ResTable table; + table.setParameters(&expectedConfig); - ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len)); - ASSERT_EQ(NO_ERROR, table.add(split_de_fr_arsc, split_de_fr_arsc_len)); + ASSERT_EQ(NO_ERROR, + table.add(basic_contents_.data(), basic_contents_.size())); + ASSERT_EQ(NO_ERROR, table.add(basic_de_fr_contents_.data(), + basic_de_fr_contents_.size())); - Res_value val; - ResTable_config config; - ssize_t block = table.getResource(base::R::string::test1, &val, MAY_NOT_BE_BAG, 0, NULL, &config); + Res_value val; + ResTable_config config; + ssize_t block = table.getResource(R::string::test1, &val, MAY_NOT_BE_BAG, 0, + NULL, &config); - EXPECT_GE(block, 0); + EXPECT_GE(block, 0); - EXPECT_EQ(0, expectedConfig.compare(config)); + EXPECT_EQ(0, expectedConfig.compare(config)); - EXPECT_EQ(Res_value::TYPE_STRING, val.dataType); + EXPECT_EQ(Res_value::TYPE_STRING, val.dataType); } -TEST(SplitTest, ResourcesFromBaseAndSplitHaveSameNames) { - ResTable_config expectedConfig; - makeConfigFrench(&expectedConfig); +TEST_F(SplitTest, ResourcesFromBaseAndSplitHaveSameNames) { + ResTable_config expectedConfig; + makeConfigFrench(&expectedConfig); - ResTable table; - table.setParameters(&expectedConfig); + ResTable table; + table.setParameters(&expectedConfig); - ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len)); + ASSERT_EQ(NO_ERROR, + table.add(basic_contents_.data(), basic_contents_.size())); - ResTable::resource_name baseName; - EXPECT_TRUE(table.getResourceName(base::R::string::test1, false, &baseName)); + ResTable::resource_name baseName; + EXPECT_TRUE(table.getResourceName(R::string::test1, false, &baseName)); - ASSERT_EQ(NO_ERROR, table.add(split_de_fr_arsc, split_de_fr_arsc_len)); + ASSERT_EQ(NO_ERROR, table.add(basic_de_fr_contents_.data(), + basic_de_fr_contents_.size())); - ResTable::resource_name frName; - EXPECT_TRUE(table.getResourceName(base::R::string::test1, false, &frName)); + ResTable::resource_name frName; + EXPECT_TRUE(table.getResourceName(R::string::test1, false, &frName)); - EXPECT_EQ( - String16(baseName.package, baseName.packageLen), + EXPECT_EQ(String16(baseName.package, baseName.packageLen), String16(frName.package, frName.packageLen)); - EXPECT_EQ( - String16(baseName.type, baseName.typeLen), + EXPECT_EQ(String16(baseName.type, baseName.typeLen), String16(frName.type, frName.typeLen)); - EXPECT_EQ( - String16(baseName.name, baseName.nameLen), + EXPECT_EQ(String16(baseName.name, baseName.nameLen), String16(frName.name, frName.nameLen)); } -TEST(SplitTest, TypeEntrySpecFlagsAreUpdated) { - ResTable_config defaultConfig; - memset(&defaultConfig, 0, sizeof(defaultConfig)); +TEST_F(SplitTest, TypeEntrySpecFlagsAreUpdated) { + ResTable_config defaultConfig; + memset(&defaultConfig, 0, sizeof(defaultConfig)); - ResTable table; - ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len)); + ResTable table; + ASSERT_EQ(NO_ERROR, + table.add(basic_contents_.data(), basic_contents_.size())); - Res_value val; - uint32_t specFlags = 0; - ssize_t block = table.getResource(base::R::string::test1, &val, MAY_NOT_BE_BAG, 0, &specFlags, NULL); - EXPECT_GE(block, 0); + Res_value val; + uint32_t specFlags = 0; + ssize_t block = table.getResource(R::string::test1, &val, MAY_NOT_BE_BAG, 0, + &specFlags, NULL); + EXPECT_GE(block, 0); - EXPECT_EQ(static_cast<uint32_t>(0), specFlags); + EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), specFlags); - ASSERT_EQ(NO_ERROR, table.add(split_de_fr_arsc, split_de_fr_arsc_len)); + ASSERT_EQ(NO_ERROR, table.add(basic_de_fr_contents_.data(), + basic_de_fr_contents_.size())); - uint32_t frSpecFlags = 0; - block = table.getResource(base::R::string::test1, &val, MAY_NOT_BE_BAG, 0, &frSpecFlags, NULL); - EXPECT_GE(block, 0); + uint32_t frSpecFlags = 0; + block = table.getResource(R::string::test1, &val, MAY_NOT_BE_BAG, 0, + &frSpecFlags, NULL); + ASSERT_GE(block, 0); - EXPECT_EQ(ResTable_config::CONFIG_LOCALE, frSpecFlags); + EXPECT_EQ(static_cast<uint32_t>(ResTable_config::CONFIG_LOCALE | ResTable_typeSpec::SPEC_PUBLIC), + frSpecFlags); } -TEST(SplitTest, SelectBestDensity) { - ResTable_config baseConfig; - memset(&baseConfig, 0, sizeof(baseConfig)); - baseConfig.density = ResTable_config::DENSITY_XHIGH; - baseConfig.sdkVersion = 21; +TEST_F(SplitTest, SelectBestDensity) { + ResTable_config baseConfig; + memset(&baseConfig, 0, sizeof(baseConfig)); + baseConfig.density = ResTable_config::DENSITY_XHIGH; + baseConfig.sdkVersion = 21; - ResTable table; - table.setParameters(&baseConfig); - ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len)); - ASSERT_EQ(NO_ERROR, table.add(split_hdpi_v4_arsc, split_hdpi_v4_arsc_len)); + ResTable table; + table.setParameters(&baseConfig); + ASSERT_EQ(NO_ERROR, + table.add(basic_contents_.data(), basic_contents_.size())); + ASSERT_EQ(NO_ERROR, table.add(basic_hdpi_contents_.data(), + basic_hdpi_contents_.size())); - EXPECT_TRUE(IsStringEqual(table, base::R::string::density, "hdpi")); + EXPECT_TRUE(IsStringEqual(table, R::string::density, "hdpi")); - ASSERT_EQ(NO_ERROR, table.add(split_xhdpi_v4_arsc, split_xhdpi_v4_arsc_len)); + ASSERT_EQ(NO_ERROR, table.add(basic_xhdpi_contents_.data(), + basic_xhdpi_contents_.size())); - EXPECT_TRUE(IsStringEqual(table, base::R::string::density, "xhdpi")); + EXPECT_TRUE(IsStringEqual(table, R::string::density, "xhdpi")); - ASSERT_EQ(NO_ERROR, table.add(split_xxhdpi_v4_arsc, split_xxhdpi_v4_arsc_len)); + ASSERT_EQ(NO_ERROR, table.add(basic_xxhdpi_contents_.data(), + basic_xxhdpi_contents_.size())); - EXPECT_TRUE(IsStringEqual(table, base::R::string::density, "xhdpi")); + EXPECT_TRUE(IsStringEqual(table, R::string::density, "xhdpi")); - baseConfig.density = ResTable_config::DENSITY_XXHIGH; - table.setParameters(&baseConfig); + baseConfig.density = ResTable_config::DENSITY_XXHIGH; + table.setParameters(&baseConfig); - EXPECT_TRUE(IsStringEqual(table, base::R::string::density, "xxhdpi")); + EXPECT_TRUE(IsStringEqual(table, R::string::density, "xxhdpi")); } -TEST(SplitFeatureTest, TestNewResourceIsAccessible) { - ResTable table; - ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len)); +TEST_F(SplitTest, TestNewResourceIsAccessible) { + ResTable table; + ASSERT_EQ(NO_ERROR, + table.add(basic_contents_.data(), basic_contents_.size())); - Res_value val; - ssize_t block = table.getResource(base::R::string::test3, &val, MAY_NOT_BE_BAG); - EXPECT_LT(block, 0); + Res_value val; + ssize_t block = table.getResource(R::string::test3, &val, MAY_NOT_BE_BAG); + EXPECT_LT(block, 0); - ASSERT_EQ(NO_ERROR, table.add(feature_arsc, feature_arsc_len)); + ASSERT_EQ(NO_ERROR, + table.add(feature_contents_.data(), feature_contents_.size())); - block = table.getResource(base::R::string::test3, &val, MAY_NOT_BE_BAG); - EXPECT_GE(block, 0); + block = table.getResource(R::string::test3, &val, MAY_NOT_BE_BAG); + ASSERT_GE(block, 0); - EXPECT_EQ(Res_value::TYPE_STRING, val.dataType); + EXPECT_EQ(Res_value::TYPE_STRING, val.dataType); } -TEST(SplitFeatureTest, TestNewResourceNameHasCorrectName) { - ResTable table; - ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len)); +TEST_F(SplitTest, TestNewResourceNameHasCorrectName) { + ResTable table; + ASSERT_EQ(NO_ERROR, + table.add(basic_contents_.data(), basic_contents_.size())); - ResTable::resource_name name; - EXPECT_FALSE(table.getResourceName(base::R::string::test3, false, &name)); + ResTable::resource_name name; + EXPECT_FALSE(table.getResourceName(R::string::test3, false, &name)); - ASSERT_EQ(NO_ERROR, table.add(feature_arsc, feature_arsc_len)); + ASSERT_EQ(NO_ERROR, + table.add(feature_contents_.data(), feature_contents_.size())); - ASSERT_TRUE(table.getResourceName(base::R::string::test3, false, &name)); + ASSERT_TRUE(table.getResourceName(R::string::test3, false, &name)); - EXPECT_EQ(String16("com.android.test.basic"), + EXPECT_EQ(String16("com.android.basic"), String16(name.package, name.packageLen)); - EXPECT_EQ(String16("string"), - String16(name.type, name.typeLen)); + EXPECT_EQ(String16("string"), String16(name.type, name.typeLen)); - EXPECT_EQ(String16("test3"), - String16(name.name, name.nameLen)); + EXPECT_EQ(String16("test3"), String16(name.name, name.nameLen)); } -TEST(SplitFeatureTest, TestNewResourceIsAccessibleByName) { - ResTable table; - ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len)); - ASSERT_EQ(NO_ERROR, table.add(feature_arsc, feature_arsc_len)); - - const String16 name("test3"); - const String16 type("string"); - const String16 package("com.android.test.basic"); - ASSERT_EQ(base::R::string::test3, table.identifierForName(name.string(), name.size(), - type.string(), type.size(), - package.string(), package.size())); +TEST_F(SplitTest, TestNewResourceIsAccessibleByName) { + ResTable table; + ASSERT_EQ(NO_ERROR, + table.add(basic_contents_.data(), basic_contents_.size())); + ASSERT_EQ(NO_ERROR, + table.add(feature_contents_.data(), feature_contents_.size())); + + const String16 name("test3"); + const String16 type("string"); + const String16 package("com.android.basic"); + ASSERT_EQ( + R::string::test3, + table.identifierForName(name.string(), name.size(), type.string(), + type.size(), package.string(), package.size())); } -} // namespace +} // namespace diff --git a/libs/androidfw/tests/TestHelpers.cpp b/libs/androidfw/tests/TestHelpers.cpp index 41a19a7f2b24..1e763a5e53a8 100644 --- a/libs/androidfw/tests/TestHelpers.cpp +++ b/libs/androidfw/tests/TestHelpers.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014 The Android Open Source Project + * 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. @@ -16,33 +16,119 @@ #include "TestHelpers.h" -#include <androidfw/ResourceTypes.h> -#include <utils/String8.h> -#include <gtest/gtest.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 { -::testing::AssertionResult IsStringEqual(const ResTable& table, uint32_t resourceId, const char* expectedStr) { - Res_value val; - ssize_t block = table.getResource(resourceId, &val, MAY_NOT_BE_BAG); - if (block < 0) { - return ::testing::AssertionFailure() << "could not find resource"; - } +static std::string sTestDataPath; - if (val.dataType != Res_value::TYPE_STRING) { - return ::testing::AssertionFailure() << "resource is not a string"; - } +// 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; +} - const ResStringPool* pool = table.getTableStringBlock(block); - if (pool == NULL) { - return ::testing::AssertionFailure() << "table has no string pool for block " << block; - } +void InitializeTest(int* argc, char** argv) { + // Set the default test data path to be the executable path directory. + SetTestDataPath(GetExecutableDir()); - const String8 actual = pool->string8ObjectAt(val.data); - if (String8(expectedStr) != actual) { - return ::testing::AssertionFailure() << actual.string(); + 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); } - return ::testing::AssertionSuccess() << actual.string(); + } +} + +void SetTestDataPath(const std::string& path) { sTestDataPath = path; } + +const std::string& GetTestDataPath() { + CHECK(!sTestDataPath.empty()) << "no test data path set."; + return sTestDataPath; +} + +::testing::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); + } + + ::ZipString name(file.c_str()); + ::ZipEntry entry; + result = ::FindEntry(handle, name, &entry); + if (result != 0) { + ::CloseArchive(handle); + return ::testing::AssertionFailure() << "Could not find file '" << file << "' in zip '" + << zip_path << "' : " << ::ErrorCodeString(result); + } + + out_contents->resize(entry.uncompressed_length); + result = ::ExtractToMemory( + handle, &entry, const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(out_contents->data())), + out_contents->size()); + if (result != 0) { + ::CloseArchive(handle); + return ::testing::AssertionFailure() << "Failed to extract file '" << file << "' from zip '" + << zip_path << "': " << ::ErrorCodeString(result); + } + + ::CloseArchive(handle); + return ::testing::AssertionSuccess(); +} + +::testing::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"; + } + + if (val.dataType != Res_value::TYPE_STRING) { + return ::testing::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; + } + + const String8 actual_str = pool->string8ObjectAt(val.data); + if (String8(expected_str) != actual_str) { + return ::testing::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()); } -} // namespace android +} // namespace android diff --git a/libs/androidfw/tests/TestHelpers.h b/libs/androidfw/tests/TestHelpers.h index ff9be164dbb0..a11ea8416c7d 100644 --- a/libs/androidfw/tests/TestHelpers.h +++ b/libs/androidfw/tests/TestHelpers.h @@ -1,35 +1,65 @@ -#ifndef __TEST_HELPERS_H -#define __TEST_HELPERS_H +/* + * 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. + */ + +#ifndef TEST_HELPERS_H_ +#define TEST_HELPERS_H_ #include <ostream> +#include <string> -#include <androidfw/ResourceTypes.h> -#include <utils/String8.h> -#include <utils/String16.h> -#include <gtest/gtest.h> +#include "androidfw/ResourceTypes.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(); + return out << str.string(); } static inline ::std::ostream& operator<<(::std::ostream& out, const android::String16& str) { - return out << android::String8(str).string(); + return out << android::String8(str).string(); } namespace android { +void InitializeTest(int* argc, char** argv); + enum { MAY_NOT_BE_BAG = false }; -static inline bool operator==(const android::ResTable_config& a, const android::ResTable_config& b) { - return a.compare(b) == 0; +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 android::ResTable_config& c) { - return out << c.toString().string(); +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 resourceId, const char* expectedStr); +::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 +} // namespace android -#endif // __TEST_HELPERS_H +#endif // TEST_HELPERS_H_ diff --git a/libs/androidfw/tests/TestMain.cpp b/libs/androidfw/tests/TestMain.cpp new file mode 100644 index 000000000000..d1c0f6057dd1 --- /dev/null +++ b/libs/androidfw/tests/TestMain.cpp @@ -0,0 +1,28 @@ +/* + * 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 <iostream> + +#include "TestHelpers.h" + +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + ::android::InitializeTest(&argc, argv); + + std::cerr << "using --testdata=" << ::android::GetTestDataPath() << "\n"; + + return RUN_ALL_TESTS(); +} diff --git a/libs/androidfw/tests/Theme_bench.cpp b/libs/androidfw/tests/Theme_bench.cpp new file mode 100644 index 000000000000..c471be6cd09d --- /dev/null +++ b/libs/androidfw/tests/Theme_bench.cpp @@ -0,0 +1,99 @@ +/* + * 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 "benchmark/benchmark.h" + +#include "androidfw/ApkAssets.h" +#include "androidfw/AssetManager.h" +#include "androidfw/AssetManager2.h" +#include "androidfw/ResourceTypes.h" + +namespace android { + +constexpr const static char* kFrameworkPath = "/system/framework/framework-res.apk"; +constexpr const static uint32_t kStyleId = 0x01030237u; // android:style/Theme.Material.Light +constexpr const static uint32_t kAttrId = 0x01010030u; // android:attr/colorForeground + +static void BM_ThemeApplyStyleFramework(benchmark::State& state) { + std::unique_ptr<ApkAssets> apk = ApkAssets::Load(kFrameworkPath); + if (apk == nullptr) { + state.SkipWithError("Failed to load assets"); + return; + } + + AssetManager2 assets; + assets.SetApkAssets({apk.get()}); + + while (state.KeepRunning()) { + auto theme = assets.NewTheme(); + theme->ApplyStyle(kStyleId, false /* force */); + } +} +BENCHMARK(BM_ThemeApplyStyleFramework); + +static void BM_ThemeApplyStyleFrameworkOld(benchmark::State& state) { + AssetManager assets; + if (!assets.addAssetPath(String8(kFrameworkPath), nullptr /* cookie */, false /* appAsLib */, + true /* isSystemAsset */)) { + state.SkipWithError("Failed to load assets"); + return; + } + + const ResTable& res_table = assets.getResources(true); + + while (state.KeepRunning()) { + std::unique_ptr<ResTable::Theme> theme{new ResTable::Theme(res_table)}; + theme->applyStyle(kStyleId, false /* force */); + } +} +BENCHMARK(BM_ThemeApplyStyleFrameworkOld); + +static void BM_ThemeGetAttribute(benchmark::State& state) { + std::unique_ptr<ApkAssets> apk = ApkAssets::Load(kFrameworkPath); + + AssetManager2 assets; + assets.SetApkAssets({apk.get()}); + + auto theme = assets.NewTheme(); + theme->ApplyStyle(kStyleId, false /* force */); + + Res_value value; + uint32_t flags; + + while (state.KeepRunning()) { + theme->GetAttribute(kAttrId, &value, &flags); + } +} +BENCHMARK(BM_ThemeGetAttribute); + +static void BM_ThemeGetAttributeOld(benchmark::State& state) { + AssetManager assets; + assets.addAssetPath(String8(kFrameworkPath), nullptr /* cookie */, false /* appAsLib */, + true /* isSystemAsset */); + const ResTable& res_table = assets.getResources(true); + std::unique_ptr<ResTable::Theme> theme{new ResTable::Theme(res_table)}; + theme->applyStyle(kStyleId, false /* force */); + + Res_value value; + uint32_t flags; + + while (state.KeepRunning()) { + theme->getAttribute(kAttrId, &value, &flags); + } +} +BENCHMARK(BM_ThemeGetAttributeOld); + +} // namespace android diff --git a/libs/androidfw/tests/Theme_test.cpp b/libs/androidfw/tests/Theme_test.cpp index 4d07130acb39..c0011b6d6e89 100644 --- a/libs/androidfw/tests/Theme_test.cpp +++ b/libs/androidfw/tests/Theme_test.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014 The Android Open Source Project + * 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. @@ -14,55 +14,221 @@ * limitations under the License. */ -#include <androidfw/ResourceTypes.h> +#include "androidfw/AssetManager2.h" + +#include "android-base/logging.h" -#include <utils/String8.h> -#include <utils/String16.h> #include "TestHelpers.h" -#include "data/system/R.h" -#include "data/app/R.h" +#include "data/styles/R.h" -#include <gtest/gtest.h> +namespace app = com::android::app; -using namespace android; +namespace android { -namespace { +class ThemeTest : public ::testing::Test { + public: + void SetUp() override { + style_assets_ = ApkAssets::Load(GetTestDataPath() + "/styles/styles.apk"); + ASSERT_NE(nullptr, style_assets_); + } -#include "data/system/system_arsc.h" -#include "data/app/app_arsc.h" + protected: + std::unique_ptr<ApkAssets> style_assets_; +}; -enum { MAY_NOT_BE_BAG = false }; +TEST_F(ThemeTest, EmptyTheme) { + AssetManager2 assetmanager; + assetmanager.SetApkAssets({style_assets_.get()}); -/** - * TODO(adamlesinski): Enable when fixed. - */ -TEST(ThemeTest, DISABLED_shouldCopyThemeFromDifferentResTable) { - ResTable table; - ASSERT_EQ(NO_ERROR, table.add(system_arsc, system_arsc_len)); - ASSERT_EQ(NO_ERROR, table.add(app_arsc, app_arsc_len)); - - ResTable::Theme theme1(table); - ASSERT_EQ(NO_ERROR, theme1.applyStyle(app::R::style::Theme_One)); - Res_value val; - ASSERT_GE(theme1.getAttribute(android::R::attr::background, &val), 0); - ASSERT_EQ(Res_value::TYPE_INT_COLOR_RGB8, val.dataType); - ASSERT_EQ(uint32_t(0xffff0000), val.data); - ASSERT_GE(theme1.getAttribute(app::R::attr::number, &val), 0); - ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType); - ASSERT_EQ(uint32_t(1), val.data); - - ResTable table2; - ASSERT_EQ(NO_ERROR, table2.add(system_arsc, system_arsc_len)); - ASSERT_EQ(NO_ERROR, table2.add(app_arsc, app_arsc_len)); - - ResTable::Theme theme2(table2); - ASSERT_EQ(NO_ERROR, theme2.setTo(theme1)); - ASSERT_GE(theme2.getAttribute(android::R::attr::background, &val), 0); - ASSERT_EQ(Res_value::TYPE_INT_COLOR_RGB8, val.dataType); - ASSERT_EQ(uint32_t(0xffff0000), val.data); - ASSERT_GE(theme2.getAttribute(app::R::attr::number, &val), 0); - ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType); - ASSERT_EQ(uint32_t(1), val.data); + std::unique_ptr<Theme> theme = assetmanager.NewTheme(); + EXPECT_EQ(0u, theme->GetChangingConfigurations()); + EXPECT_EQ(&assetmanager, theme->GetAssetManager()); + + Res_value value; + uint32_t flags; + EXPECT_EQ(kInvalidCookie, theme->GetAttribute(app::R::attr::attr_one, &value, &flags)); +} + +TEST_F(ThemeTest, SingleThemeNoParent) { + AssetManager2 assetmanager; + assetmanager.SetApkAssets({style_assets_.get()}); + + std::unique_ptr<Theme> theme = assetmanager.NewTheme(); + ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleOne)); + + Res_value value; + uint32_t flags; + ApkAssetsCookie cookie; + + cookie = theme->GetAttribute(app::R::attr::attr_one, &value, &flags); + ASSERT_NE(kInvalidCookie, cookie); + EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType); + EXPECT_EQ(1u, value.data); + EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags); + + cookie = theme->GetAttribute(app::R::attr::attr_two, &value, &flags); + ASSERT_NE(kInvalidCookie, cookie); + EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType); + EXPECT_EQ(2u, value.data); + EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags); +} + +TEST_F(ThemeTest, SingleThemeWithParent) { + 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; + ApkAssetsCookie cookie; + + cookie = theme->GetAttribute(app::R::attr::attr_one, &value, &flags); + ASSERT_NE(kInvalidCookie, cookie); + EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType); + EXPECT_EQ(1u, value.data); + EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags); + + cookie = theme->GetAttribute(app::R::attr::attr_two, &value, &flags); + ASSERT_NE(kInvalidCookie, cookie); + EXPECT_EQ(Res_value::TYPE_STRING, value.dataType); + EXPECT_EQ(0, cookie); + EXPECT_EQ(std::string("string"), + GetStringFromPool(assetmanager.GetStringPoolForCookie(0), value.data)); + EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags); + + // This attribute should point to an attr_indirect, so the result should be 3. + cookie = theme->GetAttribute(app::R::attr::attr_three, &value, &flags); + ASSERT_NE(kInvalidCookie, cookie); + EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType); + EXPECT_EQ(3u, value.data); + EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags); +} + +TEST_F(ThemeTest, MultipleThemesOverlaidNotForce) { + AssetManager2 assetmanager; + assetmanager.SetApkAssets({style_assets_.get()}); + + std::unique_ptr<Theme> theme = assetmanager.NewTheme(); + ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleTwo)); + ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleThree)); + + Res_value value; + uint32_t flags; + ApkAssetsCookie cookie; + + // attr_one is still here from the base. + cookie = theme->GetAttribute(app::R::attr::attr_one, &value, &flags); + ASSERT_NE(kInvalidCookie, cookie); + EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType); + EXPECT_EQ(1u, value.data); + EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags); + + // check for the new attr_six + cookie = theme->GetAttribute(app::R::attr::attr_six, &value, &flags); + ASSERT_NE(kInvalidCookie, cookie); + EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType); + EXPECT_EQ(6u, value.data); + EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags); + + // check for the old attr_five (force=true was not used). + cookie = theme->GetAttribute(app::R::attr::attr_five, &value, &flags); + ASSERT_NE(kInvalidCookie, cookie); + EXPECT_EQ(Res_value::TYPE_REFERENCE, value.dataType); + EXPECT_EQ(app::R::string::string_one, value.data); + EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags); } +TEST_F(ThemeTest, MultipleThemesOverlaidForced) { + AssetManager2 assetmanager; + assetmanager.SetApkAssets({style_assets_.get()}); + + std::unique_ptr<Theme> theme = assetmanager.NewTheme(); + ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleTwo)); + ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleThree, true /* force */)); + + Res_value value; + uint32_t flags; + ApkAssetsCookie cookie; + + // attr_one is still here from the base. + cookie = theme->GetAttribute(app::R::attr::attr_one, &value, &flags); + ASSERT_NE(kInvalidCookie, cookie); + EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType); + EXPECT_EQ(1u, value.data); + EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags); + + // check for the new attr_six + cookie = theme->GetAttribute(app::R::attr::attr_six, &value, &flags); + ASSERT_NE(kInvalidCookie, cookie); + EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType); + EXPECT_EQ(6u, value.data); + EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags); + + // check for the new attr_five (force=true was used). + cookie = theme->GetAttribute(app::R::attr::attr_five, &value, &flags); + ASSERT_NE(kInvalidCookie, cookie); + EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType); + EXPECT_EQ(5u, value.data); + EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags); } + +TEST_F(ThemeTest, CopyThemeSameAssetManager) { + AssetManager2 assetmanager; + assetmanager.SetApkAssets({style_assets_.get()}); + + std::unique_ptr<Theme> theme_one = assetmanager.NewTheme(); + ASSERT_TRUE(theme_one->ApplyStyle(app::R::style::StyleOne)); + + Res_value value; + uint32_t flags; + ApkAssetsCookie cookie; + + // attr_one is still here from the base. + cookie = theme_one->GetAttribute(app::R::attr::attr_one, &value, &flags); + ASSERT_NE(kInvalidCookie, cookie); + EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType); + EXPECT_EQ(1u, value.data); + EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags); + + // attr_six is not here. + EXPECT_EQ(kInvalidCookie, theme_one->GetAttribute(app::R::attr::attr_six, &value, &flags)); + + std::unique_ptr<Theme> theme_two = assetmanager.NewTheme(); + ASSERT_TRUE(theme_two->ApplyStyle(app::R::style::StyleThree)); + + // Copy the theme to theme_one. + ASSERT_TRUE(theme_one->SetTo(*theme_two)); + + // Clear theme_two to make sure we test that there WAS a copy. + theme_two->Clear(); + + // attr_one is now not here. + EXPECT_EQ(kInvalidCookie, theme_one->GetAttribute(app::R::attr::attr_one, &value, &flags)); + + // attr_six is now here because it was copied. + cookie = theme_one->GetAttribute(app::R::attr::attr_six, &value, &flags); + ASSERT_NE(kInvalidCookie, cookie); + EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType); + EXPECT_EQ(6u, value.data); + EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags); +} + +TEST_F(ThemeTest, FailToCopyThemeWithDifferentAssetManager) { + AssetManager2 assetmanager_one; + assetmanager_one.SetApkAssets({style_assets_.get()}); + + AssetManager2 assetmanager_two; + assetmanager_two.SetApkAssets({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(app::R::style::StyleTwo)); + + EXPECT_FALSE(theme_one->SetTo(*theme_two)); +} + +} // namespace android diff --git a/libs/androidfw/tests/data/.gitignore b/libs/androidfw/tests/data/.gitignore deleted file mode 100644 index c05cfb043024..000000000000 --- a/libs/androidfw/tests/data/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -*.apk -*.arsc diff --git a/libs/androidfw/tests/data/app/R.h b/libs/androidfw/tests/data/app/R.h index 23e68e3e80dc..5be2eee11bfa 100644 --- a/libs/androidfw/tests/data/app/R.h +++ b/libs/androidfw/tests/data/app/R.h @@ -14,25 +14,31 @@ * limitations under the License. */ -#ifndef __APP_R_H -#define __APP_R_H +#ifndef TEST_DATA_APP_R_H_ +#define TEST_DATA_APP_R_H_ +#include <cstdint> + +namespace com { +namespace android { namespace app { -namespace R { -namespace attr { - enum { - number = 0x7f010000, // default +struct R { + struct attr { + enum : uint32_t { + number = 0x7f010000, // default }; -} + }; -namespace style { - enum { - Theme_One = 0x7f020000, // default + struct style { + enum : uint32_t { + Theme_One = 0x7f020000, // default }; -} + }; +}; -} // namespace R -} // namespace app +} // namespace app +} // namespace android +} // namespace com -#endif // __APP_R_H +#endif // TEST_DATA_APP_R_H_ diff --git a/libs/androidfw/tests/data/app/app.apk b/libs/androidfw/tests/data/app/app.apk Binary files differnew file mode 100644 index 000000000000..ccb08242a656 --- /dev/null +++ b/libs/androidfw/tests/data/app/app.apk diff --git a/libs/androidfw/tests/data/app/app_arsc.h b/libs/androidfw/tests/data/app/app_arsc.h deleted file mode 100644 index d5d9a3b8be5b..000000000000 --- a/libs/androidfw/tests/data/app/app_arsc.h +++ /dev/null @@ -1,62 +0,0 @@ -unsigned char app_arsc[] = { - 0x02, 0x00, 0x0c, 0x00, 0xc4, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x1c, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x01, 0x9c, 0x02, 0x00, 0x00, - 0x7f, 0x00, 0x00, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x2e, 0x00, - 0x61, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x69, 0x00, - 0x64, 0x00, 0x2e, 0x00, 0x61, 0x00, 0x70, 0x00, 0x70, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x60, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x40, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x61, 0x00, 0x74, 0x00, 0x74, 0x00, - 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x73, 0x00, 0x74, 0x00, 0x79, 0x00, - 0x6c, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, - 0x4c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x06, 0x00, 0x6e, 0x00, - 0x75, 0x00, 0x6d, 0x00, 0x62, 0x00, 0x65, 0x00, 0x72, 0x00, 0x00, 0x00, - 0x09, 0x00, 0x54, 0x00, 0x68, 0x00, 0x65, 0x00, 0x6d, 0x00, 0x65, 0x00, - 0x2e, 0x00, 0x4f, 0x00, 0x6e, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00, - 0x64, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x48, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x10, 0x04, 0x00, 0x00, 0x00, - 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00, - 0x64, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x48, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x01, 0x7f, 0x08, 0x00, 0x00, 0x10, 0x01, 0x00, 0x00, 0x00 -}; -unsigned int app_arsc_len = 708; diff --git a/libs/androidfw/tests/data/app/build b/libs/androidfw/tests/data/app/build index 62257bc26d4b..d418158c547b 100755 --- a/libs/androidfw/tests/data/app/build +++ b/libs/androidfw/tests/data/app/build @@ -15,7 +15,6 @@ # limitations under the License. # -aapt package -v -I ../system/bundle.apk -M AndroidManifest.xml -S res -F bundle.apk -f && \ -unzip bundle.apk resources.arsc && \ -mv resources.arsc app.arsc && \ -xxd -i app.arsc > app_arsc.h +set -e + +aapt package -I ../system/system.apk -M AndroidManifest.xml -S res -F app.apk -f diff --git a/libs/androidfw/tests/data/appaslib/R.h b/libs/androidfw/tests/data/appaslib/R.h index 3af921a7ba65..5a2132733795 100644 --- a/libs/androidfw/tests/data/appaslib/R.h +++ b/libs/androidfw/tests/data/appaslib/R.h @@ -14,39 +14,53 @@ * limitations under the License. */ -#ifndef __APPASLIB_R_H -#define __APPASLIB_R_H +#ifndef DATA_APPASLIB_R_H_ +#define DATA_APPASLIB_R_H_ +#include <cstdint> + +namespace com { +namespace android { namespace appaslib { -namespace R { + namespace lib { -namespace integer { - enum { - number1 = 0x02020000, // default + +struct R { + struct integer { + enum : uint32_t { + number1 = 0x02020000, // default }; -} + }; -namespace array { - enum { - integerArray1 = 0x02030000, // default + struct array { + enum : uint32_t { + integerArray1 = 0x02030000, // default }; -} -} // namespace lib + }; +}; + +} // namespace lib namespace app { -namespace integer { - enum { - number1 = 0x7f020000, // default + +struct R { + struct integer { + enum : uint32_t { + number1 = 0x7f020000, // default }; -} + }; -namespace array { - enum { - integerArray1 = 0x7f030000, // default + struct array { + enum : uint32_t { + integerArray1 = 0x7f030000, // default }; -} -} // namespace app -} // namespace R -} // namespace appaslib + }; +}; + +} // namespace app + +} // namespace appaslib +} // namespace android +} // namespace com -#endif // __APPASLIB_R_H +#endif // DATA_APPASLIB_R_H_ diff --git a/libs/androidfw/tests/data/appaslib/appaslib.apk b/libs/androidfw/tests/data/appaslib/appaslib.apk Binary files differnew file mode 100644 index 000000000000..6ebd8237a2d8 --- /dev/null +++ b/libs/androidfw/tests/data/appaslib/appaslib.apk diff --git a/libs/androidfw/tests/data/appaslib/appaslib_arsc.h b/libs/androidfw/tests/data/appaslib/appaslib_arsc.h deleted file mode 100644 index be176ab5e63f..000000000000 --- a/libs/androidfw/tests/data/appaslib/appaslib_arsc.h +++ /dev/null @@ -1,68 +0,0 @@ -unsigned char appaslib_arsc[] = { - 0x02, 0x00, 0x0c, 0x00, 0x04, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x1c, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x01, 0xdc, 0x02, 0x00, 0x00, - 0x7f, 0x00, 0x00, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x2e, 0x00, - 0x61, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x69, 0x00, - 0x64, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00, - 0x2e, 0x00, 0x62, 0x00, 0x61, 0x00, 0x73, 0x00, 0x69, 0x00, 0x63, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x74, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x54, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x0c, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x04, 0x00, 0x61, 0x00, - 0x74, 0x00, 0x74, 0x00, 0x72, 0x00, 0x00, 0x00, 0x07, 0x00, 0x69, 0x00, - 0x6e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x67, 0x00, 0x65, 0x00, 0x72, 0x00, - 0x00, 0x00, 0x05, 0x00, 0x61, 0x00, 0x72, 0x00, 0x72, 0x00, 0x61, 0x00, - 0x79, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x54, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x12, 0x00, 0x00, 0x00, 0x07, 0x00, 0x6e, 0x00, 0x75, 0x00, 0x6d, 0x00, - 0x62, 0x00, 0x65, 0x00, 0x72, 0x00, 0x31, 0x00, 0x00, 0x00, 0x0d, 0x00, - 0x69, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x67, 0x00, 0x65, 0x00, - 0x72, 0x00, 0x41, 0x00, 0x72, 0x00, 0x72, 0x00, 0x61, 0x00, 0x79, 0x00, - 0x31, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, - 0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x48, 0x00, 0x5c, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, - 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x03, 0x7f, - 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x48, 0x00, - 0x80, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x4c, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x10, - 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x10, - 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x10, - 0x03, 0x00, 0x00, 0x00 -}; -unsigned int appaslib_arsc_len = 772; diff --git a/libs/androidfw/tests/data/appaslib/appaslib_lib.apk b/libs/androidfw/tests/data/appaslib/appaslib_lib.apk Binary files differnew file mode 100644 index 000000000000..ee1521cb4fdd --- /dev/null +++ b/libs/androidfw/tests/data/appaslib/appaslib_lib.apk diff --git a/libs/androidfw/tests/data/appaslib/appaslib_lib_arsc.h b/libs/androidfw/tests/data/appaslib/appaslib_lib_arsc.h deleted file mode 100644 index 099285a17aad..000000000000 --- a/libs/androidfw/tests/data/appaslib/appaslib_lib_arsc.h +++ /dev/null @@ -1,68 +0,0 @@ -unsigned char appaslib_lib_arsc[] = { - 0x02, 0x00, 0x0c, 0x00, 0x04, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x1c, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x01, 0xdc, 0x02, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x2e, 0x00, - 0x61, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x69, 0x00, - 0x64, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00, - 0x2e, 0x00, 0x62, 0x00, 0x61, 0x00, 0x73, 0x00, 0x69, 0x00, 0x63, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x74, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x54, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x0c, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x04, 0x00, 0x61, 0x00, - 0x74, 0x00, 0x74, 0x00, 0x72, 0x00, 0x00, 0x00, 0x07, 0x00, 0x69, 0x00, - 0x6e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x67, 0x00, 0x65, 0x00, 0x72, 0x00, - 0x00, 0x00, 0x05, 0x00, 0x61, 0x00, 0x72, 0x00, 0x72, 0x00, 0x61, 0x00, - 0x79, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x54, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x12, 0x00, 0x00, 0x00, 0x07, 0x00, 0x6e, 0x00, 0x75, 0x00, 0x6d, 0x00, - 0x62, 0x00, 0x65, 0x00, 0x72, 0x00, 0x31, 0x00, 0x00, 0x00, 0x0d, 0x00, - 0x69, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x67, 0x00, 0x65, 0x00, - 0x72, 0x00, 0x41, 0x00, 0x72, 0x00, 0x72, 0x00, 0x61, 0x00, 0x79, 0x00, - 0x31, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, - 0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x48, 0x00, 0x5c, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, - 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x07, 0x00, 0x00, 0x03, 0x00, - 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x48, 0x00, - 0x80, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x4c, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x10, - 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x10, - 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x10, - 0x03, 0x00, 0x00, 0x00 -}; -unsigned int appaslib_lib_arsc_len = 772; diff --git a/libs/androidfw/tests/data/appaslib/build b/libs/androidfw/tests/data/appaslib/build index e4bd88b4032c..baaf70075feb 100755 --- a/libs/androidfw/tests/data/appaslib/build +++ b/libs/androidfw/tests/data/appaslib/build @@ -15,14 +15,9 @@ # limitations under the License. # -PATH_TO_FRAMEWORK_RES=$(gettop)/prebuilts/sdk/current/android.jar +set -e -aapt package -M AndroidManifest.xml -S res -I $PATH_TO_FRAMEWORK_RES -F bundle.apk -f && \ -unzip bundle.apk resources.arsc && \ -mv resources.arsc appaslib.arsc && \ -xxd -i appaslib.arsc > appaslib_arsc.h && \ -aapt package -M AndroidManifest.xml -S res -I $PATH_TO_FRAMEWORK_RES -F bundle.apk -f --shared-lib && \ -unzip bundle.apk resources.arsc && \ -mv resources.arsc appaslib_lib.arsc && \ -xxd -i appaslib_lib.arsc > appaslib_lib_arsc.h \ +PATH_TO_FRAMEWORK_RES=$(gettop)/prebuilts/sdk/current/android.jar +aapt package -M AndroidManifest.xml -S res -I $PATH_TO_FRAMEWORK_RES -F appaslib.apk -f +aapt package -M AndroidManifest.xml -S res -I $PATH_TO_FRAMEWORK_RES -F appaslib_lib.apk -f --shared-lib diff --git a/libs/androidfw/tests/data/basic/AndroidManifest.xml b/libs/androidfw/tests/data/basic/AndroidManifest.xml index a56ac18e900b..b117882802d7 100644 --- a/libs/androidfw/tests/data/basic/AndroidManifest.xml +++ b/libs/androidfw/tests/data/basic/AndroidManifest.xml @@ -15,7 +15,6 @@ --> <manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.test.basic"> - <application> - </application> + package="com.android.basic"> + <application /> </manifest> diff --git a/libs/androidfw/tests/data/basic/R.h b/libs/androidfw/tests/data/basic/R.h index 6694dd0c36e1..9352b5c6629e 100644 --- a/libs/androidfw/tests/data/basic/R.h +++ b/libs/androidfw/tests/data/basic/R.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014 The Android Open Source Project + * 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. @@ -14,59 +14,67 @@ * limitations under the License. */ -#ifndef __BASE_R_H -#define __BASE_R_H +#ifndef TESTS_DATA_BASIC_R_H_ +#define TESTS_DATA_BASIC_R_H_ -namespace base { -namespace R { +#include <cstdint> -namespace attr { - enum { - attr1 = 0x7f010000, // default - attr2 = 0x7f010001, // default +namespace com { +namespace android { +namespace basic { + +struct R { + struct attr { + enum : uint32_t { + attr1 = 0x7f010000, + attr2 = 0x7f010001, }; -} + }; -namespace layout { - enum { - main = 0x7f020000, // default, fr-sw600dp-v13 + struct layout { + enum : uint32_t { + main = 0x7f020000, }; -} + }; -namespace string { - enum { - test1 = 0x7f030000, // default - test2 = 0x7f030001, // default - density = 0x7f030002, // default + struct string { + enum : uint32_t { + test1 = 0x7f030000, + test2 = 0x7f030001, + density = 0x7f030002, - test3 = 0x7f080000, // default (in feature) - test4 = 0x7f080001, // default (in feature) + // From feature + test3 = 0x7f080000, + test4 = 0x7f080001, }; -} + }; -namespace integer { - enum { - number1 = 0x7f040000, // default, sv, vs - number2 = 0x7f040001, // default + struct integer { + enum : uint32_t { + number1 = 0x7f040000, + number2 = 0x7f040001, - test3 = 0x7f090000, // default (in feature) + // From feature + number3 = 0x7f090000, }; -} + }; -namespace style { - enum { - Theme1 = 0x7f050000, // default - Theme2 = 0x7f050001, // default + struct style { + enum : uint32_t { + Theme1 = 0x7f050000, + Theme2 = 0x7f050001, }; -} + }; -namespace array { - enum { - integerArray1 = 0x7f060000, // default + struct array { + enum : uint32_t { + integerArray1 = 0x7f060000, }; -} + }; +}; -} // namespace R -} // namespace base +} // namespace basic +} // namespace android +} // namespace com -#endif // __BASE_R_H +#endif /* TESTS_DATA_BASIC_R_H_ */ diff --git a/libs/androidfw/tests/data/basic/basic.apk b/libs/androidfw/tests/data/basic/basic.apk Binary files differnew file mode 100644 index 000000000000..2c9771b18934 --- /dev/null +++ b/libs/androidfw/tests/data/basic/basic.apk diff --git a/libs/androidfw/tests/data/basic/basic_arsc.h b/libs/androidfw/tests/data/basic/basic_arsc.h deleted file mode 100644 index e497401bdddb..000000000000 --- a/libs/androidfw/tests/data/basic/basic_arsc.h +++ /dev/null @@ -1,175 +0,0 @@ -unsigned char basic_arsc[] = { - 0x02, 0x00, 0x0c, 0x00, 0x0c, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x1c, 0x00, 0xbc, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, - 0x72, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x13, 0x00, 0x72, 0x00, - 0x65, 0x00, 0x73, 0x00, 0x2f, 0x00, 0x6c, 0x00, 0x61, 0x00, 0x79, 0x00, - 0x6f, 0x00, 0x75, 0x00, 0x74, 0x00, 0x2f, 0x00, 0x6d, 0x00, 0x61, 0x00, - 0x69, 0x00, 0x6e, 0x00, 0x2e, 0x00, 0x78, 0x00, 0x6d, 0x00, 0x6c, 0x00, - 0x00, 0x00, 0x22, 0x00, 0x72, 0x00, 0x65, 0x00, 0x73, 0x00, 0x2f, 0x00, - 0x6c, 0x00, 0x61, 0x00, 0x79, 0x00, 0x6f, 0x00, 0x75, 0x00, 0x74, 0x00, - 0x2d, 0x00, 0x66, 0x00, 0x72, 0x00, 0x2d, 0x00, 0x73, 0x00, 0x77, 0x00, - 0x36, 0x00, 0x30, 0x00, 0x30, 0x00, 0x64, 0x00, 0x70, 0x00, 0x2d, 0x00, - 0x76, 0x00, 0x31, 0x00, 0x33, 0x00, 0x2f, 0x00, 0x6d, 0x00, 0x61, 0x00, - 0x69, 0x00, 0x6e, 0x00, 0x2e, 0x00, 0x78, 0x00, 0x6d, 0x00, 0x6c, 0x00, - 0x00, 0x00, 0x05, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00, - 0x31, 0x00, 0x00, 0x00, 0x05, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, - 0x74, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x01, - 0x44, 0x07, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x63, 0x00, 0x6f, 0x00, - 0x6d, 0x00, 0x2e, 0x00, 0x61, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x72, 0x00, - 0x6f, 0x00, 0x69, 0x00, 0x64, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x65, 0x00, - 0x73, 0x00, 0x74, 0x00, 0x2e, 0x00, 0x62, 0x00, 0x61, 0x00, 0x73, 0x00, - 0x69, 0x00, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x20, 0x01, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00, - 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, - 0x90, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, - 0x2c, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x61, 0x00, 0x74, 0x00, 0x74, 0x00, 0x72, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x6c, 0x00, 0x61, 0x00, 0x79, 0x00, 0x6f, 0x00, 0x75, 0x00, - 0x74, 0x00, 0x00, 0x00, 0x06, 0x00, 0x73, 0x00, 0x74, 0x00, 0x72, 0x00, - 0x69, 0x00, 0x6e, 0x00, 0x67, 0x00, 0x00, 0x00, 0x07, 0x00, 0x69, 0x00, - 0x6e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x67, 0x00, 0x65, 0x00, 0x72, 0x00, - 0x00, 0x00, 0x05, 0x00, 0x73, 0x00, 0x74, 0x00, 0x79, 0x00, 0x6c, 0x00, - 0x65, 0x00, 0x00, 0x00, 0x05, 0x00, 0x61, 0x00, 0x72, 0x00, 0x72, 0x00, - 0x61, 0x00, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, - 0xec, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, - 0x28, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, - 0x56, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, - 0x88, 0x00, 0x00, 0x00, 0x05, 0x00, 0x61, 0x00, 0x74, 0x00, 0x74, 0x00, - 0x72, 0x00, 0x31, 0x00, 0x00, 0x00, 0x05, 0x00, 0x61, 0x00, 0x74, 0x00, - 0x74, 0x00, 0x72, 0x00, 0x32, 0x00, 0x00, 0x00, 0x04, 0x00, 0x6d, 0x00, - 0x61, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x00, 0x00, 0x05, 0x00, 0x74, 0x00, - 0x65, 0x00, 0x73, 0x00, 0x74, 0x00, 0x31, 0x00, 0x00, 0x00, 0x05, 0x00, - 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00, 0x32, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x6e, 0x00, 0x75, 0x00, 0x6d, 0x00, 0x62, 0x00, 0x65, 0x00, - 0x72, 0x00, 0x31, 0x00, 0x00, 0x00, 0x07, 0x00, 0x6e, 0x00, 0x75, 0x00, - 0x6d, 0x00, 0x62, 0x00, 0x65, 0x00, 0x72, 0x00, 0x32, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x54, 0x00, 0x68, 0x00, 0x65, 0x00, 0x6d, 0x00, 0x65, 0x00, - 0x31, 0x00, 0x00, 0x00, 0x06, 0x00, 0x54, 0x00, 0x68, 0x00, 0x65, 0x00, - 0x6d, 0x00, 0x65, 0x00, 0x32, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x69, 0x00, - 0x6e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x67, 0x00, 0x65, 0x00, 0x72, 0x00, - 0x41, 0x00, 0x72, 0x00, 0x72, 0x00, 0x61, 0x00, 0x79, 0x00, 0x31, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x4c, 0x00, 0x8c, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, - 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x1c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x08, 0x00, 0x00, 0x10, 0x05, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x10, 0x05, 0x00, 0x00, 0x00, - 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x04, 0x24, 0x00, 0x00, 0x01, 0x02, 0x4c, 0x00, - 0x60, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x50, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x08, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x4c, 0x00, - 0x60, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x50, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x66, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0x02, - 0x00, 0x00, 0x00, 0x00, 0x4c, 0x61, 0x74, 0x6e, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x08, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, - 0x1c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x02, 0x4c, 0x00, 0x78, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, - 0xff, 0xff, 0xff, 0xff, 0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x08, 0x00, 0x00, 0x03, 0x02, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03, 0x03, 0x00, 0x00, 0x00, - 0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x02, 0x4c, 0x00, 0x74, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x10, - 0xc8, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x06, 0x7f, 0x01, 0x02, 0x4c, 0x00, - 0x64, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x54, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x76, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x08, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x10, 0x58, 0x02, 0x00, 0x00, - 0x01, 0x02, 0x4c, 0x00, 0x64, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x73, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c, 0x61, 0x74, 0x6e, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, - 0x08, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x10, - 0x90, 0x01, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x4c, 0x00, 0x98, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, - 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x28, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x7f, - 0x08, 0x00, 0x00, 0x10, 0x64, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x7f, - 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x04, 0x7f, 0x10, 0x00, 0x01, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x7f, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x01, 0x7f, 0x08, 0x00, 0x00, 0x10, 0x2c, 0x01, 0x00, 0x00, - 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x4c, 0x00, - 0x84, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x50, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00, 0x09, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, - 0x08, 0x00, 0x00, 0x10, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, - 0x08, 0x00, 0x00, 0x10, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, - 0x08, 0x00, 0x00, 0x10, 0x03, 0x00, 0x00, 0x00 -}; -unsigned int basic_arsc_len = 2060; diff --git a/libs/androidfw/tests/data/basic/basic_de_fr.apk b/libs/androidfw/tests/data/basic/basic_de_fr.apk Binary files differnew file mode 100644 index 000000000000..04814440e0f8 --- /dev/null +++ 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 differnew file mode 100644 index 000000000000..a8d06e7f3c19 --- /dev/null +++ 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 differnew file mode 100644 index 000000000000..d1dfb143f91b --- /dev/null +++ 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 differnew file mode 100644 index 000000000000..dca6f2fbc0ca --- /dev/null +++ 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 fd289fad62b1..af0fd8787f48 100755 --- a/libs/androidfw/tests/data/basic/build +++ b/libs/androidfw/tests/data/basic/build @@ -15,25 +15,8 @@ # limitations under the License. # -PATH_TO_FRAMEWORK_RES=$(gettop)/prebuilts/sdk/current/android.jar +set -e -aapt package -M AndroidManifest.xml -S res -I $PATH_TO_FRAMEWORK_RES --split hdpi --split xhdpi --split xxhdpi --split fr,de -F bundle.apk -f && \ -unzip bundle.apk resources.arsc && \ -mv resources.arsc basic.arsc && \ -xxd -i basic.arsc > basic_arsc.h && \ -\ -unzip bundle_de_fr.apk resources.arsc && \ -mv resources.arsc split_de_fr.arsc && \ -xxd -i split_de_fr.arsc > split_de_fr_arsc.h && \ -\ -unzip bundle_hdpi-v4.apk resources.arsc && \ -mv resources.arsc split_hdpi_v4.arsc && \ -xxd -i split_hdpi_v4.arsc > split_hdpi_v4_arsc.h && \ -\ -unzip bundle_xhdpi-v4.apk resources.arsc && \ -mv resources.arsc split_xhdpi_v4.arsc && \ -xxd -i split_xhdpi_v4.arsc > split_xhdpi_v4_arsc.h && \ -\ -unzip bundle_xxhdpi-v4.apk resources.arsc && \ -mv resources.arsc split_xxhdpi_v4.arsc && \ -xxd -i split_xxhdpi_v4.arsc > split_xxhdpi_v4_arsc.h \ +PATH_TO_FRAMEWORK_RES=${ANDROID_BUILD_TOP}/prebuilts/sdk/current/android.jar + +aapt package -M AndroidManifest.xml -S res -I $PATH_TO_FRAMEWORK_RES --split hdpi --split xhdpi --split xxhdpi --split fr,de -F basic.apk -f diff --git a/libs/androidfw/tests/data/basic/res/values/values.xml b/libs/androidfw/tests/data/basic/res/values/values.xml index a010cca182ee..11f6b8adbdba 100644 --- a/libs/androidfw/tests/data/basic/res/values/values.xml +++ b/libs/androidfw/tests/data/basic/res/values/values.xml @@ -15,24 +15,40 @@ --> <resources> + <public type="attr" name="attr1" id="0x7f010000" /> <attr name="attr1" format="reference|integer" /> + + <public type="attr" name="attr2" id="0x7f010001" /> <attr name="attr2" format="reference|integer" /> + <public type="layout" name="main" id="0x7f020000" /> + + <public type="string" name="test1" id="0x7f030000" /> <string name="test1">test1</string> + + <public type="string" name="test2" id="0x7f030001" /> <string name="test2">test2</string> + <public type="string" name="density" id="0x7f030002" /> + + <public type="integer" name="number1" id="0x7f040000" /> <integer name="number1">200</integer> + + <public type="integer" name="number2" id="0x7f040001" /> <integer name="number2">@array/integerArray1</integer> + <public type="style" name="Theme1" id="0x7f050000" /> <style name="Theme1"> - <item name="com.android.test.basic:attr1">100</item> - <item name="com.android.test.basic:attr2">@integer/number1</item> + <item name="com.android.basic:attr1">100</item> + <item name="com.android.basic:attr2">@integer/number1</item> </style> - <style name="Theme2" parent="@com.android.test.basic:style/Theme1"> - <item name="com.android.test.basic:attr1">300</item> + <public type="style" name="Theme2" id="0x7f050001" /> + <style name="Theme2" parent="@com.android.basic:style/Theme1"> + <item name="com.android.basic:attr1">300</item> </style> + <public type="array" name="integerArray1" id="0x7f060000" /> <integer-array name="integerArray1"> <item>1</item> <item>2</item> diff --git a/libs/androidfw/tests/data/basic/split_de_fr_arsc.h b/libs/androidfw/tests/data/basic/split_de_fr_arsc.h deleted file mode 100644 index a2aa598e5c90..000000000000 --- a/libs/androidfw/tests/data/basic/split_de_fr_arsc.h +++ /dev/null @@ -1,88 +0,0 @@ -unsigned char split_de_fr_arsc[] = { - 0x02, 0x00, 0x0c, 0x00, 0xf4, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x1c, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, - 0x2c, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x09, 0x00, 0x76, 0x00, - 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x75, 0x00, 0x63, 0x00, 0x68, 0x00, - 0x20, 0x00, 0x31, 0x00, 0x00, 0x00, 0x09, 0x00, 0x76, 0x00, 0x65, 0x00, - 0x72, 0x00, 0x73, 0x00, 0x75, 0x00, 0x63, 0x00, 0x68, 0x00, 0x20, 0x00, - 0x32, 0x00, 0x00, 0x00, 0x07, 0x00, 0x65, 0x00, 0x73, 0x00, 0x73, 0x00, - 0x61, 0x00, 0x69, 0x00, 0x20, 0x00, 0x31, 0x00, 0x00, 0x00, 0x07, 0x00, - 0x65, 0x00, 0x73, 0x00, 0x73, 0x00, 0x61, 0x00, 0x69, 0x00, 0x20, 0x00, - 0x32, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x01, 0x6c, 0x03, 0x00, 0x00, - 0x7f, 0x00, 0x00, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x2e, 0x00, - 0x61, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x69, 0x00, - 0x64, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00, - 0x2e, 0x00, 0x62, 0x00, 0x61, 0x00, 0x73, 0x00, 0x69, 0x00, 0x63, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x90, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x0c, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x61, 0x00, - 0x74, 0x00, 0x74, 0x00, 0x72, 0x00, 0x00, 0x00, 0x06, 0x00, 0x6c, 0x00, - 0x61, 0x00, 0x79, 0x00, 0x6f, 0x00, 0x75, 0x00, 0x74, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x73, 0x00, 0x74, 0x00, 0x72, 0x00, 0x69, 0x00, 0x6e, 0x00, - 0x67, 0x00, 0x00, 0x00, 0x07, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x74, 0x00, - 0x65, 0x00, 0x67, 0x00, 0x65, 0x00, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, - 0x73, 0x00, 0x74, 0x00, 0x79, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x61, 0x00, 0x72, 0x00, 0x72, 0x00, 0x61, 0x00, 0x79, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x40, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x0e, 0x00, 0x00, 0x00, 0x05, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, - 0x74, 0x00, 0x31, 0x00, 0x00, 0x00, 0x05, 0x00, 0x74, 0x00, 0x65, 0x00, - 0x73, 0x00, 0x74, 0x00, 0x32, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, - 0x18, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, - 0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x1c, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x4c, 0x00, - 0x78, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x58, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x64, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x4c, 0x61, 0x74, 0x6e, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, - 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03, - 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x08, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, 0x00, 0x01, 0x02, 0x4c, 0x00, - 0x78, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x58, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x66, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x4c, 0x61, 0x74, 0x6e, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, - 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03, - 0x02, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x08, 0x00, 0x00, 0x03, 0x03, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, - 0x18, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, - 0x18, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, - 0x14, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00 -}; -unsigned int split_de_fr_arsc_len = 1012; diff --git a/libs/androidfw/tests/data/basic/split_hdpi_v4_arsc.h b/libs/androidfw/tests/data/basic/split_hdpi_v4_arsc.h deleted file mode 100644 index 0cc39154a5ec..000000000000 --- a/libs/androidfw/tests/data/basic/split_hdpi_v4_arsc.h +++ /dev/null @@ -1,69 +0,0 @@ -unsigned char split_hdpi_v4_arsc[] = { - 0x02, 0x00, 0x0c, 0x00, 0x10, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x1c, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x68, 0x00, - 0x64, 0x00, 0x70, 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x01, - 0xd8, 0x02, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x63, 0x00, 0x6f, 0x00, - 0x6d, 0x00, 0x2e, 0x00, 0x61, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x72, 0x00, - 0x6f, 0x00, 0x69, 0x00, 0x64, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x65, 0x00, - 0x73, 0x00, 0x74, 0x00, 0x2e, 0x00, 0x62, 0x00, 0x61, 0x00, 0x73, 0x00, - 0x69, 0x00, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x20, 0x01, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, - 0x90, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, - 0x2c, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x61, 0x00, 0x74, 0x00, 0x74, 0x00, 0x72, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x6c, 0x00, 0x61, 0x00, 0x79, 0x00, 0x6f, 0x00, 0x75, 0x00, - 0x74, 0x00, 0x00, 0x00, 0x06, 0x00, 0x73, 0x00, 0x74, 0x00, 0x72, 0x00, - 0x69, 0x00, 0x6e, 0x00, 0x67, 0x00, 0x00, 0x00, 0x07, 0x00, 0x69, 0x00, - 0x6e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x67, 0x00, 0x65, 0x00, 0x72, 0x00, - 0x00, 0x00, 0x05, 0x00, 0x73, 0x00, 0x74, 0x00, 0x79, 0x00, 0x6c, 0x00, - 0x65, 0x00, 0x00, 0x00, 0x05, 0x00, 0x61, 0x00, 0x72, 0x00, 0x72, 0x00, - 0x61, 0x00, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, - 0x34, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x64, 0x00, 0x65, 0x00, 0x6e, 0x00, - 0x73, 0x00, 0x69, 0x00, 0x74, 0x00, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, - 0x1c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x02, 0x4c, 0x00, 0x68, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x08, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, - 0x18, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, - 0x18, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, - 0x14, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00 -}; -unsigned int split_hdpi_v4_arsc_len = 784; diff --git a/libs/androidfw/tests/data/basic/split_xhdpi_v4_arsc.h b/libs/androidfw/tests/data/basic/split_xhdpi_v4_arsc.h deleted file mode 100644 index d44ba9630aba..000000000000 --- a/libs/androidfw/tests/data/basic/split_xhdpi_v4_arsc.h +++ /dev/null @@ -1,69 +0,0 @@ -unsigned char split_xhdpi_v4_arsc[] = { - 0x02, 0x00, 0x0c, 0x00, 0x14, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x1c, 0x00, 0x30, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x78, 0x00, - 0x68, 0x00, 0x64, 0x00, 0x70, 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x02, 0x20, 0x01, 0xd8, 0x02, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, - 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x2e, 0x00, 0x61, 0x00, 0x6e, 0x00, - 0x64, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x69, 0x00, 0x64, 0x00, 0x2e, 0x00, - 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00, 0x2e, 0x00, 0x62, 0x00, - 0x61, 0x00, 0x73, 0x00, 0x69, 0x00, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0xb0, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x1c, 0x00, 0x90, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, - 0x1c, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, - 0x4c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x61, 0x00, 0x74, 0x00, 0x74, 0x00, - 0x72, 0x00, 0x00, 0x00, 0x06, 0x00, 0x6c, 0x00, 0x61, 0x00, 0x79, 0x00, - 0x6f, 0x00, 0x75, 0x00, 0x74, 0x00, 0x00, 0x00, 0x06, 0x00, 0x73, 0x00, - 0x74, 0x00, 0x72, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x67, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x67, 0x00, - 0x65, 0x00, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x73, 0x00, 0x74, 0x00, - 0x79, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x00, 0x00, 0x05, 0x00, 0x61, 0x00, - 0x72, 0x00, 0x72, 0x00, 0x61, 0x00, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x1c, 0x00, 0x34, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x64, 0x00, - 0x65, 0x00, 0x6e, 0x00, 0x73, 0x00, 0x69, 0x00, 0x74, 0x00, 0x79, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x02, 0x02, 0x10, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x4c, 0x00, 0x68, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, - 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x40, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, - 0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; -unsigned int split_xhdpi_v4_arsc_len = 788; diff --git a/libs/androidfw/tests/data/basic/split_xxhdpi_v4_arsc.h b/libs/androidfw/tests/data/basic/split_xxhdpi_v4_arsc.h deleted file mode 100644 index 2f3f682fb6e4..000000000000 --- a/libs/androidfw/tests/data/basic/split_xxhdpi_v4_arsc.h +++ /dev/null @@ -1,69 +0,0 @@ -unsigned char split_xxhdpi_v4_arsc[] = { - 0x02, 0x00, 0x0c, 0x00, 0x14, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x1c, 0x00, 0x30, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x78, 0x00, - 0x78, 0x00, 0x68, 0x00, 0x64, 0x00, 0x70, 0x00, 0x69, 0x00, 0x00, 0x00, - 0x00, 0x02, 0x20, 0x01, 0xd8, 0x02, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, - 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x2e, 0x00, 0x61, 0x00, 0x6e, 0x00, - 0x64, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x69, 0x00, 0x64, 0x00, 0x2e, 0x00, - 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00, 0x2e, 0x00, 0x62, 0x00, - 0x61, 0x00, 0x73, 0x00, 0x69, 0x00, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0xb0, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x1c, 0x00, 0x90, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, - 0x1c, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, - 0x4c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x61, 0x00, 0x74, 0x00, 0x74, 0x00, - 0x72, 0x00, 0x00, 0x00, 0x06, 0x00, 0x6c, 0x00, 0x61, 0x00, 0x79, 0x00, - 0x6f, 0x00, 0x75, 0x00, 0x74, 0x00, 0x00, 0x00, 0x06, 0x00, 0x73, 0x00, - 0x74, 0x00, 0x72, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x67, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x67, 0x00, - 0x65, 0x00, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x73, 0x00, 0x74, 0x00, - 0x79, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x00, 0x00, 0x05, 0x00, 0x61, 0x00, - 0x72, 0x00, 0x72, 0x00, 0x61, 0x00, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x1c, 0x00, 0x34, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x64, 0x00, - 0x65, 0x00, 0x6e, 0x00, 0x73, 0x00, 0x69, 0x00, 0x74, 0x00, 0x79, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x02, 0x02, 0x10, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x4c, 0x00, 0x68, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, - 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0xe0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, - 0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; -unsigned int split_xxhdpi_v4_arsc_len = 788; diff --git a/libs/androidfw/tests/data/feature/AndroidManifest.xml b/libs/androidfw/tests/data/feature/AndroidManifest.xml index c2343b75e16e..c972372508be 100644 --- a/libs/androidfw/tests/data/feature/AndroidManifest.xml +++ b/libs/androidfw/tests/data/feature/AndroidManifest.xml @@ -15,5 +15,5 @@ --> <manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.test.basic"> + package="com.android.basic"> </manifest> diff --git a/libs/androidfw/tests/data/feature/build b/libs/androidfw/tests/data/feature/build index 0f3307f518ec..6ed3e416fb10 100755 --- a/libs/androidfw/tests/data/feature/build +++ b/libs/androidfw/tests/data/feature/build @@ -15,7 +15,8 @@ # limitations under the License. # -aapt package -M AndroidManifest.xml -S res --feature-of ../basic/bundle.apk -F bundle.apk -f && \ -unzip bundle.apk resources.arsc && \ -mv resources.arsc feature.arsc && \ -xxd -i feature.arsc > feature_arsc.h +set -e + +PATH_TO_FRAMEWORK_RES=${ANDROID_BUILD_TOP}/prebuilts/sdk/current/android.jar + +aapt package -M AndroidManifest.xml -S res -I $PATH_TO_FRAMEWORK_RES --feature-of ../basic/basic.apk -F feature.apk -f diff --git a/libs/androidfw/tests/data/feature/feature.apk b/libs/androidfw/tests/data/feature/feature.apk Binary files differnew file mode 100644 index 000000000000..04940fb9bce2 --- /dev/null +++ b/libs/androidfw/tests/data/feature/feature.apk diff --git a/libs/androidfw/tests/data/feature/feature_arsc.h b/libs/androidfw/tests/data/feature/feature_arsc.h deleted file mode 100644 index cd299102e8f6..000000000000 --- a/libs/androidfw/tests/data/feature/feature_arsc.h +++ /dev/null @@ -1,73 +0,0 @@ -unsigned char feature_arsc[] = { - 0x02, 0x00, 0x0c, 0x00, 0x44, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x1c, 0x00, 0x40, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00, 0x33, 0x00, - 0x00, 0x00, 0x05, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00, - 0x34, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x01, 0xf8, 0x02, 0x00, 0x00, - 0x7f, 0x00, 0x00, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x2e, 0x00, - 0x61, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x69, 0x00, - 0x64, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00, - 0x2e, 0x00, 0x62, 0x00, 0x61, 0x00, 0x73, 0x00, 0x69, 0x00, 0x63, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, - 0x09, 0x00, 0x00, 0x00, 0xa0, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x80, 0x00, 0x00, 0x00, - 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, - 0x1e, 0x00, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x00, 0x07, 0x00, 0x3c, 0x00, - 0x65, 0x00, 0x6d, 0x00, 0x70, 0x00, 0x74, 0x00, 0x79, 0x00, 0x3e, 0x00, - 0x00, 0x00, 0x04, 0x00, 0x61, 0x00, 0x74, 0x00, 0x74, 0x00, 0x72, 0x00, - 0x00, 0x00, 0x06, 0x00, 0x73, 0x00, 0x74, 0x00, 0x72, 0x00, 0x69, 0x00, - 0x6e, 0x00, 0x67, 0x00, 0x00, 0x00, 0x07, 0x00, 0x69, 0x00, 0x6e, 0x00, - 0x74, 0x00, 0x65, 0x00, 0x67, 0x00, 0x65, 0x00, 0x72, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x1c, 0x00, 0x58, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, - 0x1c, 0x00, 0x00, 0x00, 0x05, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, - 0x74, 0x00, 0x33, 0x00, 0x00, 0x00, 0x05, 0x00, 0x74, 0x00, 0x65, 0x00, - 0x73, 0x00, 0x74, 0x00, 0x34, 0x00, 0x00, 0x00, 0x07, 0x00, 0x6e, 0x00, - 0x75, 0x00, 0x6d, 0x00, 0x62, 0x00, 0x65, 0x00, 0x72, 0x00, 0x33, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, - 0x18, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00, - 0x6c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x4c, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03, - 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x08, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, - 0x14, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00, 0x58, 0x00, 0x00, 0x00, - 0x09, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, - 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x08, 0x00, 0x00, 0x10, 0xc8, 0x00, 0x00, 0x00 -}; -unsigned int feature_arsc_len = 836; diff --git a/libs/androidfw/tests/data/feature/res/values/values.xml b/libs/androidfw/tests/data/feature/res/values/values.xml index 343fd6c8389f..59f7d93ee389 100644 --- a/libs/androidfw/tests/data/feature/res/values/values.xml +++ b/libs/androidfw/tests/data/feature/res/values/values.xml @@ -15,8 +15,13 @@ --> <resources> + <!-- Features are offset, so 7f020000 will become 7f080000 at runtime. --> + <public type="string" name="test3" id="0x7f020000" /> <string name="test3">test3</string> + + <public type="string" name="test4" id="0x7f020001" /> <string name="test4">test4</string> + <public type="integer" name="number3" id="0x7f030000" /> <integer name="number3">200</integer> </resources> diff --git a/libs/androidfw/tests/data/lib/AndroidManifest.xml b/libs/androidfw/tests/data/lib/AndroidManifest.xml index a56ac18e900b..02f5d3efabea 100644 --- a/libs/androidfw/tests/data/lib/AndroidManifest.xml +++ b/libs/androidfw/tests/data/lib/AndroidManifest.xml @@ -15,7 +15,6 @@ --> <manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.test.basic"> - <application> - </application> + package="com.android.lib"> + <application /> </manifest> diff --git a/libs/androidfw/tests/data/lib/R.h b/libs/androidfw/tests/data/lib/R.h index 6013973072cc..bb22d22f90e1 100644 --- a/libs/androidfw/tests/data/lib/R.h +++ b/libs/androidfw/tests/data/lib/R.h @@ -14,26 +14,32 @@ * limitations under the License. */ -#ifndef __LIB_R_H -#define __LIB_R_H +#ifndef TEST_DATA_LIB_R_H_ +#define TEST_DATA_LIB_R_H_ +#include <cstdint> + +namespace com { +namespace android { namespace lib { -namespace R { -namespace attr { - enum { - attr1 = 0x02010000, // default - attr2 = 0x02010001, // default +struct R { + struct attr { + enum : uint32_t { + attr1 = 0x02010000, // default + attr2 = 0x02010001, // default }; -} + }; -namespace style { - enum { - Theme = 0x02020000, // default + struct style { + enum : uint32_t { + Theme = 0x02020000, // default }; -} + }; +}; -} // namespace R -} // namespace lib +} // namespace lib +} // namespace android +} // namespace com -#endif // __LIB_R_H +#endif // TEST_DATA_R_H_ diff --git a/libs/androidfw/tests/data/lib/build b/libs/androidfw/tests/data/lib/build index 41029031028c..5c3d02c850bf 100755 --- a/libs/androidfw/tests/data/lib/build +++ b/libs/androidfw/tests/data/lib/build @@ -15,7 +15,6 @@ # limitations under the License. # -aapt package -M AndroidManifest.xml -S res -F bundle.apk -f --shared-lib && \ -unzip bundle.apk resources.arsc && \ -mv resources.arsc lib.arsc && \ -xxd -i lib.arsc > lib_arsc.h +set -e + +aapt package -M AndroidManifest.xml -S res -F lib.apk -f --shared-lib diff --git a/libs/androidfw/tests/data/lib/lib.apk b/libs/androidfw/tests/data/lib/lib.apk Binary files differnew file mode 100644 index 000000000000..44c27c79ae7c --- /dev/null +++ b/libs/androidfw/tests/data/lib/lib.apk diff --git a/libs/androidfw/tests/data/lib/lib_arsc.h b/libs/androidfw/tests/data/lib/lib_arsc.h deleted file mode 100644 index 62bed655f914..000000000000 --- a/libs/androidfw/tests/data/lib/lib_arsc.h +++ /dev/null @@ -1,68 +0,0 @@ -unsigned char lib_arsc[] = { - 0x02, 0x00, 0x0c, 0x00, 0x0c, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x1c, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x01, 0xe4, 0x02, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x2e, 0x00, - 0x61, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x69, 0x00, - 0x64, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00, - 0x2e, 0x00, 0x62, 0x00, 0x61, 0x00, 0x73, 0x00, 0x69, 0x00, 0x63, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x60, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x40, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x61, 0x00, 0x74, 0x00, 0x74, 0x00, - 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x73, 0x00, 0x74, 0x00, 0x79, 0x00, - 0x6c, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, - 0x54, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x61, 0x00, 0x74, 0x00, 0x74, 0x00, 0x72, 0x00, 0x31, 0x00, - 0x00, 0x00, 0x05, 0x00, 0x61, 0x00, 0x74, 0x00, 0x74, 0x00, 0x72, 0x00, - 0x32, 0x00, 0x00, 0x00, 0x05, 0x00, 0x54, 0x00, 0x68, 0x00, 0x65, 0x00, - 0x6d, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, - 0x18, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x4c, 0x00, - 0x8c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x54, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x10, 0x04, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x10, - 0x04, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x02, 0x4c, 0x00, 0x78, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x01, 0x00, 0x08, 0x00, 0x00, 0x10, 0xbc, 0x02, 0x00, 0x00, - 0x01, 0x00, 0x01, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x01, 0x00 -}; -unsigned int lib_arsc_len = 780; diff --git a/libs/androidfw/tests/data/lib/res/values/values.xml b/libs/androidfw/tests/data/lib/res/values/values.xml index ec8117a78c56..51e3a407c538 100644 --- a/libs/androidfw/tests/data/lib/res/values/values.xml +++ b/libs/androidfw/tests/data/lib/res/values/values.xml @@ -19,7 +19,7 @@ <attr name="attr2" format="integer" /> <style name="Theme"> - <item name="com.android.test.basic:attr1">700</item> - <item name="com.android.test.basic:attr2">?com.android.test.basic:attr1</item> + <item name="com.android.lib:attr1">700</item> + <item name="com.android.lib:attr2">?com.android.lib:attr1</item> </style> </resources> diff --git a/libs/androidfw/tests/data/overlay/build b/libs/androidfw/tests/data/overlay/build index f73767749274..112f373ead30 100755 --- a/libs/androidfw/tests/data/overlay/build +++ b/libs/androidfw/tests/data/overlay/build @@ -15,7 +15,6 @@ # limitations under the License. # -aapt package -M AndroidManifest.xml -S res -F bundle.apk -f && \ -unzip bundle.apk resources.arsc && \ -mv resources.arsc overlay.arsc && \ -xxd -i overlay.arsc > overlay_arsc.h +set -e + +aapt package -M AndroidManifest.xml -S res -F overlay.apk -f diff --git a/libs/androidfw/tests/data/overlay/overlay.apk b/libs/androidfw/tests/data/overlay/overlay.apk Binary files differnew file mode 100644 index 000000000000..e0e054343601 --- /dev/null +++ b/libs/androidfw/tests/data/overlay/overlay.apk diff --git a/libs/androidfw/tests/data/overlay/overlay_arsc.h b/libs/androidfw/tests/data/overlay/overlay_arsc.h deleted file mode 100644 index 5bd98b28409d..000000000000 --- a/libs/androidfw/tests/data/overlay/overlay_arsc.h +++ /dev/null @@ -1,69 +0,0 @@ -unsigned char overlay_arsc[] = { - 0x02, 0x00, 0x0c, 0x00, 0x10, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x1c, 0x00, 0x40, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x74, 0x00, - 0x65, 0x00, 0x73, 0x00, 0x74, 0x00, 0x32, 0x00, 0x2d, 0x00, 0x6f, 0x00, - 0x76, 0x00, 0x65, 0x00, 0x72, 0x00, 0x6c, 0x00, 0x61, 0x00, 0x79, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x01, 0xc4, 0x02, 0x00, 0x00, - 0x7f, 0x00, 0x00, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x2e, 0x00, - 0x61, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x69, 0x00, - 0x64, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00, - 0x2e, 0x00, 0x62, 0x00, 0x61, 0x00, 0x73, 0x00, 0x69, 0x00, 0x63, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x74, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x54, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x0c, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x61, 0x00, - 0x74, 0x00, 0x74, 0x00, 0x72, 0x00, 0x00, 0x00, 0x06, 0x00, 0x73, 0x00, - 0x74, 0x00, 0x72, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x67, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x61, 0x00, 0x72, 0x00, 0x72, 0x00, 0x61, 0x00, 0x79, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x50, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x0e, 0x00, 0x00, 0x00, 0x05, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, - 0x74, 0x00, 0x32, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x69, 0x00, 0x6e, 0x00, - 0x74, 0x00, 0x65, 0x00, 0x67, 0x00, 0x65, 0x00, 0x72, 0x00, 0x41, 0x00, - 0x72, 0x00, 0x72, 0x00, 0x61, 0x00, 0x79, 0x00, 0x31, 0x00, 0x00, 0x00, - 0x02, 0x02, 0x10, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x02, 0x44, 0x00, 0x58, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03, - 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x02, 0x44, 0x00, 0x70, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x10, - 0x0a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x10, - 0x0b, 0x00, 0x00, 0x00 -}; -unsigned int overlay_arsc_len = 784; diff --git a/libs/androidfw/tests/data/styles/AndroidManifest.xml b/libs/androidfw/tests/data/styles/AndroidManifest.xml new file mode 100644 index 000000000000..521131659cfe --- /dev/null +++ b/libs/androidfw/tests/data/styles/AndroidManifest.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.app"> +</manifest> diff --git a/libs/androidfw/tests/data/styles/R.h b/libs/androidfw/tests/data/styles/R.h new file mode 100644 index 000000000000..68527c744b37 --- /dev/null +++ b/libs/androidfw/tests/data/styles/R.h @@ -0,0 +1,58 @@ +/* + * 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. + */ + +#ifndef TEST_DATA_STYLES_R_H_ +#define TEST_DATA_STYLES_R_H_ + +#include <cstdint> + +namespace com { +namespace android { +namespace app { + +struct R { + struct attr { + enum : uint32_t { + attr_one = 0x7f010000u, + attr_two = 0x7f010001u, + attr_three = 0x7f010002u, + attr_four = 0x7f010003u, + attr_five = 0x7f010004u, + attr_indirect = 0x7f010005u, + attr_six = 0x7f010006u, + }; + }; + + struct string { + enum : uint32_t { + string_one = 0x7f030000u, + }; + }; + + struct style { + enum : uint32_t { + StyleOne = 0x7f020000u, + StyleTwo = 0x7f020001u, + StyleThree = 0x7f020002u, + }; + }; +}; + +} // namespace app +} // namespace android +} // namespace com + +#endif // TEST_DATA_STYLES_R_H_ diff --git a/libs/androidfw/tests/data/styles/build b/libs/androidfw/tests/data/styles/build new file mode 100755 index 000000000000..81f78b1b7b7f --- /dev/null +++ b/libs/androidfw/tests/data/styles/build @@ -0,0 +1,5 @@ +#!/bin/bash + +set -e + +aapt package -F styles.apk -M AndroidManifest.xml -S res -f diff --git a/libs/androidfw/tests/data/styles/res/layout/layout.xml b/libs/androidfw/tests/data/styles/res/layout/layout.xml new file mode 100644 index 000000000000..f3aa0f83d9a3 --- /dev/null +++ b/libs/androidfw/tests/data/styles/res/layout/layout.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<View xmlns:app="http://schemas.android.com/apk/res-auto" + app:attr_four="?attr/attr_indirect" + app:attr_three="10" /> + diff --git a/libs/androidfw/tests/data/styles/res/values/styles.xml b/libs/androidfw/tests/data/styles/res/values/styles.xml new file mode 100644 index 000000000000..da592f806d21 --- /dev/null +++ b/libs/androidfw/tests/data/styles/res/values/styles.xml @@ -0,0 +1,62 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<resources> + <public type="attr" name="attr_one" id="0x7f010000" /> + <attr name="attr_one" /> + + <public type="attr" name="attr_two" id="0x7f010001" /> + <attr name="attr_two" /> + + <public type="attr" name="attr_three" id="0x7f010002" /> + <attr name="attr_three" /> + + <public type="attr" name="attr_four" id="0x7f010003" /> + <attr name="attr_four" /> + + <public type="attr" name="attr_five" id="0x7f010004" /> + <attr name="attr_five" /> + + <public type="attr" name="attr_indirect" id="0x7f010005" /> + <attr name="attr_indirect" /> + + <public type="string" name="string_one" id="0x7f030000" /> + <string name="string_one">Hi</string> + + <public type="style" name="StyleOne" id="0x7f020000" /> + <style name="StyleOne"> + <item name="attr_one">1</item> + <item name="attr_two">2</item> + </style> + + <public type="style" name="StyleTwo" id="0x7f020001" /> + <style name="StyleTwo" parent="@style/StyleOne"> + <item name="attr_indirect">3</item> + <item name="attr_two">"string"</item> + <item name="attr_three">?attr/attr_indirect</item> + <item name="attr_five">@string/string_one</item> + </style> + + <public type="attr" name="attr_six" id="0x7f010006" /> + <attr name="attr_six" /> + + <public type="style" name="StyleThree" id="0x7f020002" /> + <style name="StyleThree"> + <item name="attr_six">6</item> + <item name="attr_five">5</item> + </style> + +</resources> diff --git a/libs/androidfw/tests/data/styles/styles.apk b/libs/androidfw/tests/data/styles/styles.apk Binary files differnew file mode 100644 index 000000000000..d4ccb8391202 --- /dev/null +++ b/libs/androidfw/tests/data/styles/styles.apk diff --git a/libs/androidfw/tests/data/system/R.h b/libs/androidfw/tests/data/system/R.h index 6a31fb8ff088..becb38830fb3 100644 --- a/libs/androidfw/tests/data/system/R.h +++ b/libs/androidfw/tests/data/system/R.h @@ -14,32 +14,34 @@ * limitations under the License. */ -#ifndef __ANDROID_R_H -#define __ANDROID_R_H +#ifndef TEST_DATA_SYSTEM_R_H_ +#define TEST_DATA_SYSTEM_R_H_ + +#include <cstdint> namespace android { -namespace R { -namespace attr { - enum { - background = 0x01010000, // default - foreground = 0x01010001, // default +struct R { + struct attr { + enum : uint32_t { + background = 0x01010000, // default + foreground = 0x01010001, // default }; -} + }; -namespace style { - enum { - Theme_One = 0x01020000, // default + struct style { + enum : uint32_t { + Theme_One = 0x01020000, // default }; -} + }; -namespace integer { - enum { - number = 0x01030000, // sv + struct integer { + enum : uint32_t { + number = 0x01030000, // sv }; -} + }; +}; -} // namespace R -} // namespace android +} // namespace android -#endif // __ANDROID_R_H +#endif // TEST_DATA_SYSTEM_R_H_ diff --git a/libs/androidfw/tests/data/system/build b/libs/androidfw/tests/data/system/build index 1a70e84114b0..bfbdf4ca770b 100755 --- a/libs/androidfw/tests/data/system/build +++ b/libs/androidfw/tests/data/system/build @@ -15,7 +15,6 @@ # limitations under the License. # -aapt package -x -M AndroidManifest.xml -S res -F bundle.apk -f && \ -unzip bundle.apk resources.arsc && \ -mv resources.arsc system.arsc && \ -xxd -i system.arsc > system_arsc.h +set -e + +aapt package -x -M AndroidManifest.xml -S res -F system.apk -f diff --git a/libs/androidfw/tests/data/system/system.apk b/libs/androidfw/tests/data/system/system.apk Binary files differnew file mode 100644 index 000000000000..1299016a0f83 --- /dev/null +++ b/libs/androidfw/tests/data/system/system.apk diff --git a/libs/androidfw/tests/data/system/system_arsc.h b/libs/androidfw/tests/data/system/system_arsc.h deleted file mode 100644 index b0dab6b357e9..000000000000 --- a/libs/androidfw/tests/data/system/system_arsc.h +++ /dev/null @@ -1,88 +0,0 @@ -unsigned char system_arsc[] = { - 0x02, 0x00, 0x0c, 0x00, 0xf8, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x1c, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x01, 0xd0, 0x03, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x61, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x72, 0x00, - 0x6f, 0x00, 0x69, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x98, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x78, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x0c, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x61, 0x00, 0x74, 0x00, 0x74, 0x00, 0x72, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x73, 0x00, 0x74, 0x00, 0x79, 0x00, 0x6c, 0x00, 0x65, 0x00, - 0x00, 0x00, 0x07, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x65, 0x00, - 0x67, 0x00, 0x65, 0x00, 0x72, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x5e, 0x00, - 0x61, 0x00, 0x74, 0x00, 0x74, 0x00, 0x72, 0x00, 0x2d, 0x00, 0x70, 0x00, - 0x72, 0x00, 0x69, 0x00, 0x76, 0x00, 0x61, 0x00, 0x74, 0x00, 0x65, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x84, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, - 0x0a, 0x00, 0x62, 0x00, 0x61, 0x00, 0x63, 0x00, 0x6b, 0x00, 0x67, 0x00, - 0x72, 0x00, 0x6f, 0x00, 0x75, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x00, 0x00, - 0x0a, 0x00, 0x66, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x65, 0x00, 0x67, 0x00, - 0x72, 0x00, 0x6f, 0x00, 0x75, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x00, 0x00, - 0x09, 0x00, 0x54, 0x00, 0x68, 0x00, 0x65, 0x00, 0x6d, 0x00, 0x65, 0x00, - 0x2e, 0x00, 0x4f, 0x00, 0x6e, 0x00, 0x65, 0x00, 0x00, 0x00, 0x06, 0x00, - 0x6e, 0x00, 0x75, 0x00, 0x6d, 0x00, 0x62, 0x00, 0x65, 0x00, 0x72, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, - 0x00, 0x00, 0x00, 0x40, 0x01, 0x02, 0x4c, 0x00, 0x8c, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, - 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x1c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x08, 0x00, 0x00, 0x10, 0x11, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x10, 0x11, 0x00, 0x00, 0x00, - 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x01, 0x02, 0x4c, 0x00, - 0x78, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x50, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, - 0x08, 0x00, 0x00, 0x1d, 0x00, 0x00, 0xff, 0xff, 0x01, 0x00, 0x01, 0x01, - 0x08, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, 0xff, 0x02, 0x02, 0x10, 0x00, - 0x14, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x40, 0x01, 0x02, 0x4c, 0x00, 0x60, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, - 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x73, 0x76, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x08, 0x00, 0x02, 0x00, 0x03, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x10, - 0x01, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; -unsigned int system_arsc_len = 1016; diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk index a7cbf5e562d1..8f7787bd5d15 100644 --- a/libs/hwui/Android.mk +++ b/libs/hwui/Android.mk @@ -2,7 +2,6 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk -HWUI_NEW_OPS := true BUGREPORT_FONT_CACHE_USAGE := false # Enables fine-grained GLES error checking @@ -11,6 +10,7 @@ BUGREPORT_FONT_CACHE_USAGE := false HWUI_ENABLE_OPENGL_VALIDATION := false hwui_src_files := \ + hwui/Bitmap.cpp \ font/CacheTexture.cpp \ font/Font.cpp \ hwui/Canvas.cpp \ @@ -18,6 +18,17 @@ hwui_src_files := \ hwui/MinikinUtils.cpp \ hwui/PaintImpl.cpp \ hwui/Typeface.cpp \ + pipeline/skia/GLFunctorDrawable.cpp \ + pipeline/skia/LayerDrawable.cpp \ + pipeline/skia/RenderNodeDrawable.cpp \ + pipeline/skia/ReorderBarrierDrawables.cpp \ + pipeline/skia/SkiaDisplayList.cpp \ + pipeline/skia/SkiaOpenGLPipeline.cpp \ + pipeline/skia/SkiaOpenGLReadback.cpp \ + pipeline/skia/SkiaPipeline.cpp \ + pipeline/skia/SkiaProfileRenderer.cpp \ + pipeline/skia/SkiaRecordingCanvas.cpp \ + pipeline/skia/SkiaVulkanPipeline.cpp \ renderstate/Blend.cpp \ renderstate/MeshState.cpp \ renderstate/OffscreenBufferPool.cpp \ @@ -27,17 +38,19 @@ hwui_src_files := \ renderstate/Stencil.cpp \ renderstate/TextureState.cpp \ renderthread/CanvasContext.cpp \ + renderthread/OpenGLPipeline.cpp \ renderthread/DrawFrameTask.cpp \ renderthread/EglManager.cpp \ + renderthread/VulkanManager.cpp \ renderthread/RenderProxy.cpp \ renderthread/RenderTask.cpp \ renderthread/RenderThread.cpp \ renderthread/TimeLord.cpp \ + renderthread/Frame.cpp \ thread/TaskManager.cpp \ utils/Blur.cpp \ utils/GLUtils.cpp \ utils/LinearAllocator.cpp \ - utils/NinePatchImpl.cpp \ utils/StringUtils.cpp \ utils/TestWindowContext.cpp \ utils/VectorDrawableUtils.cpp \ @@ -45,23 +58,24 @@ hwui_src_files := \ AnimationContext.cpp \ Animator.cpp \ AnimatorManager.cpp \ - AssetAtlas.cpp \ + BakedOpDispatcher.cpp \ + BakedOpRenderer.cpp \ + BakedOpState.cpp \ Caches.cpp \ CanvasState.cpp \ ClipArea.cpp \ DamageAccumulator.cpp \ - DeferredDisplayList.cpp \ DeferredLayerUpdater.cpp \ DeviceInfo.cpp \ DisplayList.cpp \ - DisplayListCanvas.cpp \ - Dither.cpp \ Extensions.cpp \ FboCache.cpp \ FontRenderer.cpp \ + FrameBuilder.cpp \ FrameInfo.cpp \ FrameInfoVisualizer.cpp \ GammaFontRenderer.cpp \ + GlLayer.cpp \ GlopBuilder.cpp \ GpuMemoryTracker.cpp \ GradientCache.cpp \ @@ -69,23 +83,24 @@ hwui_src_files := \ Interpolator.cpp \ JankTracker.cpp \ Layer.cpp \ - LayerCache.cpp \ - LayerRenderer.cpp \ + LayerBuilder.cpp \ LayerUpdateQueue.cpp \ Matrix.cpp \ - OpenGLRenderer.cpp \ + OpDumper.cpp \ + OpenGLReadback.cpp \ Patch.cpp \ PatchCache.cpp \ PathCache.cpp \ - PathTessellator.cpp \ PathParser.cpp \ + PathTessellator.cpp \ PixelBuffer.cpp \ + ProfileRenderer.cpp \ Program.cpp \ ProgramCache.cpp \ Properties.cpp \ - PropertyValuesHolder.cpp \ PropertyValuesAnimatorSet.cpp \ - Readback.cpp \ + PropertyValuesHolder.cpp \ + RecordingCanvas.cpp \ RenderBufferCache.cpp \ RenderNode.cpp \ RenderProperties.cpp \ @@ -101,14 +116,25 @@ hwui_src_files := \ Texture.cpp \ TextureCache.cpp \ VectorDrawable.cpp \ + VkLayer.cpp \ protos/hwui.proto hwui_test_common_src_files := \ $(call all-cpp-files-under, tests/common/scenes) \ + tests/common/LeakChecker.cpp \ + tests/common/TestListViewSceneBase.cpp \ tests/common/TestContext.cpp \ tests/common/TestScene.cpp \ tests/common/TestUtils.cpp +hwui_debug_common_src_files := \ + debug/wrap_gles.cpp \ + debug/DefaultGlesDriver.cpp \ + debug/GlesErrorCheckWrapper.cpp \ + debug/GlesDriver.cpp \ + debug/FatalBaseDriver.cpp \ + debug/NullGlesDriver.cpp + hwui_cflags := \ -DEGL_EGLEXT_PROTOTYPES -DGL_GLEXT_PROTOTYPES \ -DATRACE_TAG=ATRACE_TAG_VIEW -DLOG_TAG=\"OpenGLRenderer\" \ @@ -118,23 +144,20 @@ ifeq ($(TARGET_USES_HWC2),true) hwui_cflags += -DUSE_HWC2 endif +# TODO: Linear blending should be enabled by default, but we are +# TODO: making it an opt-in while it's a work in progress +# TODO: The final test should be: +# TODO: ifneq ($(TARGET_ENABLE_LINEAR_BLENDING),false) +ifeq ($(TARGET_ENABLE_LINEAR_BLENDING),true) + hwui_cflags += -DANDROID_ENABLE_LINEAR_BLENDING +endif + # GCC false-positives on this warning, and since we -Werror that's # a problem hwui_cflags += -Wno-free-nonheap-object -ifeq (true, $(HWUI_NEW_OPS)) - hwui_src_files += \ - BakedOpDispatcher.cpp \ - BakedOpRenderer.cpp \ - BakedOpState.cpp \ - FrameBuilder.cpp \ - LayerBuilder.cpp \ - OpDumper.cpp \ - RecordingCanvas.cpp - - hwui_cflags += -DHWUI_NEW_OPS - -endif +# clang's warning is broken, see: https://llvm.org/bugs/show_bug.cgi?id=21629 +hwui_cflags += -Wno-missing-braces ifeq (true, $(BUGREPORT_FONT_CACHE_USAGE)) hwui_src_files += \ @@ -142,7 +165,6 @@ ifeq (true, $(BUGREPORT_FONT_CACHE_USAGE)) hwui_cflags += -DBUGREPORT_FONT_CACHE_USAGE endif - ifndef HWUI_COMPILE_SYMBOLS hwui_cflags += -fvisibility=hidden endif @@ -161,6 +183,9 @@ endef hwui_c_includes += \ external/skia/include/private \ external/skia/src/core \ + external/skia/src/effects \ + external/skia/src/image \ + external/skia/src/utils \ external/harfbuzz_ng/src \ external/freetype/include @@ -172,14 +197,6 @@ ifneq (false,$(ANDROID_ENABLE_RENDERSCRIPT)) frameworks/rs endif -ifeq (true, $(HWUI_ENABLE_OPENGL_VALIDATION)) - hwui_cflags += -include debug/wrap_gles.h - hwui_src_files += debug/wrap_gles.cpp - hwui_c_includes += frameworks/native/opengl/libs/GLES2 - hwui_cflags += -DDEBUG_OPENGL=3 -endif - - # ------------------------ # static library # ------------------------ @@ -190,6 +207,13 @@ LOCAL_MODULE_CLASS := STATIC_LIBRARIES LOCAL_MODULE := libhwui_static LOCAL_CFLAGS := $(hwui_cflags) LOCAL_SRC_FILES := $(hwui_src_files) + +ifeq (true, $(HWUI_ENABLE_OPENGL_VALIDATION)) + LOCAL_CFLAGS += -include debug/wrap_gles.h + LOCAL_CFLAGS += -DDEBUG_OPENGL=3 + LOCAL_SRC_FILES += $(hwui_debug_common_src_files) +endif + LOCAL_C_INCLUDES := $(hwui_c_includes) $(call hwui_proto_include) LOCAL_EXPORT_C_INCLUDE_DIRS := \ $(LOCAL_PATH) \ @@ -205,14 +229,15 @@ include $(BUILD_STATIC_LIBRARY) include $(CLEAR_VARS) LOCAL_MODULE_CLASS := STATIC_LIBRARIES -LOCAL_MODULE := libhwui_static_null_gpu +LOCAL_MODULE := libhwui_static_debug LOCAL_CFLAGS := \ $(hwui_cflags) \ + -include debug/wrap_gles.h \ -DHWUI_NULL_GPU LOCAL_SRC_FILES := \ $(hwui_src_files) \ - debug/nullegl.cpp \ - debug/nullgles.cpp + $(hwui_debug_common_src_files) \ + debug/nullegl.cpp LOCAL_C_INCLUDES := $(hwui_c_includes) $(call hwui_proto_include) LOCAL_EXPORT_C_INCLUDE_DIRS := \ $(LOCAL_PATH) \ @@ -243,49 +268,55 @@ include $(CLEAR_VARS) LOCAL_MODULE := hwui_unit_tests LOCAL_MODULE_TAGS := tests -LOCAL_STATIC_LIBRARIES := libhwui_static_null_gpu +LOCAL_STATIC_LIBRARIES := libgmock libhwui_static_debug LOCAL_SHARED_LIBRARIES := libmemunreachable LOCAL_CFLAGS := \ $(hwui_cflags) \ + -include debug/wrap_gles.h \ -DHWUI_NULL_GPU LOCAL_C_INCLUDES := $(hwui_c_includes) LOCAL_SRC_FILES += \ $(hwui_test_common_src_files) \ tests/unit/main.cpp \ + tests/unit/BakedOpDispatcherTests.cpp \ + tests/unit/BakedOpRendererTests.cpp \ + tests/unit/BakedOpStateTests.cpp \ + tests/unit/BitmapTests.cpp \ + tests/unit/CanvasContextTests.cpp \ tests/unit/CanvasStateTests.cpp \ tests/unit/ClipAreaTests.cpp \ tests/unit/DamageAccumulatorTests.cpp \ + tests/unit/DeferredLayerUpdaterTests.cpp \ tests/unit/DeviceInfoTests.cpp \ tests/unit/FatVectorTests.cpp \ tests/unit/FontRendererTests.cpp \ + tests/unit/FrameBuilderTests.cpp \ tests/unit/GlopBuilderTests.cpp \ tests/unit/GpuMemoryTrackerTests.cpp \ tests/unit/GradientCacheTests.cpp \ tests/unit/LayerUpdateQueueTests.cpp \ + tests/unit/LeakCheckTests.cpp \ tests/unit/LinearAllocatorTests.cpp \ tests/unit/MatrixTests.cpp \ + tests/unit/MeshStateTests.cpp \ tests/unit/OffscreenBufferPoolTests.cpp \ + tests/unit/OpDumperTests.cpp \ + tests/unit/PathInterpolatorTests.cpp \ + tests/unit/RenderNodeDrawableTests.cpp \ + tests/unit/RecordingCanvasTests.cpp \ tests/unit/RenderNodeTests.cpp \ tests/unit/RenderPropertiesTests.cpp \ tests/unit/SkiaBehaviorTests.cpp \ + tests/unit/SkiaDisplayListTests.cpp \ + tests/unit/SkiaPipelineTests.cpp \ + tests/unit/SkiaRenderPropertiesTests.cpp \ + tests/unit/SkiaCanvasTests.cpp \ tests/unit/SnapshotTests.cpp \ tests/unit/StringUtilsTests.cpp \ tests/unit/TestUtilsTests.cpp \ tests/unit/TextDropShadowCacheTests.cpp \ - tests/unit/VectorDrawableTests.cpp - -ifeq (true, $(HWUI_NEW_OPS)) - LOCAL_SRC_FILES += \ - tests/unit/BakedOpDispatcherTests.cpp \ - tests/unit/BakedOpRendererTests.cpp \ - tests/unit/BakedOpStateTests.cpp \ - tests/unit/FrameBuilderTests.cpp \ - tests/unit/LeakCheckTests.cpp \ - tests/unit/OpDumperTests.cpp \ - tests/unit/RecordingCanvasTests.cpp \ - tests/unit/SkiaCanvasTests.cpp -endif + tests/unit/VectorDrawableTests.cpp \ include $(LOCAL_PATH)/hwui_static_deps.mk include $(BUILD_NATIVE_TEST) @@ -297,17 +328,15 @@ include $(BUILD_NATIVE_TEST) include $(CLEAR_VARS) LOCAL_MODULE_PATH := $(TARGET_OUT_DATA)/local/tmp -LOCAL_MODULE:= hwuitest +LOCAL_MODULE:= hwuimacro LOCAL_MODULE_TAGS := tests -LOCAL_MODULE_CLASS := EXECUTABLES LOCAL_MULTILIB := both -LOCAL_MODULE_STEM_32 := hwuitest -LOCAL_MODULE_STEM_64 := hwuitest64 LOCAL_CFLAGS := $(hwui_cflags) LOCAL_C_INCLUDES := $(hwui_c_includes) -# set to libhwui_static_null_gpu to skip actual GL commands +# set to libhwui_static_debug to skip actual GL commands LOCAL_WHOLE_STATIC_LIBRARIES := libhwui_static +LOCAL_SHARED_LIBRARIES := libmemunreachable LOCAL_SRC_FILES += \ $(hwui_test_common_src_files) \ @@ -315,43 +344,37 @@ LOCAL_SRC_FILES += \ tests/macrobench/main.cpp include $(LOCAL_PATH)/hwui_static_deps.mk -include $(BUILD_EXECUTABLE) +include $(BUILD_NATIVE_BENCHMARK) # ------------------------ # Micro-bench app # --------------------- include $(CLEAR_VARS) -LOCAL_MODULE_PATH := $(TARGET_OUT_DATA)/local/tmp LOCAL_MODULE:= hwuimicro LOCAL_MODULE_TAGS := tests -LOCAL_MODULE_CLASS := EXECUTABLES -LOCAL_MULTILIB := both -LOCAL_MODULE_STEM_32 := hwuimicro -LOCAL_MODULE_STEM_64 := hwuimicro64 LOCAL_CFLAGS := \ $(hwui_cflags) \ + -include debug/wrap_gles.h \ -DHWUI_NULL_GPU LOCAL_C_INCLUDES := $(hwui_c_includes) -LOCAL_WHOLE_STATIC_LIBRARIES := libhwui_static_null_gpu -LOCAL_STATIC_LIBRARIES := libgoogle-benchmark +LOCAL_WHOLE_STATIC_LIBRARIES := libhwui_static_debug +LOCAL_SHARED_LIBRARIES := libmemunreachable LOCAL_SRC_FILES += \ $(hwui_test_common_src_files) \ tests/microbench/main.cpp \ tests/microbench/DisplayListCanvasBench.cpp \ tests/microbench/FontBench.cpp \ + tests/microbench/FrameBuilderBench.cpp \ tests/microbench/LinearAllocatorBench.cpp \ tests/microbench/PathParserBench.cpp \ + tests/microbench/RenderNodeBench.cpp \ tests/microbench/ShadowBench.cpp \ tests/microbench/TaskManagerBench.cpp -ifeq (true, $(HWUI_NEW_OPS)) - LOCAL_SRC_FILES += \ - tests/microbench/FrameBuilderBench.cpp -endif include $(LOCAL_PATH)/hwui_static_deps.mk -include $(BUILD_EXECUTABLE) +include $(BUILD_NATIVE_BENCHMARK) diff --git a/libs/hwui/Animator.cpp b/libs/hwui/Animator.cpp index 74aa3033ee12..b6fbf891f84d 100644 --- a/libs/hwui/Animator.cpp +++ b/libs/hwui/Animator.cpp @@ -317,7 +317,7 @@ struct RenderPropertyAnimator::PropertyAccessors { const RenderPropertyAnimator::PropertyAccessors RenderPropertyAnimator::PROPERTY_ACCESSOR_LUT[] = { {RenderNode::TRANSLATION_X, &RenderProperties::getTranslationX, &RenderProperties::setTranslationX }, {RenderNode::TRANSLATION_Y, &RenderProperties::getTranslationY, &RenderProperties::setTranslationY }, - {RenderNode::TRANSLATION_X, &RenderProperties::getTranslationZ, &RenderProperties::setTranslationZ }, + {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 }, diff --git a/libs/hwui/AssetAtlas.cpp b/libs/hwui/AssetAtlas.cpp deleted file mode 100644 index e2e7037202b8..000000000000 --- a/libs/hwui/AssetAtlas.cpp +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "AssetAtlas.h" -#include "Caches.h" -#include "Image.h" - -#include <GLES2/gl2ext.h> - -namespace android { -namespace uirenderer { - -/////////////////////////////////////////////////////////////////////////////// -// Lifecycle -/////////////////////////////////////////////////////////////////////////////// - -void AssetAtlas::init(const sp<GraphicBuffer>& buffer, int64_t* map, int count) { - if (mImage) { - return; - } - - ATRACE_NAME("AssetAtlas::init"); - - mImage = new Image(buffer); - if (mImage->getTexture()) { - if (!mTexture) { - Caches& caches = Caches::getInstance(); - mTexture = new Texture(caches); - mTexture->wrap(mImage->getTexture(), - buffer->getWidth(), buffer->getHeight(), GL_RGBA); - createEntries(caches, map, count); - } - } else { - ALOGW("Could not create atlas image"); - terminate(); - } -} - -void AssetAtlas::terminate() { - delete mImage; - mImage = nullptr; - delete mTexture; - mTexture = nullptr; - mEntries.clear(); -} - -/////////////////////////////////////////////////////////////////////////////// -// Entries -/////////////////////////////////////////////////////////////////////////////// - -AssetAtlas::Entry* AssetAtlas::getEntry(const SkPixelRef* pixelRef) const { - auto result = mEntries.find(pixelRef); - return result != mEntries.end() ? result->second.get() : nullptr; -} - -Texture* AssetAtlas::getEntryTexture(const SkPixelRef* pixelRef) const { - auto result = mEntries.find(pixelRef); - return result != mEntries.end() ? result->second->texture : nullptr; -} - -/** - * Delegates changes to wrapping and filtering to the base atlas texture - * instead of applying the changes to the virtual textures. - */ -struct DelegateTexture: public Texture { - DelegateTexture(Caches& caches, Texture* delegate) - : Texture(caches), mDelegate(delegate) { } - - virtual void setWrapST(GLenum wrapS, GLenum wrapT, bool bindTexture = false, - bool force = false, GLenum renderTarget = GL_TEXTURE_2D) override { - mDelegate->setWrapST(wrapS, wrapT, bindTexture, force, renderTarget); - } - - virtual void setFilterMinMag(GLenum min, GLenum mag, bool bindTexture = false, - bool force = false, GLenum renderTarget = GL_TEXTURE_2D) override { - mDelegate->setFilterMinMag(min, mag, bindTexture, force, renderTarget); - } - -private: - Texture* const mDelegate; -}; // struct DelegateTexture - -void AssetAtlas::createEntries(Caches& caches, int64_t* map, int count) { - const float width = float(mTexture->width()); - const float height = float(mTexture->height()); - - for (int i = 0; i < count; ) { - SkPixelRef* pixelRef = reinterpret_cast<SkPixelRef*>(map[i++]); - // NOTE: We're converting from 64 bit signed values to 32 bit - // signed values. This is guaranteed to be safe because the "x" - // and "y" coordinate values are guaranteed to be representable - // with 32 bits. The array is 64 bits wide so that it can carry - // pointers on 64 bit architectures. - const int x = static_cast<int>(map[i++]); - const int y = static_cast<int>(map[i++]); - - // Bitmaps should never be null, we're just extra paranoid - if (!pixelRef) continue; - - const UvMapper mapper( - x / width, (x + pixelRef->info().width()) / width, - y / height, (y + pixelRef->info().height()) / height); - - Texture* texture = new DelegateTexture(caches, mTexture); - texture->blend = !SkAlphaTypeIsOpaque(pixelRef->info().alphaType()); - texture->wrap(mTexture->id(), pixelRef->info().width(), - pixelRef->info().height(), mTexture->format()); - - std::unique_ptr<Entry> entry(new Entry(pixelRef, texture, mapper, *this)); - texture->uvMapper = &entry->uvMapper; - - mEntries.emplace(entry->pixelRef, std::move(entry)); - } -} - -}; // namespace uirenderer -}; // namespace android diff --git a/libs/hwui/AssetAtlas.h b/libs/hwui/AssetAtlas.h deleted file mode 100644 index b32e51851b94..000000000000 --- a/libs/hwui/AssetAtlas.h +++ /dev/null @@ -1,174 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ANDROID_HWUI_ASSET_ATLAS_H -#define ANDROID_HWUI_ASSET_ATLAS_H - -#include "Texture.h" -#include "UvMapper.h" - -#include <cutils/compiler.h> -#include <GLES2/gl2.h> -#include <ui/GraphicBuffer.h> -#include <SkBitmap.h> - -#include <memory> -#include <unordered_map> - -namespace android { -namespace uirenderer { - -class Caches; -class Image; - -/** - * An asset atlas holds a collection of framework bitmaps in a single OpenGL - * texture. Each bitmap is associated with a location, defined in pixels, - * inside the atlas. The atlas is generated by the framework and bound as - * an external texture using the EGLImageKHR extension. - */ -class AssetAtlas { -public: - /** - * Entry representing the texture and uvMapper of a PixelRef in the - * atlas - */ - class Entry { - public: - /* - * A "virtual texture" object that represents the texture - * this entry belongs to. This texture should never be - * modified. - */ - Texture* texture; - - /** - * Maps texture coordinates in the [0..1] range into the - * correct range to sample this entry from the atlas. - */ - const UvMapper uvMapper; - - /** - * Unique identifier used to merge bitmaps and 9-patches stored - * in the atlas. - */ - const void* getMergeId() const { - return texture->blend ? &atlas.mBlendKey : &atlas.mOpaqueKey; - } - - ~Entry() { - delete texture; - } - - private: - /** - * The pixel ref that generated this atlas entry. - */ - SkPixelRef* pixelRef; - - /** - * Atlas this entry belongs to. - */ - const AssetAtlas& atlas; - - Entry(SkPixelRef* pixelRef, Texture* texture, const UvMapper& mapper, - const AssetAtlas& atlas) - : texture(texture) - , uvMapper(mapper) - , pixelRef(pixelRef) - , atlas(atlas) { - } - - friend class AssetAtlas; - }; - - AssetAtlas(): mTexture(nullptr), mImage(nullptr), - mBlendKey(true), mOpaqueKey(false) { } - ~AssetAtlas() { terminate(); } - - /** - * Initializes the atlas with the specified buffer and - * map. The buffer is a gralloc'd texture that will be - * used as an EGLImage. The map is a list of SkBitmap* - * and their (x, y) positions - * - * This method returns immediately if the atlas is already - * initialized. To re-initialize the atlas, you must - * first call terminate(). - */ - ANDROID_API void init(const sp<GraphicBuffer>& buffer, int64_t* map, int count); - - /** - * Destroys the atlas texture. This object can be - * re-initialized after calling this method. - * - * After calling this method, the width, height - * and texture are set to 0. - */ - void terminate(); - - /** - * Returns the width of this atlas in pixels. - * Can return 0 if the atlas is not initialized. - */ - uint32_t getWidth() const { - return mTexture ? mTexture->width() : 0; - } - - /** - * Returns the height of this atlas in pixels. - * Can return 0 if the atlas is not initialized. - */ - uint32_t getHeight() const { - return mTexture ? mTexture->height() : 0; - } - - /** - * Returns the OpenGL name of the texture backing this atlas. - * Can return 0 if the atlas is not initialized. - */ - GLuint getTexture() const { - return mTexture ? mTexture->id() : 0; - } - - /** - * Returns the entry in the atlas associated with the specified - * pixelRef. If the pixelRef is not in the atlas, return NULL. - */ - Entry* getEntry(const SkPixelRef* pixelRef) const; - - /** - * Returns the texture for the atlas entry associated with the - * specified pixelRef. If the pixelRef is not in the atlas, return NULL. - */ - Texture* getEntryTexture(const SkPixelRef* pixelRef) const; - -private: - void createEntries(Caches& caches, int64_t* map, int count); - - Texture* mTexture; - Image* mImage; - - const bool mBlendKey; - const bool mOpaqueKey; - - std::unordered_map<const SkPixelRef*, std::unique_ptr<Entry>> mEntries; -}; // class AssetAtlas - -}; // namespace uirenderer -}; // namespace android - -#endif // ANDROID_HWUI_ASSET_ATLAS_H diff --git a/libs/hwui/BakedOpDispatcher.cpp b/libs/hwui/BakedOpDispatcher.cpp index 8b3f1722dddf..6079d5def1f8 100644 --- a/libs/hwui/BakedOpDispatcher.cpp +++ b/libs/hwui/BakedOpDispatcher.cpp @@ -35,29 +35,24 @@ namespace android { namespace uirenderer { -static void storeTexturedRect(TextureVertex* vertices, const Rect& bounds, const Rect& texCoord) { - vertices[0] = { bounds.left, bounds.top, texCoord.left, texCoord.top }; - vertices[1] = { bounds.right, bounds.top, texCoord.right, texCoord.top }; - vertices[2] = { bounds.left, bounds.bottom, texCoord.left, texCoord.bottom }; - vertices[3] = { bounds.right, bounds.bottom, texCoord.right, texCoord.bottom }; +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 }; } void BakedOpDispatcher::onMergedBitmapOps(BakedOpRenderer& renderer, const MergedBakedOpList& opList) { const BakedOpState& firstState = *(opList.states[0]); - const SkBitmap* bitmap = (static_cast<const BitmapOp*>(opList.states[0]->op))->bitmap; + Bitmap* bitmap = (static_cast<const BitmapOp*>(opList.states[0]->op))->bitmap; - AssetAtlas::Entry* entry = renderer.renderState().assetAtlas().getEntry(bitmap->pixelRef()); - Texture* texture = entry ? entry->texture : renderer.caches().textureCache.get(bitmap); + Texture* texture = renderer.caches().textureCache.get(bitmap); if (!texture) return; const AutoTexture autoCleanup(texture); TextureVertex vertices[opList.count * 4]; - Rect texCoords(0, 0, 1, 1); - if (entry) { - entry->uvMapper.map(texCoords); - } for (size_t i = 0; i < opList.count; i++) { const BakedOpState& state = *(opList.states[i]); TextureVertex* rectVerts = &vertices[i * 4]; @@ -69,7 +64,7 @@ void BakedOpDispatcher::onMergedBitmapOps(BakedOpRenderer& renderer, // pure translate, so snap (same behavior as onBitmapOp) opBounds.snapToPixelBoundaries(); } - storeTexturedRect(rectVerts, opBounds, texCoords); + storeTexturedRect(rectVerts, opBounds); renderer.dirtyRenderTarget(opBounds); } @@ -92,8 +87,6 @@ void BakedOpDispatcher::onMergedPatchOps(BakedOpRenderer& renderer, const MergedBakedOpList& opList) { const PatchOp& firstOp = *(static_cast<const PatchOp*>(opList.states[0]->op)); const BakedOpState& firstState = *(opList.states[0]); - AssetAtlas::Entry* entry = renderer.renderState().assetAtlas().getEntry( - firstOp.bitmap->pixelRef()); // Batches will usually contain a small number of items so it's // worth performing a first iteration to count the exact number @@ -105,7 +98,7 @@ void BakedOpDispatcher::onMergedPatchOps(BakedOpRenderer& renderer, // TODO: cache mesh lookups const Patch* opMesh = renderer.caches().patchCache.get( - entry, op.bitmap->width(), op.bitmap->height(), + op.bitmap->width(), op.bitmap->height(), op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.patch); totalVertices += opMesh->verticesCount; } @@ -126,7 +119,7 @@ void BakedOpDispatcher::onMergedPatchOps(BakedOpRenderer& renderer, // TODO: cache mesh lookups const Patch* opMesh = renderer.caches().patchCache.get( - entry, op.bitmap->width(), op.bitmap->height(), + op.bitmap->width(), op.bitmap->height(), op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.patch); @@ -172,7 +165,7 @@ void BakedOpDispatcher::onMergedPatchOps(BakedOpRenderer& renderer, } - Texture* texture = entry ? entry->texture : renderer.caches().textureCache.get(firstOp.bitmap); + Texture* texture = renderer.caches().textureCache.get(firstOp.bitmap); if (!texture) return; const AutoTexture autoCleanup(texture); @@ -299,7 +292,7 @@ static void renderText(BakedOpRenderer& renderer, const TextOp& op, const BakedO 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; - SkXfermode::Mode mode = PaintUtils::getXfermodeDirect(op.paint); + SkBlendMode mode = PaintUtils::getBlendModeDirect(op.paint); TextDrawFunctor functor(&renderer, &state, renderClip, x, y, pureTranslate, alpha, mode, op.paint); @@ -442,7 +435,12 @@ void BakedOpDispatcher::onBitmapOp(BakedOpRenderer& renderer, const BitmapOp& op } void BakedOpDispatcher::onBitmapMeshOp(BakedOpRenderer& renderer, const BitmapMeshOp& op, const BakedOpState& state) { - const static UvMapper defaultUvMapper; + Texture* texture = renderer.caches().textureCache.get(op.bitmap); + if (!texture) { + return; + } + const AutoTexture autoCleanup(texture); + const uint32_t elementCount = op.meshWidth * op.meshHeight * 6; std::unique_ptr<ColorTextureVertex[]> mesh(new ColorTextureVertex[elementCount]); @@ -457,9 +455,6 @@ void BakedOpDispatcher::onBitmapMeshOp(BakedOpRenderer& renderer, const BitmapMe colors = tempColors.get(); } - Texture* texture = renderer.renderState().assetAtlas().getEntryTexture(op.bitmap->pixelRef()); - const UvMapper& mapper(texture && texture->uvMapper ? *texture->uvMapper : defaultUvMapper); - for (int32_t y = 0; y < op.meshHeight; y++) { for (int32_t x = 0; x < op.meshWidth; x++) { uint32_t i = (y * (op.meshWidth + 1) + x) * 2; @@ -469,8 +464,6 @@ void BakedOpDispatcher::onBitmapMeshOp(BakedOpRenderer& renderer, const BitmapMe float v1 = float(y) / op.meshHeight; float v2 = float(y + 1) / op.meshHeight; - mapper.map(u1, v1, u2, v2); - int ax = i + (op.meshWidth + 1) * 2; int ay = ax + 1; int bx = i; @@ -491,14 +484,6 @@ void BakedOpDispatcher::onBitmapMeshOp(BakedOpRenderer& renderer, const BitmapMe } } - if (!texture) { - texture = renderer.caches().textureCache.get(op.bitmap); - if (!texture) { - return; - } - } - const AutoTexture autoCleanup(texture); - /* * TODO: handle alpha_8 textures correctly by applying paint color, but *not* * shader in that case to mimic the behavior in SkiaCanvas::drawBitmapMesh. @@ -543,7 +528,7 @@ void BakedOpDispatcher::onBitmapRectOp(BakedOpRenderer& renderer, const BitmapRe void BakedOpDispatcher::onColorOp(BakedOpRenderer& renderer, const ColorOp& op, const BakedOpState& state) { SkPaint paint; paint.setColor(op.color); - paint.setXfermodeMode(op.mode); + paint.setBlendMode(op.mode); Glop glop; GlopBuilder(renderer.renderState(), renderer.caches(), &glop) @@ -599,12 +584,11 @@ void BakedOpDispatcher::onPatchOp(BakedOpRenderer& renderer, const PatchOp& op, } // TODO: avoid redoing the below work each frame: - AssetAtlas::Entry* entry = renderer.renderState().assetAtlas().getEntry(op.bitmap->pixelRef()); const Patch* mesh = renderer.caches().patchCache.get( - entry, op.bitmap->width(), op.bitmap->height(), + op.bitmap->width(), op.bitmap->height(), op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.patch); - Texture* texture = entry ? entry->texture : renderer.caches().textureCache.get(op.bitmap); + Texture* texture = renderer.caches().textureCache.get(op.bitmap); if (CC_LIKELY(texture)) { const AutoTexture autoCleanup(texture); Glop glop; @@ -760,7 +744,7 @@ void BakedOpDispatcher::onTextOnPathOp(BakedOpRenderer& renderer, const TextOnPa 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; - SkXfermode::Mode mode = PaintUtils::getXfermodeDirect(op.paint); + SkBlendMode mode = PaintUtils::getBlendModeDirect(op.paint); TextDrawFunctor functor(&renderer, &state, renderTargetClip, 0.0f, 0.0f, false, alpha, mode, op.paint); @@ -792,11 +776,11 @@ void BakedOpDispatcher::onTextureLayerOp(BakedOpRenderer& renderer, const Textur } void renderRectForLayer(BakedOpRenderer& renderer, const LayerOp& op, const BakedOpState& state, - int color, SkXfermode::Mode mode, SkColorFilter* colorFilter) { + int color, SkBlendMode mode, SkColorFilter* colorFilter) { SkPaint paint; paint.setColor(color); - paint.setXfermodeMode(mode); - paint.setColorFilter(colorFilter); + paint.setBlendMode(mode); + paint.setColorFilter(sk_ref_sp(colorFilter)); RectOp rectOp(op.unmappedBounds, op.localMatrix, op.localClip, &paint); BakedOpDispatcher::onRectOp(renderer, rectOp, state); } @@ -824,11 +808,11 @@ void BakedOpDispatcher::onLayerOp(BakedOpRenderer& renderer, const LayerOp& op, if (CC_UNLIKELY(Properties::debugLayersUpdates)) { // render debug layer highlight renderRectForLayer(renderer, op, state, - 0x7f00ff00, SkXfermode::Mode::kSrcOver_Mode, nullptr); + 0x7f00ff00, SkBlendMode::kSrcOver, nullptr); } else if (CC_UNLIKELY(Properties::debugOverdraw)) { // render transparent to increment overdraw for repaint area renderRectForLayer(renderer, op, state, - SK_ColorTRANSPARENT, SkXfermode::Mode::kSrcOver_Mode, nullptr); + SK_ColorTRANSPARENT, SkBlendMode::kSrcOver, nullptr); } } } @@ -845,14 +829,14 @@ void BakedOpDispatcher::onCopyFromLayerOp(BakedOpRenderer& renderer, const CopyF if (op.paint && op.paint->getAlpha() < 255) { SkPaint layerPaint; layerPaint.setAlpha(op.paint->getAlpha()); - layerPaint.setXfermodeMode(SkXfermode::kDstIn_Mode); - layerPaint.setColorFilter(op.paint->getColorFilter()); + layerPaint.setBlendMode(SkBlendMode::kDstIn); + layerPaint.setColorFilter(sk_ref_sp(op.paint->getColorFilter())); RectOp rectOp(state.computedState.clippedBounds, Matrix4::identity(), nullptr, &layerPaint); BakedOpDispatcher::onRectOp(renderer, rectOp, state); } OffscreenBuffer& layer = **(op.layerHandle); - auto mode = PaintUtils::getXfermodeDirect(op.paint); + auto mode = PaintUtils::getBlendModeDirect(op.paint); Glop glop; GlopBuilder(renderer.renderState(), renderer.caches(), &glop) .setRoundRectClipState(state.roundRectClipState) diff --git a/libs/hwui/BakedOpRenderer.cpp b/libs/hwui/BakedOpRenderer.cpp index 6db345ac0006..e8972aab9f8f 100644 --- a/libs/hwui/BakedOpRenderer.cpp +++ b/libs/hwui/BakedOpRenderer.cpp @@ -181,12 +181,8 @@ void BakedOpRenderer::clearColorBuffer(const Rect& rect) { if (!mRenderTarget.frameBufferId) mHasDrawn = true; } -Texture* BakedOpRenderer::getTexture(const SkBitmap* bitmap) { - Texture* texture = mRenderState.assetAtlas().getEntryTexture(bitmap->pixelRef()); - if (!texture) { - return mCaches.textureCache.get(bitmap); - } - return texture; +Texture* BakedOpRenderer::getTexture(Bitmap* bitmap) { + return mCaches.textureCache.get(bitmap); } void BakedOpRenderer::drawRects(const float* rects, int count, const SkPaint* paint) { diff --git a/libs/hwui/BakedOpRenderer.h b/libs/hwui/BakedOpRenderer.h index 62bc564a4a2a..4d76a3df7a62 100644 --- a/libs/hwui/BakedOpRenderer.h +++ b/libs/hwui/BakedOpRenderer.h @@ -74,7 +74,7 @@ public: void endLayer(); WARN_UNUSED_RESULT OffscreenBuffer* copyToLayer(const Rect& area); - Texture* getTexture(const SkBitmap* bitmap); + Texture* getTexture(Bitmap* bitmap); const LightInfo& getLightInfo() const { return mLightInfo; } void renderGlop(const BakedOpState& state, const Glop& glop) { diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp index a8ced9b2597b..a0366dee3218 100644 --- a/libs/hwui/Caches.cpp +++ b/libs/hwui/Caches.cpp @@ -17,7 +17,7 @@ #include "Caches.h" #include "GammaFontRenderer.h" -#include "LayerRenderer.h" +#include "GlLayer.h" #include "Properties.h" #include "renderstate/RenderState.h" #include "ShadowTessellator.h" @@ -53,7 +53,6 @@ Caches::Caches(RenderState& renderState) : gradientCache(mExtensions) , patchCache(renderState) , programCache(mExtensions) - , dither(*this) , mRenderState(&renderState) , mInitialized(false) { INIT_LOGD("Creating OpenGL renderer caches"); @@ -71,8 +70,6 @@ bool Caches::init() { mRegionMesh = nullptr; mProgram = nullptr; - patchCache.init(); - mInitialized = true; mPixelBufferState = new PixelBufferState(); @@ -168,17 +165,17 @@ void Caches::dumpMemoryUsage(String8 &log) { log.appendFormat("Current memory usage / total memory usage (bytes):\n"); log.appendFormat(" TextureCache %8d / %8d\n", textureCache.getSize(), textureCache.getMaxSize()); - log.appendFormat(" LayerCache %8d / %8d (numLayers = %zu)\n", - layerCache.getSize(), layerCache.getMaxSize(), layerCache.getCount()); if (mRenderState) { int memused = 0; for (std::set<Layer*>::iterator it = mRenderState->mActiveLayers.begin(); it != mRenderState->mActiveLayers.end(); it++) { const Layer* layer = *it; - log.appendFormat(" Layer size %dx%d; isTextureLayer()=%d; texid=%u fbo=%u; refs=%d\n", + 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(), - layer->isTextureLayer(), layer->getTextureId(), - layer->getFbo(), layer->getStrongCount()); + glLayer->getTextureId(), + layer->getStrongCount()); memused += layer->getWidth() * layer->getHeight() * 4; } log.appendFormat(" Layers total %8d (numLayers = %zu)\n", @@ -242,7 +239,6 @@ void Caches::flush(FlushMode mode) { gradientCache.clear(); fontRenderer.clear(); fboCache.clear(); - dither.clear(); // fall through case FlushMode::Moderate: fontRenderer.flush(); @@ -251,7 +247,6 @@ void Caches::flush(FlushMode mode) { tessellationCache.clear(); // fall through case FlushMode::Layers: - layerCache.clear(); renderBufferCache.clear(); break; } diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h index 317c122c62b8..7c2e78c7d3b8 100644 --- a/libs/hwui/Caches.h +++ b/libs/hwui/Caches.h @@ -14,16 +14,12 @@ * limitations under the License. */ -#ifndef ANDROID_HWUI_CACHES_H -#define ANDROID_HWUI_CACHES_H +#pragma once -#include "AssetAtlas.h" -#include "Dither.h" #include "Extensions.h" #include "FboCache.h" #include "GammaFontRenderer.h" #include "GradientCache.h" -#include "LayerCache.h" #include "PatchCache.h" #include "ProgramCache.h" #include "PathCache.h" @@ -132,6 +128,15 @@ public: TextureVertex* getRegionMesh(); /** + * Returns the GL RGBA internal format to use for the current device + * If the device supports linear blending and needSRGB is true, + * this function returns GL_SRGB8_ALPHA8, otherwise it returns GL_RGBA + */ + constexpr GLint rgbaInternalFormat(bool needSRGB = true) const { + return extensions().hasSRGB() && needSRGB ? GL_SRGB8_ALPHA8 : GL_RGBA; + } + + /** * Displays the memory usage of each cache and the total sum. */ void dumpMemoryUsage(); @@ -146,7 +151,6 @@ private: Extensions mExtensions; public: TextureCache textureCache; - LayerCache layerCache; RenderBufferCache renderBufferCache; GradientCache gradientCache; PatchCache patchCache; @@ -160,8 +164,6 @@ public: TaskManager tasks; - Dither dither; - bool gpuPixelBuffersEnabled; // Debug methods @@ -172,7 +174,7 @@ public: void setProgram(const ProgramDescription& description); void setProgram(Program* program); - Extensions& extensions() { return mExtensions; } + const Extensions& extensions() const { return mExtensions; } Program& program() { return *mProgram; } PixelBufferState& pixelBufferState() { return *mPixelBufferState; } TextureState& textureState() { return *mTextureState; } @@ -205,5 +207,3 @@ private: }; // namespace uirenderer }; // namespace android - -#endif // ANDROID_HWUI_CACHES_H diff --git a/libs/hwui/CanvasState.cpp b/libs/hwui/CanvasState.cpp index e2149d1e4a69..9c068b080d45 100644 --- a/libs/hwui/CanvasState.cpp +++ b/libs/hwui/CanvasState.cpp @@ -23,8 +23,7 @@ namespace uirenderer { CanvasState::CanvasState(CanvasStateClient& renderer) - : mDirtyClip(false) - , mWidth(-1) + : mWidth(-1) , mHeight(-1) , mSaveCount(1) , mCanvas(renderer) @@ -203,21 +202,13 @@ void CanvasState::concatMatrix(const Matrix4& matrix) { // Clip /////////////////////////////////////////////////////////////////////////////// -bool CanvasState::clipRect(float left, float top, float right, float bottom, SkRegion::Op op) { +bool CanvasState::clipRect(float left, float top, float right, float bottom, SkClipOp op) { mSnapshot->clip(Rect(left, top, right, bottom), op); - mDirtyClip = true; return !mSnapshot->clipIsEmpty(); } -bool CanvasState::clipPath(const SkPath* path, SkRegion::Op op) { +bool CanvasState::clipPath(const SkPath* path, SkClipOp op) { mSnapshot->clipPath(*path, op); - mDirtyClip = true; - return !mSnapshot->clipIsEmpty(); -} - -bool CanvasState::clipRegion(const SkRegion* region, SkRegion::Op op) { - mSnapshot->clipRegionTransformed(*region, op); - mDirtyClip = true; return !mSnapshot->clipIsEmpty(); } @@ -229,22 +220,13 @@ void CanvasState::setClippingOutline(LinearAllocator& allocator, const Outline* bool outlineIsRounded = MathUtils::isPositive(radius); if (!outlineIsRounded || currentTransform()->isSimple()) { // TODO: consider storing this rect separately, so that this can't be replaced with clip ops - clipRect(bounds.left, bounds.top, bounds.right, bounds.bottom, SkRegion::kIntersect_Op); + clipRect(bounds.left, bounds.top, bounds.right, bounds.bottom, SkClipOp::kIntersect); } if (outlineIsRounded) { setClippingRoundRect(allocator, bounds, radius, false); } } -void CanvasState::setClippingRoundRect(LinearAllocator& allocator, - const Rect& rect, float radius, bool highPriority) { - mSnapshot->setClippingRoundRect(allocator, rect, radius, highPriority); -} - -void CanvasState::setProjectionPathMask(LinearAllocator& allocator, const SkPath* path) { - mSnapshot->setProjectionPathMask(allocator, path); -} - /////////////////////////////////////////////////////////////////////////////// // Quick Rejection /////////////////////////////////////////////////////////////////////////////// @@ -263,7 +245,7 @@ bool CanvasState::calculateQuickRejectForScissor(float left, float top, float right, float bottom, bool* clipRequired, bool* roundRectClipRequired, bool snapOut) const { - if (mSnapshot->isIgnored() || bottom <= top || right <= left) { + if (bottom <= top || right <= left) { return true; } @@ -291,7 +273,7 @@ bool CanvasState::calculateQuickRejectForScissor(float left, float top, bool CanvasState::quickRejectConservative(float left, float top, float right, float bottom) const { - if (mSnapshot->isIgnored() || bottom <= top || right <= left) { + if (bottom <= top || right <= left) { return true; } diff --git a/libs/hwui/CanvasState.h b/libs/hwui/CanvasState.h index 22feef523f49..b1fe09eb1aec 100644 --- a/libs/hwui/CanvasState.h +++ b/libs/hwui/CanvasState.h @@ -14,11 +14,11 @@ * limitations under the License. */ -#ifndef ANDROID_HWUI_CANVAS_STATE_H -#define ANDROID_HWUI_CANVAS_STATE_H +#pragma once #include "Snapshot.h" +#include <SkClipOp.h> #include <SkMatrix.h> #include <SkPath.h> #include <SkRegion.h> @@ -62,11 +62,11 @@ public: * Renderer interface. Drawing and recording classes that include a CanvasState will have * different use cases: * - * Drawing code maintaining canvas state (i.e. OpenGLRenderer) can query attributes (such as + * Drawing code maintaining canvas state (e.g. FrameBuilder) can query attributes (such as * transform) or hook into changes (e.g. save/restore) with minimal surface area for manipulating * the stack itself. * - * Recording code maintaining canvas state (i.e. DisplayListCanvas) can both record and pass + * Recording code maintaining canvas state (e.g. RecordingCanvas) can both record and pass * through state operations to CanvasState, so that not only will querying operations work * (getClip/Matrix), but so that quickRejection can also be used. */ @@ -122,9 +122,8 @@ public: bool quickRejectConservative(float left, float top, float right, float bottom) const; - bool clipRect(float left, float top, float right, float bottom, SkRegion::Op op); - bool clipPath(const SkPath* path, SkRegion::Op op); - bool clipRegion(const SkRegion* region, SkRegion::Op op); + bool clipRect(float left, float top, float right, float bottom, SkClipOp op); + bool clipPath(const SkPath* path, SkClipOp op); /** * Sets a "clipping outline", which is independent from the regular clip. @@ -134,8 +133,12 @@ public: */ void setClippingOutline(LinearAllocator& allocator, const Outline* outline); void setClippingRoundRect(LinearAllocator& allocator, - const Rect& rect, float radius, bool highPriority = true); - void setProjectionPathMask(LinearAllocator& allocator, const SkPath* path); + const Rect& rect, float radius, bool highPriority = true) { + mSnapshot->setClippingRoundRect(allocator, rect, radius, highPriority); + } + void setProjectionPathMask(const SkPath* path) { + mSnapshot->setProjectionPathMask(path); + } /** * Returns true if drawing in the rectangle (left, top, right, bottom) @@ -145,19 +148,12 @@ public: bool calculateQuickRejectForScissor(float left, float top, float right, float bottom, bool* clipRequired, bool* roundRectClipRequired, bool snapOut) const; - void setDirtyClip(bool opaque) { mDirtyClip = opaque; } - bool getDirtyClip() const { return mDirtyClip; } - void scaleAlpha(float alpha) { mSnapshot->alpha *= alpha; } - void setEmpty(bool value) { mSnapshot->empty = value; } - void setInvisible(bool value) { mSnapshot->invisible = value; } inline const mat4* currentTransform() const { return currentSnapshot()->transform; } inline const Rect& currentRenderTargetClip() const { return currentSnapshot()->getRenderTargetClip(); } - inline Region* currentRegion() const { return currentSnapshot()->region; } inline int currentFlags() const { return currentSnapshot()->flags; } const Vector3& currentLightCenter() const { return currentSnapshot()->getRelativeLightCenter(); } - inline bool currentlyIgnored() const { return currentSnapshot()->isIgnored(); } int getViewportWidth() const { return currentSnapshot()->getViewportWidth(); } int getViewportHeight() const { return currentSnapshot()->getViewportHeight(); } int getWidth() const { return mWidth; } @@ -173,10 +169,6 @@ private: void freeSnapshot(Snapshot* snapshot); void freeAllSnapshots(); - /// indicates that the clip has been changed since the last time it was consumed - // TODO: delete when switching to HWUI_NEW_OPS - bool mDirtyClip; - /// Dimensions of the drawing surface int mWidth, mHeight; @@ -201,5 +193,3 @@ private: }; // namespace uirenderer }; // namespace android - -#endif // ANDROID_HWUI_CANVAS_STATE_H diff --git a/libs/hwui/ClipArea.h b/libs/hwui/ClipArea.h index 2e561601d452..cf5751606d12 100644 --- a/libs/hwui/ClipArea.h +++ b/libs/hwui/ClipArea.h @@ -146,7 +146,6 @@ public: void setClip(float left, float top, float right, float bottom); void clipRectWithTransform(const Rect& r, const mat4* transform, SkRegion::Op op); - void clipRegion(const SkRegion& region, SkRegion::Op op); void clipPathWithTransform(const SkPath& path, const mat4* transform, SkRegion::Op op); @@ -195,6 +194,7 @@ private: void regionModeClipRectWithTransform(const Rect& r, const mat4* transform, SkRegion::Op op); + void clipRegion(const SkRegion& region, SkRegion::Op op); void ensureClipRegion(); void onClipRegionUpdated(); diff --git a/libs/hwui/Debug.h b/libs/hwui/Debug.h index 748edef730b7..e29699d0faf4 100644 --- a/libs/hwui/Debug.h +++ b/libs/hwui/Debug.h @@ -98,6 +98,9 @@ // Turn on to enable debugging shadow #define DEBUG_SHADOW 0 +// Turn on to enable debugging vector drawable +#define DEBUG_VECTOR_DRAWABLE 0 + #if DEBUG_INIT #define INIT_LOGD(...) ALOGD(__VA_ARGS__) #else diff --git a/libs/hwui/DeferredDisplayList.cpp b/libs/hwui/DeferredDisplayList.cpp deleted file mode 100644 index 689179dd8fb4..000000000000 --- a/libs/hwui/DeferredDisplayList.cpp +++ /dev/null @@ -1,686 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <utils/Trace.h> -#include <ui/Rect.h> -#include <ui/Region.h> - -#include "Caches.h" -#include "Debug.h" -#include "DeferredDisplayList.h" -#include "DisplayListOp.h" -#include "OpenGLRenderer.h" -#include "Properties.h" -#include "utils/MathUtils.h" - -#if DEBUG_DEFER - #define DEFER_LOGD(...) ALOGD(__VA_ARGS__) -#else - #define DEFER_LOGD(...) -#endif - -namespace android { -namespace uirenderer { - -// Depth of the save stack at the beginning of batch playback at flush time -#define FLUSH_SAVE_STACK_DEPTH 2 - -#define DEBUG_COLOR_BARRIER 0x1f000000 -#define DEBUG_COLOR_MERGEDBATCH 0x5f7f7fff -#define DEBUG_COLOR_MERGEDBATCH_SOLO 0x5f7fff7f - -static bool avoidOverdraw() { - // Don't avoid overdraw when visualizing it, since that makes it harder to - // debug where it's coming from, and when the problem occurs. - return !Properties::debugOverdraw; -}; - -///////////////////////////////////////////////////////////////////////////////// -// Operation Batches -///////////////////////////////////////////////////////////////////////////////// - -class Batch { -public: - virtual void replay(OpenGLRenderer& renderer, Rect& dirty, int index) = 0; - virtual ~Batch() {} - virtual bool purelyDrawBatch() { return false; } - virtual bool coversBounds(const Rect& bounds) { return false; } -}; - -class DrawBatch : public Batch { -public: - explicit DrawBatch(const DeferInfo& deferInfo) : mAllOpsOpaque(true), - mBatchId(deferInfo.batchId), mMergeId(deferInfo.mergeId) { - mOps.clear(); - } - - virtual ~DrawBatch() { mOps.clear(); } - - virtual void add(DrawOp* op, const DeferredDisplayState* state, bool opaqueOverBounds) { - // NOTE: ignore empty bounds special case, since we don't merge across those ops - mBounds.unionWith(state->mBounds); - mAllOpsOpaque &= opaqueOverBounds; - mOps.push_back(OpStatePair(op, state)); - } - - bool intersects(const Rect& rect) { - if (!rect.intersects(mBounds)) return false; - - for (unsigned int i = 0; i < mOps.size(); i++) { - if (rect.intersects(mOps[i].state->mBounds)) { -#if DEBUG_DEFER - DEFER_LOGD("op intersects with op %p with bounds %f %f %f %f:", mOps[i].op, - mOps[i].state->mBounds.left, mOps[i].state->mBounds.top, - mOps[i].state->mBounds.right, mOps[i].state->mBounds.bottom); - mOps[i].op->output(2); -#endif - return true; - } - } - return false; - } - - virtual void replay(OpenGLRenderer& renderer, Rect& dirty, int index) override { - DEFER_LOGD("%d replaying DrawBatch %p, with %d ops (batch id %x, merge id %p)", - index, this, mOps.size(), getBatchId(), getMergeId()); - - for (unsigned int i = 0; i < mOps.size(); i++) { - DrawOp* op = mOps[i].op; - const DeferredDisplayState* state = mOps[i].state; - renderer.restoreDisplayState(*state); - -#if DEBUG_DISPLAY_LIST_OPS_AS_EVENTS - renderer.eventMark(op->name()); -#endif - op->applyDraw(renderer, dirty); - -#if DEBUG_MERGE_BEHAVIOR - const Rect& bounds = state->mBounds; - int batchColor = 0x1f000000; - if (getBatchId() & 0x1) batchColor |= 0x0000ff; - if (getBatchId() & 0x2) batchColor |= 0x00ff00; - if (getBatchId() & 0x4) batchColor |= 0xff0000; - renderer.drawScreenSpaceColorRect(bounds.left, bounds.top, bounds.right, bounds.bottom, - batchColor); -#endif - } - } - - virtual bool purelyDrawBatch() override { return true; } - - virtual bool coversBounds(const Rect& bounds) override { - if (CC_LIKELY(!mAllOpsOpaque || !mBounds.contains(bounds) || count() == 1)) return false; - - Region uncovered(android::Rect(bounds.left, bounds.top, bounds.right, bounds.bottom)); - for (unsigned int i = 0; i < mOps.size(); i++) { - const Rect &r = mOps[i].state->mBounds; - uncovered.subtractSelf(android::Rect(r.left, r.top, r.right, r.bottom)); - } - return uncovered.isEmpty(); - } - - inline int getBatchId() const { return mBatchId; } - inline mergeid_t getMergeId() const { return mMergeId; } - inline int count() const { return mOps.size(); } - -protected: - std::vector<OpStatePair> mOps; - Rect mBounds; // union of bounds of contained ops -private: - bool mAllOpsOpaque; - int mBatchId; - mergeid_t mMergeId; -}; - -class MergingDrawBatch : public DrawBatch { -public: - MergingDrawBatch(DeferInfo& deferInfo, int width, int height) : - DrawBatch(deferInfo), mClipRect(width, height), - mClipSideFlags(kClipSide_None) {} - - /* - * 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) { - bool currentClipExists = currentFlags & side; - bool newClipExists = newFlags & side; - - // if current is clipped, we must be able to fit new bounds in current - if (boundsDelta > 0 && currentClipExists) return false; - - // if new is clipped, we must be able to fit current bounds in new - if (boundsDelta < 0 && newClipExists) return false; - - return true; - } - - /* - * Checks if a (mergeable) op can be merged into this batch - * - * If true, the op's multiDraw must be guaranteed to handle both ops simultaneously, so it is - * important to consider all paint attributes used in the draw calls in deciding both a) if an - * op tries to merge at all, and b) if the op can merge with another set of ops - * - * False positives can lead to information from the paints of subsequent merged operations being - * dropped, so we make simplifying qualifications on the ops that can merge, per op type. - */ - bool canMergeWith(const DrawOp* op, const DeferredDisplayState* state) { - bool isTextBatch = getBatchId() == DeferredDisplayList::kOpBatch_Text || - getBatchId() == DeferredDisplayList::kOpBatch_ColorText; - - // Overlapping other operations is only allowed for text without shadow. For other ops, - // multiDraw isn't guaranteed to overdraw correctly - if (!isTextBatch || op->hasTextShadow()) { - if (intersects(state->mBounds)) return false; - } - const DeferredDisplayState* lhs = state; - const DeferredDisplayState* rhs = mOps[0].state; - - if (!MathUtils::areEqual(lhs->mAlpha, rhs->mAlpha)) return false; - - // Identical round rect clip state means both ops will clip in the same way, or not at all. - // As the state objects are const, we can compare their pointers to determine mergeability - if (lhs->mRoundRectClipState != rhs->mRoundRectClipState) return false; - if (lhs->mProjectionPathMask != rhs->mProjectionPathMask) return false; - - /* Clipping compatibility check - * - * Exploits the fact that if a op or batch is clipped on a side, its bounds will equal its - * clip for that side. - */ - const int currentFlags = mClipSideFlags; - const int newFlags = state->mClipSideFlags; - if (currentFlags != kClipSide_None || newFlags != kClipSide_None) { - const Rect& opBounds = state->mBounds; - float boundsDelta = mBounds.left - opBounds.left; - if (!checkSide(currentFlags, newFlags, kClipSide_Left, boundsDelta)) return false; - boundsDelta = mBounds.top - opBounds.top; - if (!checkSide(currentFlags, newFlags, kClipSide_Top, boundsDelta)) return false; - - // right and bottom delta calculation reversed to account for direction - boundsDelta = opBounds.right - mBounds.right; - if (!checkSide(currentFlags, newFlags, kClipSide_Right, boundsDelta)) return false; - boundsDelta = opBounds.bottom - mBounds.bottom; - if (!checkSide(currentFlags, newFlags, kClipSide_Bottom, boundsDelta)) return false; - } - - // if paints are equal, then modifiers + paint attribs don't need to be compared - if (op->mPaint == mOps[0].op->mPaint) return true; - - if (PaintUtils::getAlphaDirect(op->mPaint) - != PaintUtils::getAlphaDirect(mOps[0].op->mPaint)) { - return false; - } - - if (op->mPaint && mOps[0].op->mPaint && - op->mPaint->getColorFilter() != mOps[0].op->mPaint->getColorFilter()) { - return false; - } - - if (op->mPaint && mOps[0].op->mPaint && - op->mPaint->getShader() != mOps[0].op->mPaint->getShader()) { - return false; - } - - return true; - } - - virtual void add(DrawOp* op, const DeferredDisplayState* state, - bool opaqueOverBounds) override { - DrawBatch::add(op, state, opaqueOverBounds); - - const int newClipSideFlags = state->mClipSideFlags; - mClipSideFlags |= newClipSideFlags; - if (newClipSideFlags & kClipSide_Left) mClipRect.left = state->mClip.left; - if (newClipSideFlags & kClipSide_Top) mClipRect.top = state->mClip.top; - if (newClipSideFlags & kClipSide_Right) mClipRect.right = state->mClip.right; - if (newClipSideFlags & kClipSide_Bottom) mClipRect.bottom = state->mClip.bottom; - } - - virtual void replay(OpenGLRenderer& renderer, Rect& dirty, int index) override { - DEFER_LOGD("%d replaying MergingDrawBatch %p, with %d ops," - " clip flags %x (batch id %x, merge id %p)", - index, this, mOps.size(), mClipSideFlags, getBatchId(), getMergeId()); - if (mOps.size() == 1) { - DrawBatch::replay(renderer, dirty, -1); - return; - } - - // clipping in the merged case is done ahead of time since all ops share the clip (if any) - renderer.setupMergedMultiDraw(mClipSideFlags ? &mClipRect : nullptr); - - DrawOp* op = mOps[0].op; -#if DEBUG_DISPLAY_LIST_OPS_AS_EVENTS - renderer.eventMark("multiDraw"); - renderer.eventMark(op->name()); -#endif - op->multiDraw(renderer, dirty, mOps, mBounds); - -#if DEBUG_MERGE_BEHAVIOR - renderer.drawScreenSpaceColorRect(mBounds.left, mBounds.top, mBounds.right, mBounds.bottom, - DEBUG_COLOR_MERGEDBATCH); -#endif - } - -private: - /* - * Contains the effective clip rect shared by all merged ops. Initialized to the layer viewport, - * it will shrink if an op must be clipped on a certain side. The clipped sides are reflected in - * mClipSideFlags. - */ - Rect mClipRect; - int mClipSideFlags; -}; - -class StateOpBatch : public Batch { -public: - // creates a single operation batch - StateOpBatch(const StateOp* op, const DeferredDisplayState* state) : mOp(op), mState(state) {} - - virtual void replay(OpenGLRenderer& renderer, Rect& dirty, int index) override { - DEFER_LOGD("replaying state op batch %p", this); - renderer.restoreDisplayState(*mState); - - // use invalid save count because it won't be used at flush time - RestoreToCountOp is the - // only one to use it, and we don't use that class at flush time, instead calling - // renderer.restoreToCount directly - int saveCount = -1; - mOp->applyState(renderer, saveCount); - } - -private: - const StateOp* mOp; - const DeferredDisplayState* mState; -}; - -class RestoreToCountBatch : public Batch { -public: - RestoreToCountBatch(const StateOp* op, const DeferredDisplayState* state, int restoreCount) : - mState(state), mRestoreCount(restoreCount) {} - - virtual void replay(OpenGLRenderer& renderer, Rect& dirty, int index) override { - DEFER_LOGD("batch %p restoring to count %d", this, mRestoreCount); - - renderer.restoreDisplayState(*mState); - renderer.restoreToCount(mRestoreCount); - } - -private: - // we use the state storage for the RestoreToCountOp, but don't replay the op itself - const DeferredDisplayState* mState; - - /* - * The count used here represents the flush() time saveCount. This is as opposed to the - * DisplayList record time, or defer() time values (which are RestoreToCountOp's mCount, and - * (saveCount + mCount) respectively). Since the count is different from the original - * RestoreToCountOp, we don't store a pointer to the op, as elsewhere. - */ - const int mRestoreCount; -}; - -#if DEBUG_MERGE_BEHAVIOR -class BarrierDebugBatch : public Batch { - virtual void replay(OpenGLRenderer& renderer, Rect& dirty, int index) { - renderer.drawScreenSpaceColorRect(0, 0, 10000, 10000, DEBUG_COLOR_BARRIER); - } -}; -#endif - -///////////////////////////////////////////////////////////////////////////////// -// DeferredDisplayList -///////////////////////////////////////////////////////////////////////////////// - -void DeferredDisplayList::resetBatchingState() { - for (int i = 0; i < kOpBatch_Count; i++) { - mBatchLookup[i] = nullptr; - mMergingBatches[i].clear(); - } -#if DEBUG_MERGE_BEHAVIOR - if (mBatches.size() != 0) { - mBatches.add(new BarrierDebugBatch()); - } -#endif - mEarliestBatchIndex = mBatches.size(); -} - -void DeferredDisplayList::clear() { - resetBatchingState(); - mComplexClipStackStart = -1; - - for (unsigned int i = 0; i < mBatches.size(); i++) { - delete mBatches[i]; - } - mBatches.clear(); - mSaveStack.clear(); - mEarliestBatchIndex = 0; - mEarliestUnclearedIndex = 0; -} - -///////////////////////////////////////////////////////////////////////////////// -// Operation adding -///////////////////////////////////////////////////////////////////////////////// - -int DeferredDisplayList::getStateOpDeferFlags() const { - // For both clipOp and save(Layer)Op, we don't want to save drawing info, and only want to save - // the clip if we aren't recording a complex clip (and can thus trust it to be a rect) - return recordingComplexClip() ? 0 : kStateDeferFlag_Clip; -} - -int DeferredDisplayList::getDrawOpDeferFlags() const { - return kStateDeferFlag_Draw | getStateOpDeferFlags(); -} - -/** - * When an clipping operation occurs that could cause a complex clip, record the operation and all - * subsequent clipOps, save/restores (if the clip flag is set). During a flush, instead of loading - * the clip from deferred state, we play back all of the relevant state operations that generated - * the complex clip. - * - * Note that we don't need to record the associated restore operation, since operations at defer - * time record whether they should store the renderer's current clip - */ -void DeferredDisplayList::addClip(OpenGLRenderer& renderer, ClipOp* op) { - if (recordingComplexClip() || op->canCauseComplexClip() || !renderer.hasRectToRectTransform()) { - DEFER_LOGD("%p Received complex clip operation %p", this, op); - - // NOTE: defer clip op before setting mComplexClipStackStart so previous clip is recorded - storeStateOpBarrier(renderer, op); - - if (!recordingComplexClip()) { - mComplexClipStackStart = renderer.getSaveCount() - 1; - DEFER_LOGD(" Starting complex clip region, start is %d", mComplexClipStackStart); - } - } -} - -/** - * For now, we record save layer operations as barriers in the batch list, preventing drawing - * operations from reordering around the saveLayer and it's associated restore() - * - * In the future, we should send saveLayer commands (if they can be played out of order) and their - * contained drawing operations to a seperate list of batches, so that they may draw at the - * beginning of the frame. This would avoid targetting and removing an FBO in the middle of a frame. - * - * saveLayer operations should be pulled to the beginning of the frame if the canvas doesn't have a - * complex clip, and if the flags (SaveFlags::Clip & SaveFlags::ClipToLayer) are set. - */ -void DeferredDisplayList::addSaveLayer(OpenGLRenderer& renderer, - SaveLayerOp* op, int newSaveCount) { - DEFER_LOGD("%p adding saveLayerOp %p, flags %x, new count %d", - this, op, op->getFlags(), newSaveCount); - - storeStateOpBarrier(renderer, op); - mSaveStack.push_back(newSaveCount); -} - -/** - * Takes save op and it's return value - the new save count - and stores it into the stream as a - * barrier if it's needed to properly modify a complex clip - */ -void DeferredDisplayList::addSave(OpenGLRenderer& renderer, SaveOp* op, int newSaveCount) { - int saveFlags = op->getFlags(); - DEFER_LOGD("%p adding saveOp %p, flags %x, new count %d", this, op, saveFlags, newSaveCount); - - if (recordingComplexClip() && (saveFlags & SaveFlags::Clip)) { - // store and replay the save operation, as it may be needed to correctly playback the clip - DEFER_LOGD(" adding save barrier with new save count %d", newSaveCount); - storeStateOpBarrier(renderer, op); - mSaveStack.push_back(newSaveCount); - } -} - -/** - * saveLayer() commands must be associated with a restoreToCount batch that will clean up and draw - * the layer in the deferred list - * - * other save() commands which occur as children of a snapshot with complex clip will be deferred, - * and must be restored - * - * Either will act as a barrier to draw operation reordering, as we want to play back layer - * save/restore and complex canvas modifications (including save/restore) in order. - */ -void DeferredDisplayList::addRestoreToCount(OpenGLRenderer& renderer, StateOp* op, - int newSaveCount) { - DEFER_LOGD("%p addRestoreToCount %d", this, newSaveCount); - - if (recordingComplexClip() && newSaveCount <= mComplexClipStackStart) { - mComplexClipStackStart = -1; - resetBatchingState(); - } - - if (mSaveStack.empty() || newSaveCount > mSaveStack.back()) { - return; - } - - while (!mSaveStack.empty() && mSaveStack.back() >= newSaveCount) mSaveStack.pop_back(); - - storeRestoreToCountBarrier(renderer, op, mSaveStack.size() + FLUSH_SAVE_STACK_DEPTH); -} - -void DeferredDisplayList::addDrawOp(OpenGLRenderer& renderer, DrawOp* op) { - /* 1: op calculates local bounds */ - DeferredDisplayState* const state = createState(); - if (op->getLocalBounds(state->mBounds)) { - if (state->mBounds.isEmpty()) { - // valid empty bounds, don't bother deferring - tryRecycleState(state); - return; - } - } else { - state->mBounds.setEmpty(); - } - - /* 2: renderer calculates global bounds + stores state */ - if (renderer.storeDisplayState(*state, getDrawOpDeferFlags())) { - tryRecycleState(state); - return; // quick rejected - } - - /* 3: ask op for defer info, given renderer state */ - DeferInfo deferInfo; - op->onDefer(renderer, deferInfo, *state); - - // complex clip has a complex set of expectations on the renderer state - for now, avoid taking - // the merge path in those cases - deferInfo.mergeable &= !recordingComplexClip(); - deferInfo.opaqueOverBounds &= !recordingComplexClip() - && mSaveStack.empty() - && !state->mRoundRectClipState; - - if (CC_LIKELY(avoidOverdraw()) && mBatches.size() && - state->mClipSideFlags != kClipSide_ConservativeFull && - deferInfo.opaqueOverBounds && state->mBounds.contains(mBounds)) { - // avoid overdraw by resetting drawing state + discarding drawing ops - discardDrawingBatches(mBatches.size() - 1); - resetBatchingState(); - } - - if (CC_UNLIKELY(Properties::drawReorderDisabled)) { - // TODO: elegant way to reuse batches? - DrawBatch* b = new DrawBatch(deferInfo); - b->add(op, state, deferInfo.opaqueOverBounds); - mBatches.push_back(b); - return; - } - - // find the latest batch of the new op's type, and try to merge the new op into it - DrawBatch* targetBatch = nullptr; - - // insertion point of a new batch, will hopefully be immediately after similar batch - // (eventually, should be similar shader) - int insertBatchIndex = mBatches.size(); - if (!mBatches.empty()) { - if (state->mBounds.isEmpty()) { - // don't know the bounds for op, so create new batch and start from scratch on next op - DrawBatch* b = new DrawBatch(deferInfo); - b->add(op, state, deferInfo.opaqueOverBounds); - mBatches.push_back(b); - resetBatchingState(); -#if DEBUG_DEFER - DEFER_LOGD("Warning: Encountered op with empty bounds, resetting batches"); - op->output(2); -#endif - return; - } - - if (deferInfo.mergeable) { - // Try to merge with any existing batch with same mergeId. - std::unordered_map<mergeid_t, DrawBatch*>& mergingBatch - = mMergingBatches[deferInfo.batchId]; - auto getResult = mergingBatch.find(deferInfo.mergeId); - if (getResult != mergingBatch.end()) { - targetBatch = getResult->second; - if (!((MergingDrawBatch*) targetBatch)->canMergeWith(op, state)) { - targetBatch = nullptr; - } - } - } else { - // join with similar, non-merging batch - targetBatch = (DrawBatch*)mBatchLookup[deferInfo.batchId]; - } - - if (targetBatch || deferInfo.mergeable) { - // iterate back toward target to see if anything drawn since should overlap the new op - // if no target, merging ops still interate to find similar batch to insert after - for (int i = mBatches.size() - 1; i >= mEarliestBatchIndex; i--) { - DrawBatch* overBatch = (DrawBatch*)mBatches[i]; - - if (overBatch == targetBatch) break; - - // TODO: also consider shader shared between batch types - if (deferInfo.batchId == overBatch->getBatchId()) { - insertBatchIndex = i + 1; - if (!targetBatch) break; // found insert position, quit - } - - if (overBatch->intersects(state->mBounds)) { - // NOTE: it may be possible to optimize for special cases where two operations - // of the same batch/paint could swap order, such as with a non-mergeable - // (clipped) and a mergeable text operation - targetBatch = nullptr; -#if DEBUG_DEFER - DEFER_LOGD("op couldn't join batch %p, was intersected by batch %d", - targetBatch, i); - op->output(2); -#endif - break; - } - } - } - } - - if (!targetBatch) { - if (deferInfo.mergeable) { - targetBatch = new MergingDrawBatch(deferInfo, - renderer.getViewportWidth(), renderer.getViewportHeight()); - mMergingBatches[deferInfo.batchId].insert( - std::make_pair(deferInfo.mergeId, targetBatch)); - } else { - targetBatch = new DrawBatch(deferInfo); - mBatchLookup[deferInfo.batchId] = targetBatch; - } - - DEFER_LOGD("creating %singBatch %p, bid %x, at %d", - deferInfo.mergeable ? "Merg" : "Draw", - targetBatch, deferInfo.batchId, insertBatchIndex); - mBatches.insert(mBatches.begin() + insertBatchIndex, targetBatch); - } - - targetBatch->add(op, state, deferInfo.opaqueOverBounds); -} - -void DeferredDisplayList::storeStateOpBarrier(OpenGLRenderer& renderer, StateOp* op) { - DEFER_LOGD("%p adding state op barrier at pos %d", this, mBatches.size()); - - DeferredDisplayState* state = createState(); - renderer.storeDisplayState(*state, getStateOpDeferFlags()); - mBatches.push_back(new StateOpBatch(op, state)); - resetBatchingState(); -} - -void DeferredDisplayList::storeRestoreToCountBarrier(OpenGLRenderer& renderer, StateOp* op, - int newSaveCount) { - DEFER_LOGD("%p adding restore to count %d barrier, pos %d", - this, newSaveCount, mBatches.size()); - - // store displayState for the restore operation, as it may be associated with a saveLayer that - // doesn't have SaveFlags::Clip set - DeferredDisplayState* state = createState(); - renderer.storeDisplayState(*state, getStateOpDeferFlags()); - mBatches.push_back(new RestoreToCountBatch(op, state, newSaveCount)); - resetBatchingState(); -} - -///////////////////////////////////////////////////////////////////////////////// -// Replay / flush -///////////////////////////////////////////////////////////////////////////////// - -static void replayBatchList(const std::vector<Batch*>& batchList, - OpenGLRenderer& renderer, Rect& dirty) { - - for (unsigned int i = 0; i < batchList.size(); i++) { - if (batchList[i]) { - batchList[i]->replay(renderer, dirty, i); - } - } - DEFER_LOGD("--flushed, drew %d batches", batchList.size()); -} - -void DeferredDisplayList::flush(OpenGLRenderer& renderer, Rect& dirty) { - ATRACE_NAME("flush drawing commands"); - Caches::getInstance().fontRenderer.endPrecaching(); - - if (isEmpty()) return; // nothing to flush - renderer.restoreToCount(1); - - DEFER_LOGD("--flushing"); - renderer.eventMark("Flush"); - - // save and restore so that reordering doesn't affect final state - renderer.save(SaveFlags::MatrixClip); - - if (CC_LIKELY(avoidOverdraw())) { - for (unsigned int i = 1; i < mBatches.size(); i++) { - if (mBatches[i] && mBatches[i]->coversBounds(mBounds)) { - discardDrawingBatches(i - 1); - } - } - } - // NOTE: depth of the save stack at this point, before playback, should be reflected in - // FLUSH_SAVE_STACK_DEPTH, so that save/restores match up correctly - replayBatchList(mBatches, renderer, dirty); - - renderer.restoreToCount(1); - - DEFER_LOGD("--flush complete, returning %x", status); - clear(); -} - -void DeferredDisplayList::discardDrawingBatches(const unsigned int maxIndex) { - for (unsigned int i = mEarliestUnclearedIndex; i <= maxIndex; i++) { - // leave deferred state ops alone for simplicity (empty save restore pairs may now exist) - if (mBatches[i] && mBatches[i]->purelyDrawBatch()) { - delete mBatches[i]; - mBatches[i] = nullptr; - } - } - mEarliestUnclearedIndex = maxIndex + 1; -} - -}; // namespace uirenderer -}; // namespace android diff --git a/libs/hwui/DeferredDisplayList.h b/libs/hwui/DeferredDisplayList.h deleted file mode 100644 index 15703ea3feef..000000000000 --- a/libs/hwui/DeferredDisplayList.h +++ /dev/null @@ -1,200 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ANDROID_HWUI_DEFERRED_DISPLAY_LIST_H -#define ANDROID_HWUI_DEFERRED_DISPLAY_LIST_H - -#include <unordered_map> - -#include <utils/Errors.h> -#include <utils/LinearAllocator.h> - -#include "Matrix.h" -#include "OpenGLRenderer.h" -#include "Rect.h" - -#include <vector> - -class SkBitmap; - -namespace android { -namespace uirenderer { - -class ClipOp; -class DrawOp; -class SaveOp; -class SaveLayerOp; -class StateOp; - -class DeferredDisplayState; - -class Batch; -class DrawBatch; -class MergingDrawBatch; - -typedef const void* mergeid_t; - -class DeferredDisplayState { -public: - // global op bounds, mapped by mMatrix to be in screen space coordinates, clipped - Rect mBounds; - - // the below are set and used by the OpenGLRenderer at record and deferred playback - bool mClipValid; - Rect mClip; - int mClipSideFlags; // specifies which sides of the bounds are clipped, unclipped if cleared - mat4 mMatrix; - float mAlpha; - const RoundRectClipState* mRoundRectClipState; - const ProjectionPathMask* mProjectionPathMask; -}; - -class OpStatePair { -public: - OpStatePair() - : op(nullptr), state(nullptr) {} - OpStatePair(DrawOp* newOp, const DeferredDisplayState* newState) - : op(newOp), state(newState) {} - OpStatePair(const OpStatePair& other) - : op(other.op), state(other.state) {} - DrawOp* op; - const DeferredDisplayState* state; -}; - -class DeferredDisplayList { - friend struct DeferStateStruct; // used to give access to allocator -public: - explicit DeferredDisplayList(const Rect& bounds) - : mBounds(bounds) { - clear(); - } - ~DeferredDisplayList() { clear(); } - - enum OpBatchId { - kOpBatch_None = 0, // Don't batch - kOpBatch_Bitmap, - kOpBatch_Patch, - kOpBatch_AlphaVertices, - kOpBatch_Vertices, - kOpBatch_AlphaMaskTexture, - kOpBatch_Text, - kOpBatch_ColorText, - - kOpBatch_Count, // Add other batch ids before this - }; - - bool isEmpty() { return mBatches.empty(); } - - /** - * Plays back all of the draw ops recorded into batches to the renderer. - * Adjusts the state of the renderer as necessary, and restores it when complete - */ - void flush(OpenGLRenderer& renderer, Rect& dirty); - - void addClip(OpenGLRenderer& renderer, ClipOp* op); - void addSaveLayer(OpenGLRenderer& renderer, SaveLayerOp* op, int newSaveCount); - void addSave(OpenGLRenderer& renderer, SaveOp* op, int newSaveCount); - void addRestoreToCount(OpenGLRenderer& renderer, StateOp* op, int newSaveCount); - - /** - * Add a draw op into the DeferredDisplayList, reordering as needed (for performance) if - * disallowReorder is false, respecting draw order when overlaps occur. - */ - void addDrawOp(OpenGLRenderer& renderer, DrawOp* op); - -private: - DeferredDisplayList(const DeferredDisplayList& other); // disallow copy - - DeferredDisplayState* createState() { - return mAllocator.create_trivial<DeferredDisplayState>(); - } - - void tryRecycleState(DeferredDisplayState* state) { - mAllocator.rewindIfLastAlloc(state); - } - - /** - * Resets the batching back-pointers, creating a barrier in the operation stream so that no ops - * added in the future will be inserted into a batch that already exist. - */ - void resetBatchingState(); - - void clear(); - - void storeStateOpBarrier(OpenGLRenderer& renderer, StateOp* op); - void storeRestoreToCountBarrier(OpenGLRenderer& renderer, StateOp* op, int newSaveCount); - - bool recordingComplexClip() const { return mComplexClipStackStart >= 0; } - - int getStateOpDeferFlags() const; - int getDrawOpDeferFlags() const; - - void discardDrawingBatches(const unsigned int maxIndex); - - // layer space bounds of rendering - Rect mBounds; - - /** - * At defer time, stores the *defer time* savecount of save/saveLayer ops that were deferred, so - * that when an associated restoreToCount is deferred, it can be recorded as a - * RestoreToCountBatch - */ - std::vector<int> mSaveStack; - int mComplexClipStackStart; - - std::vector<Batch*> mBatches; - - // Maps batch ids to the most recent *non-merging* batch of that id - Batch* mBatchLookup[kOpBatch_Count]; - - // Points to the index after the most recent barrier - int mEarliestBatchIndex; - - // Points to the first index that may contain a pure drawing batch - int mEarliestUnclearedIndex; - - /** - * Maps the mergeid_t returned by an op's getMergeId() to the most recently seen - * MergingDrawBatch of that id. These ids are unique per draw type and guaranteed to not - * collide, which avoids the need to resolve mergeid collisions. - */ - std::unordered_map<mergeid_t, DrawBatch*> mMergingBatches[kOpBatch_Count]; - - LinearAllocator mAllocator; -}; - -/** - * Struct containing information that instructs the defer - */ -struct DeferInfo { -public: - DeferInfo() : - batchId(DeferredDisplayList::kOpBatch_None), - mergeId((mergeid_t) -1), - mergeable(false), - opaqueOverBounds(false) { - }; - - int batchId; - mergeid_t mergeId; - bool mergeable; - bool opaqueOverBounds; // opaque over bounds in DeferredDisplayState - can skip ops below -}; - -}; // namespace uirenderer -}; // namespace android - -#endif // ANDROID_HWUI_DEFERRED_DISPLAY_LIST_H diff --git a/libs/hwui/DeferredLayerUpdater.cpp b/libs/hwui/DeferredLayerUpdater.cpp index f833a5405a5c..0ae50e96fc39 100644 --- a/libs/hwui/DeferredLayerUpdater.cpp +++ b/libs/hwui/DeferredLayerUpdater.cpp @@ -15,11 +15,11 @@ */ #include "DeferredLayerUpdater.h" -#include "OpenGLRenderer.h" - -#include "LayerRenderer.h" +#include "GlLayer.h" +#include "VkLayer.h" #include "renderthread/EglManager.h" #include "renderthread/RenderTask.h" +#include "utils/PaintUtils.h" namespace android { namespace uirenderer { @@ -29,10 +29,9 @@ DeferredLayerUpdater::DeferredLayerUpdater(Layer* layer) , mTransform(nullptr) , mNeedsGLContextAttach(false) , mUpdateTexImage(false) - , mLayer(layer) - , mCaches(Caches::getInstance()) { - mWidth = mLayer->layer.getWidth(); - mHeight = mLayer->layer.getHeight(); + , mLayer(layer) { + mWidth = mLayer->getWidth(); + mHeight = mLayer->getHeight(); mBlend = mLayer->isBlend(); mColorFilter = SkSafeRef(mLayer->getColorFilter()); mAlpha = mLayer->getAlpha(); @@ -48,24 +47,33 @@ DeferredLayerUpdater::~DeferredLayerUpdater() { void DeferredLayerUpdater::setPaint(const SkPaint* paint) { mAlpha = PaintUtils::getAlphaDirect(paint); - mMode = PaintUtils::getXfermodeDirect(paint); + mMode = PaintUtils::getBlendModeDirect(paint); SkColorFilter* colorFilter = (paint) ? paint->getColorFilter() : nullptr; SkRefCnt_SafeAssign(mColorFilter, colorFilter); } void DeferredLayerUpdater::apply() { - // These properties are applied the same to both layer types mLayer->setColorFilter(mColorFilter); mLayer->setAlpha(mAlpha, mMode); if (mSurfaceTexture.get()) { - if (mNeedsGLContextAttach) { - mNeedsGLContextAttach = false; - mSurfaceTexture->attachToContext(mLayer->getTextureId()); - } - if (mUpdateTexImage) { - mUpdateTexImage = false; - doUpdateTexImage(); + if (mLayer->getApi() == Layer::Api::Vulkan) { + if (mUpdateTexImage) { + mUpdateTexImage = false; + doUpdateVkTexImage(); + } + } else { + LOG_ALWAYS_FATAL_IF(mLayer->getApi() != Layer::Api::OpenGL, + "apply surfaceTexture with non GL backend %x, GL %x, VK %x", + mLayer->getApi(), Layer::Api::OpenGL, Layer::Api::Vulkan); + if (mNeedsGLContextAttach) { + mNeedsGLContextAttach = false; + mSurfaceTexture->attachToContext(static_cast<GlLayer*>(mLayer)->getTextureId()); + } + if (mUpdateTexImage) { + mUpdateTexImage = false; + doUpdateTexImage(); + } } if (mTransform) { mLayer->getTransform().load(*mTransform); @@ -75,6 +83,9 @@ 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); if (mSurfaceTexture->updateTexImage() == NO_ERROR) { float transform[16]; @@ -110,20 +121,51 @@ void DeferredLayerUpdater::doUpdateTexImage() { LOG_ALWAYS_FATAL_IF(renderTarget != GL_TEXTURE_2D && renderTarget != GL_TEXTURE_EXTERNAL_OES, "doUpdateTexImage target %x, 2d %x, EXT %x", renderTarget, GL_TEXTURE_2D, GL_TEXTURE_EXTERNAL_OES); - LayerRenderer::updateTextureLayer(mLayer, mWidth, mHeight, - !mBlend, forceFilter, renderTarget, transform); + updateLayer(forceFilter, renderTarget, transform); + } +} + +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); + + static const mat4 identityMatrix; + updateLayer(false, GL_NONE, identityMatrix.data); + + VkLayer* vkLayer = static_cast<VkLayer*>(mLayer); + vkLayer->updateTexture(); +} + +void DeferredLayerUpdater::updateLayer(bool forceFilter, GLenum renderTarget, + const float* textureTransform) { + mLayer->setBlend(mBlend); + mLayer->setForceFilter(forceFilter); + mLayer->setSize(mWidth, mHeight); + mLayer->getTexTransform().load(textureTransform); + + if (mLayer->getApi() == Layer::Api::OpenGL) { + GlLayer* glLayer = static_cast<GlLayer*>(mLayer); + if (renderTarget != glLayer->getRenderTarget()) { + glLayer->setRenderTarget(renderTarget); + glLayer->bindTexture(); + glLayer->setFilter(GL_NEAREST, false, true); + glLayer->setWrap(GL_CLAMP_TO_EDGE, false, true); + } } } void DeferredLayerUpdater::detachSurfaceTexture() { if (mSurfaceTexture.get()) { - status_t err = mSurfaceTexture->detachFromContext(); - if (err != 0) { - // TODO: Elevate to fatal exception - ALOGE("Failed to detach SurfaceTexture from context %d", err); + if (mLayer->getApi() == Layer::Api::OpenGL) { + status_t err = mSurfaceTexture->detachFromContext(); + if (err != 0) { + // TODO: Elevate to fatal exception + ALOGE("Failed to detach SurfaceTexture from context %d", err); + } + static_cast<GlLayer*>(mLayer)->clearTexture(); } mSurfaceTexture = nullptr; - mLayer->clearTexture(); } } diff --git a/libs/hwui/DeferredLayerUpdater.h b/libs/hwui/DeferredLayerUpdater.h index 40058223fb48..3814be2cbec4 100644 --- a/libs/hwui/DeferredLayerUpdater.h +++ b/libs/hwui/DeferredLayerUpdater.h @@ -13,8 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#ifndef DEFERREDLAYERUPDATE_H_ -#define DEFERREDLAYERUPDATE_H_ + +#pragma once #include <cutils/compiler.h> #include <gui/GLConsumer.h> @@ -22,6 +22,9 @@ #include <SkMatrix.h> #include <utils/StrongPointer.h> +#include <GLES2/gl2.h> +#include <GLES2/gl2ext.h> + #include "Layer.h" #include "Rect.h" #include "renderthread/RenderThread.h" @@ -92,6 +95,8 @@ public: void detachSurfaceTexture(); + void updateLayer(bool forceFilter, GLenum renderTarget, const float* textureTransform); + private: // Generic properties int mWidth; @@ -99,20 +104,17 @@ private: bool mBlend; SkColorFilter* mColorFilter; int mAlpha; - SkXfermode::Mode mMode; - + SkBlendMode mMode; sp<GLConsumer> mSurfaceTexture; SkMatrix* mTransform; bool mNeedsGLContextAttach; bool mUpdateTexImage; Layer* mLayer; - Caches& mCaches; void doUpdateTexImage(); + void doUpdateVkTexImage(); }; } /* namespace uirenderer */ } /* namespace android */ - -#endif /* DEFERREDLAYERUPDATE_H_ */ diff --git a/libs/hwui/DeviceInfo.cpp b/libs/hwui/DeviceInfo.cpp index ff3ee2229b02..d180ba51b304 100644 --- a/libs/hwui/DeviceInfo.cpp +++ b/libs/hwui/DeviceInfo.cpp @@ -43,6 +43,13 @@ void DeviceInfo::initialize() { }); } +void DeviceInfo::initialize(int maxTextureSize) { + std::call_once(sInitializedFlag, [maxTextureSize]() { + sDeviceInfo = new DeviceInfo(); + sDeviceInfo->mMaxTextureSize = maxTextureSize; + }); +} + void DeviceInfo::load() { glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize); } diff --git a/libs/hwui/DeviceInfo.h b/libs/hwui/DeviceInfo.h index f576a4f48021..aff84b02d85a 100644 --- a/libs/hwui/DeviceInfo.h +++ b/libs/hwui/DeviceInfo.h @@ -16,7 +16,6 @@ #ifndef DEVICEINFO_H #define DEVICEINFO_H -#include "Extensions.h" #include "utils/Macros.h" namespace android { @@ -33,8 +32,7 @@ public: // only call this after GL has been initialized, or at any point if compiled // with HWUI_NULL_GPU static void initialize(); - - const Extensions& extensions() const { return mExtensions; } + static void initialize(int maxTextureSize); int maxTextureSize() const { return mMaxTextureSize; } @@ -44,7 +42,6 @@ private: void load(); - Extensions mExtensions; int mMaxTextureSize; }; diff --git a/libs/hwui/DisplayList.cpp b/libs/hwui/DisplayList.cpp index 28be05c52cfc..5e4a7f7a8d2f 100644 --- a/libs/hwui/DisplayList.cpp +++ b/libs/hwui/DisplayList.cpp @@ -19,15 +19,13 @@ #include <utils/Trace.h> +#include "DamageAccumulator.h" #include "Debug.h" #include "DisplayList.h" -#include "RenderNode.h" - -#if HWUI_NEW_OPS #include "RecordedOp.h" -#else -#include "DisplayListOp.h" -#endif +#include "RenderNode.h" +#include "VectorDrawable.h" +#include "renderthread/CanvasContext.h" namespace android { namespace uirenderer { @@ -45,8 +43,7 @@ DisplayList::DisplayList() , regions(stdAllocator) , referenceHolders(stdAllocator) , functors(stdAllocator) - , vectorDrawables(stdAllocator) - , hasDrawOps(false) { + , vectorDrawables(stdAllocator) { } DisplayList::~DisplayList() { @@ -92,5 +89,43 @@ size_t DisplayList::addChild(NodeOpType* op) { return index; } +void DisplayList::syncContents() { + for (auto& iter : functors) { + (*iter.functor)(DrawGlInfo::kModeSync, nullptr); + } + for (auto& vectorDrawable : vectorDrawables) { + vectorDrawable->syncProperties(); + } +} + +void DisplayList::updateChildren(std::function<void(RenderNode*)> updateFn) { + for (auto&& child : children) { + updateFn(child->renderNode); + } +} + +bool DisplayList::prepareListAndChildren(TreeInfo& info, bool functorsNeedLayer, + std::function<void(RenderNode*, 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; + childFn(childNode, info, childFunctorsNeedLayer); + info.damageAccumulator->popTransform(); + } + + bool isDirty = false; + for (auto& vectorDrawable : vectorDrawables) { + // If any vector drawable in the display list needs update, damage the node. + if (vectorDrawable->isDirty()) { + isDirty = true; + } + vectorDrawable->setPropertyChangeWillBeConsumed(true); + } + return isDirty; +} + }; // namespace uirenderer }; // namespace android diff --git a/libs/hwui/DisplayList.h b/libs/hwui/DisplayList.h index ccf71c6d360b..cab092ffc34c 100644 --- a/libs/hwui/DisplayList.h +++ b/libs/hwui/DisplayList.h @@ -14,10 +14,10 @@ * limitations under the License. */ -#ifndef ANDROID_HWUI_DISPLAY_LIST_H -#define ANDROID_HWUI_DISPLAY_LIST_H +#pragma once #include <SkCamera.h> +#include <SkDrawable.h> #include <SkMatrix.h> #include <private/hwui/DrawGlInfo.h> @@ -34,10 +34,11 @@ #include "Debug.h" #include "CanvasProperty.h" -#include "DeferredDisplayList.h" #include "GlFunctorLifecycleListener.h" #include "Matrix.h" #include "RenderProperties.h" +#include "TreeInfo.h" +#include "hwui/Bitmap.h" #include <vector> @@ -49,72 +50,20 @@ class SkRegion; namespace android { namespace uirenderer { -class DeferredDisplayList; -class DisplayListOp; -class DisplayListCanvas; -class OpenGLRenderer; class Rect; class Layer; -#if HWUI_NEW_OPS struct RecordedOp; struct RenderNodeOp; typedef RecordedOp BaseOpType; typedef RenderNodeOp NodeOpType; -#else -class DrawRenderNodeOp; - -typedef DisplayListOp BaseOpType; -typedef DrawRenderNodeOp NodeOpType; -#endif namespace VectorDrawable { class Tree; }; typedef uirenderer::VectorDrawable::Tree VectorDrawableRoot; -/** - * Holds data used in the playback a tree of DisplayLists. - */ -struct PlaybackStateStruct { -protected: - PlaybackStateStruct(OpenGLRenderer& renderer, int replayFlags, LinearAllocator* allocator) - : mRenderer(renderer) - , mReplayFlags(replayFlags) - , mAllocator(allocator) {} - -public: - OpenGLRenderer& mRenderer; - const int mReplayFlags; - - // Allocator with the lifetime of a single frame. replay uses an Allocator owned by the struct, - // while defer shares the DeferredDisplayList's Allocator - // TODO: move this allocator to be owned by object with clear frame lifecycle - LinearAllocator * const mAllocator; - - SkPath* allocPathForFrame() { - return mRenderer.allocPathForFrame(); - } -}; - -struct DeferStateStruct : public PlaybackStateStruct { - DeferStateStruct(DeferredDisplayList& deferredList, OpenGLRenderer& renderer, int replayFlags) - : PlaybackStateStruct(renderer, replayFlags, &(deferredList.mAllocator)), - mDeferredList(deferredList) {} - - DeferredDisplayList& mDeferredList; -}; - -struct ReplayStateStruct : public PlaybackStateStruct { - ReplayStateStruct(OpenGLRenderer& renderer, Rect& dirty, int replayFlags) - : PlaybackStateStruct(renderer, replayFlags, &mReplayAllocator), - mDirty(dirty) {} - - Rect& mDirty; - LinearAllocator mReplayAllocator; -}; - struct FunctorContainer { Functor* functor; GlFunctorLifecycleListener* listener; @@ -124,7 +73,6 @@ struct FunctorContainer { * Data structure that holds the list of commands used in display list stream */ class DisplayList { - friend class DisplayListCanvas; friend class RecordingCanvas; public: struct Chunk { @@ -138,13 +86,13 @@ public: // whether children with non-zero Z in the chunk should be reordered bool reorderChildren; -#if HWUI_NEW_OPS + + // clip at the beginning of a reorder section, applied to reordered children const ClipBase* reorderClip; -#endif }; DisplayList(); - ~DisplayList(); + virtual ~DisplayList(); // index of DisplayListOp restore, after which projected descendants should be drawn int projectionReceiveIndex; @@ -154,9 +102,7 @@ public: const LsaVector<NodeOpType*>& getChildren() const { return children; } - const LsaVector<const SkBitmap*>& getBitmapResources() const { return bitmapResources; } - const LsaVector<FunctorContainer>& getFunctors() const { return functors; } - const LsaVector<VectorDrawableRoot*>& getVectorDrawables() { return vectorDrawables; } + const LsaVector<sk_sp<Bitmap>>& getBitmapResources() const { return bitmapResources; } size_t addChild(NodeOpType* childOp); @@ -168,19 +114,26 @@ public: size_t getUsedSize() { return allocator.usedSize(); } - bool isEmpty() { -#if HWUI_NEW_OPS - return ops.empty(); -#else - return !hasDrawOps; -#endif + + virtual bool isEmpty() const { return ops.empty(); } + virtual bool hasFunctor() const { return !functors.empty(); } + virtual bool hasVectorDrawables() const { return !vectorDrawables.empty(); } + virtual bool isSkiaDL() const { return false; } + virtual bool reuseDisplayList(RenderNode* node, renderthread::CanvasContext* context) { + return false; } -private: + virtual void syncContents(); + virtual void updateChildren(std::function<void(RenderNode*)> updateFn); + virtual bool prepareListAndChildren(TreeInfo& info, bool functorsNeedLayer, + std::function<void(RenderNode*, TreeInfo&, bool)> childFn); + +protected: // allocator into which all ops and LsaVector arrays allocated LinearAllocator allocator; LinearStdAllocator<void*> stdAllocator; +private: LsaVector<Chunk> chunks; LsaVector<BaseOpType*> ops; @@ -188,7 +141,7 @@ private: LsaVector<NodeOpType*> children; // Resources - Skia objects + 9 patches referred to by this DisplayList - LsaVector<const SkBitmap*> bitmapResources; + LsaVector<sk_sp<Bitmap>> bitmapResources; LsaVector<const SkPath*> pathResources; LsaVector<const Res_png_9patch*> patchResources; LsaVector<std::unique_ptr<const SkPaint>> paints; @@ -203,12 +156,8 @@ private: // gets special treatment exclusive for webview. LsaVector<VectorDrawableRoot*> vectorDrawables; - bool hasDrawOps; // only used if !HWUI_NEW_OPS - void cleanupResources(); }; }; // namespace uirenderer }; // namespace android - -#endif // ANDROID_HWUI_OPENGL_RENDERER_H diff --git a/libs/hwui/DisplayListCanvas.cpp b/libs/hwui/DisplayListCanvas.cpp deleted file mode 100644 index bec662959f91..000000000000 --- a/libs/hwui/DisplayListCanvas.cpp +++ /dev/null @@ -1,597 +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. - */ - -#include "DisplayListCanvas.h" - -#include "DeferredDisplayList.h" -#include "DeferredLayerUpdater.h" -#include "DisplayListOp.h" -#include "ResourceCache.h" -#include "RenderNode.h" -#include "VectorDrawable.h" -#include "utils/PaintUtils.h" - -#include <SkCamera.h> -#include <SkCanvas.h> - -#include <private/hwui/DrawGlInfo.h> - -namespace android { -namespace uirenderer { - -DisplayListCanvas::DisplayListCanvas(int width, int height) - : mState(*this) - , mResourceCache(ResourceCache::getInstance()) - , mDisplayList(nullptr) - , mTranslateX(0.0f) - , mTranslateY(0.0f) - , mHasDeferredTranslate(false) - , mDeferredBarrierType(kBarrier_None) - , mHighContrastText(false) - , mRestoreSaveCount(-1) { - resetRecording(width, height); -} - -DisplayListCanvas::~DisplayListCanvas() { - LOG_ALWAYS_FATAL_IF(mDisplayList, - "Destroyed a DisplayListCanvas during a record!"); -} - -void DisplayListCanvas::resetRecording(int width, int height) { - LOG_ALWAYS_FATAL_IF(mDisplayList, - "prepareDirty called a second time during a recording!"); - mDisplayList = new DisplayList(); - - mState.initializeSaveStack(width, height, - 0, 0, width, height, Vector3()); - - mDeferredBarrierType = kBarrier_InOrder; - mState.setDirtyClip(false); - mRestoreSaveCount = -1; -} - - -/////////////////////////////////////////////////////////////////////////////// -// Operations -/////////////////////////////////////////////////////////////////////////////// - -DisplayList* DisplayListCanvas::finishRecording() { - flushRestoreToCount(); - flushTranslate(); - - mPaintMap.clear(); - mRegionMap.clear(); - mPathMap.clear(); - DisplayList* displayList = mDisplayList; - mDisplayList = nullptr; - mSkiaCanvasProxy.reset(nullptr); - return displayList; -} - -void DisplayListCanvas::callDrawGLFunction(Functor* functor, - GlFunctorLifecycleListener* listener) { - addDrawOp(new (alloc()) DrawFunctorOp(functor)); - mDisplayList->functors.push_back({functor, listener}); - mDisplayList->ref(listener); -} - -SkCanvas* DisplayListCanvas::asSkCanvas() { - LOG_ALWAYS_FATAL_IF(!mDisplayList, - "attempting to get an SkCanvas when we are not recording!"); - if (!mSkiaCanvasProxy) { - mSkiaCanvasProxy.reset(new SkiaCanvasProxy(this)); - } - - // SkCanvas instances default to identity transform, but should inherit - // the state of this Canvas; if this code was in the SkiaCanvasProxy - // constructor, we couldn't cache mSkiaCanvasProxy. - SkMatrix parentTransform; - getMatrix(&parentTransform); - mSkiaCanvasProxy.get()->setMatrix(parentTransform); - - return mSkiaCanvasProxy.get(); -} - -int DisplayListCanvas::save(SaveFlags::Flags flags) { - addStateOp(new (alloc()) SaveOp((int) flags)); - return mState.save((int) flags); -} - -void DisplayListCanvas::restore() { - if (mRestoreSaveCount < 0) { - restoreToCount(getSaveCount() - 1); - return; - } - - mRestoreSaveCount--; - flushTranslate(); - mState.restore(); -} - -void DisplayListCanvas::restoreToCount(int saveCount) { - mRestoreSaveCount = saveCount; - flushTranslate(); - mState.restoreToCount(saveCount); -} - -int DisplayListCanvas::saveLayer(float left, float top, float right, float bottom, - const SkPaint* paint, SaveFlags::Flags flags) { - // force matrix/clip isolation for layer - flags |= SaveFlags::MatrixClip; - - paint = refPaint(paint); - addStateOp(new (alloc()) SaveLayerOp(left, top, right, bottom, paint, (int) flags)); - return mState.save((int) flags); -} - -void DisplayListCanvas::translate(float dx, float dy) { - if (dx == 0.0f && dy == 0.0f) return; - - mHasDeferredTranslate = true; - mTranslateX += dx; - mTranslateY += dy; - flushRestoreToCount(); - mState.translate(dx, dy, 0.0f); -} - -void DisplayListCanvas::rotate(float degrees) { - if (degrees == 0.0f) return; - - addStateOp(new (alloc()) RotateOp(degrees)); - mState.rotate(degrees); -} - -void DisplayListCanvas::scale(float sx, float sy) { - if (sx == 1.0f && sy == 1.0f) return; - - addStateOp(new (alloc()) ScaleOp(sx, sy)); - mState.scale(sx, sy); -} - -void DisplayListCanvas::skew(float sx, float sy) { - addStateOp(new (alloc()) SkewOp(sx, sy)); - mState.skew(sx, sy); -} - -void DisplayListCanvas::setMatrix(const SkMatrix& matrix) { - addStateOp(new (alloc()) SetMatrixOp(matrix)); - mState.setMatrix(matrix); -} - -void DisplayListCanvas::concat(const SkMatrix& matrix) { - addStateOp(new (alloc()) ConcatMatrixOp(matrix)); - mState.concatMatrix(matrix); -} - -bool DisplayListCanvas::getClipBounds(SkRect* outRect) const { - Rect bounds = mState.getLocalClipBounds(); - *outRect = SkRect::MakeLTRB(bounds.left, bounds.top, bounds.right, bounds.bottom); - return !(outRect->isEmpty()); -} - -bool DisplayListCanvas::quickRejectRect(float left, float top, float right, float bottom) const { - return mState.quickRejectConservative(left, top, right, bottom); -} - -bool DisplayListCanvas::quickRejectPath(const SkPath& path) const { - SkRect bounds = path.getBounds(); - return mState.quickRejectConservative(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom); -} - - -bool DisplayListCanvas::clipRect(float left, float top, float right, float bottom, - SkRegion::Op op) { - addStateOp(new (alloc()) ClipRectOp(left, top, right, bottom, op)); - return mState.clipRect(left, top, right, bottom, op); -} - -bool DisplayListCanvas::clipPath(const SkPath* path, SkRegion::Op op) { - path = refPath(path); - addStateOp(new (alloc()) ClipPathOp(path, op)); - return mState.clipPath(path, op); -} - -bool DisplayListCanvas::clipRegion(const SkRegion* region, SkRegion::Op op) { - region = refRegion(region); - addStateOp(new (alloc()) ClipRegionOp(region, op)); - return mState.clipRegion(region, op); -} - -void DisplayListCanvas::drawRenderNode(RenderNode* renderNode) { - LOG_ALWAYS_FATAL_IF(!renderNode, "missing rendernode"); - DrawRenderNodeOp* op = new (alloc()) DrawRenderNodeOp( - renderNode, - *mState.currentTransform(), - mState.clipIsSimple()); - addRenderNodeOp(op); -} - -void DisplayListCanvas::drawLayer(DeferredLayerUpdater* layerHandle) { - // We ref the DeferredLayerUpdater due to its thread-safe ref-counting - // semantics. - mDisplayList->ref(layerHandle); - addDrawOp(new (alloc()) DrawLayerOp(layerHandle->backingLayer())); -} - -void DisplayListCanvas::drawBitmap(const SkBitmap* bitmap, const SkPaint* paint) { - bitmap = refBitmap(*bitmap); - paint = refPaint(paint); - - addDrawOp(new (alloc()) DrawBitmapOp(bitmap, paint)); -} - -void DisplayListCanvas::drawBitmap(const SkBitmap& bitmap, float left, float top, - const SkPaint* paint) { - save(SaveFlags::Matrix); - translate(left, top); - drawBitmap(&bitmap, paint); - restore(); -} - -void DisplayListCanvas::drawBitmap(const SkBitmap& 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())) { - // 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); - } else { - save(SaveFlags::Matrix); - concat(matrix); - drawBitmap(&bitmap, paint); - restore(); - } -} - -void DisplayListCanvas::drawBitmap(const SkBitmap& 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); - drawBitmap(&bitmap, paint); - restore(); - } else { - paint = refPaint(paint); - - if (paint && paint->getShader()) { - float scaleX = (dstRight - dstLeft) / (srcRight - srcLeft); - float scaleY = (dstBottom - dstTop) / (srcBottom - srcTop); - if (!MathUtils::areEqual(scaleX, 1.0f) || !MathUtils::areEqual(scaleY, 1.0f)) { - // Apply the scale transform on the canvas, so that the shader - // effectively calculates positions relative to src rect space - - save(SaveFlags::Matrix); - translate(dstLeft, dstTop); - scale(scaleX, scaleY); - - dstLeft = 0.0f; - dstTop = 0.0f; - dstRight = srcRight - srcLeft; - dstBottom = srcBottom - srcTop; - - addDrawOp(new (alloc()) DrawBitmapRectOp(refBitmap(bitmap), - srcLeft, srcTop, srcRight, srcBottom, - dstLeft, dstTop, dstRight, dstBottom, paint)); - restore(); - return; - } - } - - addDrawOp(new (alloc()) DrawBitmapRectOp(refBitmap(bitmap), - srcLeft, srcTop, srcRight, srcBottom, - dstLeft, dstTop, dstRight, dstBottom, paint)); - } -} - -void DisplayListCanvas::drawBitmapMesh(const SkBitmap& bitmap, int meshWidth, int meshHeight, - const float* vertices, const int* colors, const SkPaint* paint) { - int vertexCount = (meshWidth + 1) * (meshHeight + 1); - vertices = refBuffer<float>(vertices, vertexCount * 2); // 2 floats per vertex - paint = refPaint(paint); - colors = refBuffer<int>(colors, vertexCount); // 1 color per vertex - - addDrawOp(new (alloc()) DrawBitmapMeshOp(refBitmap(bitmap), meshWidth, meshHeight, - vertices, colors, paint)); -} - -void DisplayListCanvas::drawNinePatch(const SkBitmap& bitmap, const Res_png_9patch& patch, - float dstLeft, float dstTop, float dstRight, float dstBottom, const SkPaint* paint) { - const SkBitmap* bitmapPtr = refBitmap(bitmap); - const Res_png_9patch* patchPtr = refPatch(&patch); - paint = refPaint(paint); - - addDrawOp(new (alloc()) DrawPatchOp(bitmapPtr, patchPtr, - dstLeft, dstTop, dstRight, dstBottom, paint)); -} - -void DisplayListCanvas::drawColor(int color, SkXfermode::Mode mode) { - addDrawOp(new (alloc()) DrawColorOp(color, mode)); -} - -void DisplayListCanvas::drawPaint(const SkPaint& paint) { - SkRect bounds; - if (getClipBounds(&bounds)) { - drawRect(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, paint); - } -} - - -void DisplayListCanvas::drawRect(float left, float top, float right, float bottom, - const SkPaint& paint) { - addDrawOp(new (alloc()) DrawRectOp(left, top, right, bottom, refPaint(&paint))); -} - -void DisplayListCanvas::drawRoundRect(float left, float top, float right, float bottom, - float rx, float ry, const SkPaint& paint) { - addDrawOp(new (alloc()) DrawRoundRectOp(left, top, right, bottom, rx, ry, refPaint(&paint))); -} - -void DisplayListCanvas::drawRoundRect( - CanvasPropertyPrimitive* left, CanvasPropertyPrimitive* top, - CanvasPropertyPrimitive* right, CanvasPropertyPrimitive* bottom, - CanvasPropertyPrimitive* rx, CanvasPropertyPrimitive* ry, - CanvasPropertyPaint* paint) { - mDisplayList->ref(left); - mDisplayList->ref(top); - mDisplayList->ref(right); - mDisplayList->ref(bottom); - mDisplayList->ref(rx); - mDisplayList->ref(ry); - mDisplayList->ref(paint); - refBitmapsInShader(paint->value.getShader()); - addDrawOp(new (alloc()) DrawRoundRectPropsOp(&left->value, &top->value, - &right->value, &bottom->value, &rx->value, &ry->value, &paint->value)); -} - -void DisplayListCanvas::drawCircle(float x, float y, float radius, const SkPaint& paint) { - addDrawOp(new (alloc()) DrawCircleOp(x, y, radius, refPaint(&paint))); -} - -void DisplayListCanvas::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()); - addDrawOp(new (alloc()) DrawCirclePropsOp(&x->value, &y->value, - &radius->value, &paint->value)); -} - -void DisplayListCanvas::drawOval(float left, float top, float right, float bottom, - const SkPaint& paint) { - addDrawOp(new (alloc()) DrawOvalOp(left, top, right, bottom, refPaint(&paint))); -} - -void DisplayListCanvas::drawArc(float left, float top, float right, float bottom, - float startAngle, float sweepAngle, bool useCenter, const SkPaint& paint) { - if (fabs(sweepAngle) >= 360.0f) { - drawOval(left, top, right, bottom, paint); - } else { - addDrawOp(new (alloc()) DrawArcOp(left, top, right, bottom, - startAngle, sweepAngle, useCenter, refPaint(&paint))); - } -} - -void DisplayListCanvas::drawPath(const SkPath& path, const SkPaint& paint) { - addDrawOp(new (alloc()) DrawPathOp(refPath(&path), refPaint(&paint))); -} - -void DisplayListCanvas::drawLines(const float* points, int count, const SkPaint& paint) { - points = refBuffer<float>(points, count); - - addDrawOp(new (alloc()) DrawLinesOp(points, count, refPaint(&paint))); -} - -void DisplayListCanvas::drawPoints(const float* points, int count, const SkPaint& paint) { - points = refBuffer<float>(points, count); - - addDrawOp(new (alloc()) DrawPointsOp(points, count, refPaint(&paint))); -} - -void DisplayListCanvas::drawVectorDrawable(VectorDrawableRoot* tree) { - mDisplayList->ref(tree); - mDisplayList->vectorDrawables.push_back(tree); - addDrawOp(new (alloc()) DrawVectorDrawableOp(tree, tree->stagingProperties()->getBounds())); -} - -void DisplayListCanvas::drawGlyphsOnPath(const uint16_t* glyphs, int count, - const SkPath& path, float hOffset, float vOffset, const SkPaint& paint) { - if (!glyphs || count <= 0) return; - - int bytesCount = 2 * count; - DrawOp* op = new (alloc()) DrawTextOnPathOp(refBuffer<glyph_t>(glyphs, count), - bytesCount, count, refPath(&path), - hOffset, vOffset, refPaint(&paint)); - addDrawOp(op); -} - -void DisplayListCanvas::drawGlyphs(const uint16_t* glyphs, const float* positions, - int count, const SkPaint& paint, float x, float y, - float boundsLeft, float boundsTop, float boundsRight, float boundsBottom, - float totalAdvance) { - - if (!glyphs || count <= 0 || PaintUtils::paintWillNotDrawText(paint)) return; - - int bytesCount = count * 2; - positions = refBuffer<float>(positions, count * 2); - Rect bounds(boundsLeft, boundsTop, boundsRight, boundsBottom); - - DrawOp* op = new (alloc()) DrawTextOp(refBuffer<glyph_t>(glyphs, count), bytesCount, count, - x, y, positions, refPaint(&paint), totalAdvance, bounds); - addDrawOp(op); - drawTextDecorations(x, y, totalAdvance, paint); -} - -void DisplayListCanvas::drawRegion(const SkRegion& region, const SkPaint& paint) { - if (paint.getStyle() != SkPaint::kFill_Style || - (paint.isAntiAlias() && !mState.currentTransform()->isSimple())) { - SkRegion::Iterator it(region); - while (!it.done()) { - const SkIRect& r = it.rect(); - drawRect(r.fLeft, r.fTop, r.fRight, r.fBottom, paint); - it.next(); - } - } else { - int count = 0; - Vector<float> rects; - SkRegion::Iterator it(region); - while (!it.done()) { - const SkIRect& r = it.rect(); - rects.push(r.fLeft); - rects.push(r.fTop); - rects.push(r.fRight); - rects.push(r.fBottom); - count += 4; - it.next(); - } - drawRects(rects.array(), count, &paint); - } -} - -void DisplayListCanvas::drawRects(const float* rects, int count, const SkPaint* paint) { - if (count <= 0) return; - - rects = refBuffer<float>(rects, count); - paint = refPaint(paint); - addDrawOp(new (alloc()) DrawRectsOp(rects, count, paint)); -} - -void DisplayListCanvas::setDrawFilter(SkDrawFilter* filter) { - mDrawFilter.reset(SkSafeRef(filter)); -} - -void DisplayListCanvas::insertReorderBarrier(bool enableReorder) { - flushRestoreToCount(); - flushTranslate(); - mDeferredBarrierType = enableReorder ? kBarrier_OutOfOrder : kBarrier_InOrder; -} - -void DisplayListCanvas::flushRestoreToCount() { - if (mRestoreSaveCount >= 0) { - addOpAndUpdateChunk(new (alloc()) RestoreToCountOp(mRestoreSaveCount)); - mRestoreSaveCount = -1; - } -} - -void DisplayListCanvas::flushTranslate() { - if (mHasDeferredTranslate) { - if (mTranslateX != 0.0f || mTranslateY != 0.0f) { - addOpAndUpdateChunk(new (alloc()) TranslateOp(mTranslateX, mTranslateY)); - mTranslateX = mTranslateY = 0.0f; - } - mHasDeferredTranslate = false; - } -} - -size_t DisplayListCanvas::addOpAndUpdateChunk(DisplayListOp* op) { - int insertIndex = mDisplayList->ops.size(); -#if HWUI_NEW_OPS - LOG_ALWAYS_FATAL("unsupported"); -#else - mDisplayList->ops.push_back(op); -#endif - if (mDeferredBarrierType != kBarrier_None) { - // op is first in new chunk - mDisplayList->chunks.emplace_back(); - DisplayList::Chunk& newChunk = mDisplayList->chunks.back(); - newChunk.beginOpIndex = insertIndex; - newChunk.endOpIndex = insertIndex + 1; - newChunk.reorderChildren = (mDeferredBarrierType == kBarrier_OutOfOrder); - - int nextChildIndex = mDisplayList->children.size(); - newChunk.beginChildIndex = newChunk.endChildIndex = nextChildIndex; - mDeferredBarrierType = kBarrier_None; - } else { - // standard case - append to existing chunk - mDisplayList->chunks.back().endOpIndex = insertIndex + 1; - } - return insertIndex; -} - -size_t DisplayListCanvas::flushAndAddOp(DisplayListOp* op) { - flushRestoreToCount(); - flushTranslate(); - return addOpAndUpdateChunk(op); -} - -size_t DisplayListCanvas::addStateOp(StateOp* op) { - return flushAndAddOp(op); -} - -size_t DisplayListCanvas::addDrawOp(DrawOp* op) { - Rect localBounds; - if (op->getLocalBounds(localBounds)) { - bool rejected = quickRejectRect(localBounds.left, localBounds.top, - localBounds.right, localBounds.bottom); - op->setQuickRejected(rejected); - } - - mDisplayList->hasDrawOps = true; - return flushAndAddOp(op); -} - -size_t DisplayListCanvas::addRenderNodeOp(DrawRenderNodeOp* op) { - int opIndex = addDrawOp(op); -#if !HWUI_NEW_OPS - int childIndex = mDisplayList->addChild(op); - - // update the chunk's child indices - DisplayList::Chunk& chunk = mDisplayList->chunks.back(); - chunk.endChildIndex = childIndex + 1; - - if (op->renderNode->stagingProperties().isProjectionReceiver()) { - // use staging property, since recording on UI thread - mDisplayList->projectionReceiveIndex = opIndex; - } -#endif - return opIndex; -} - -void DisplayListCanvas::refBitmapsInShader(const SkShader* shader) { - if (!shader) return; - - // If this paint has an SkShader that has an SkBitmap add - // it to the bitmap pile - SkBitmap bitmap; - SkShader::TileMode xy[2]; - if (shader->isABitmap(&bitmap, nullptr, xy)) { - refBitmap(bitmap); - return; - } - SkShader::ComposeRec rec; - if (shader->asACompose(&rec)) { - refBitmapsInShader(rec.fShaderA); - refBitmapsInShader(rec.fShaderB); - return; - } -} - -}; // namespace uirenderer -}; // namespace android diff --git a/libs/hwui/DisplayListCanvas.h b/libs/hwui/DisplayListCanvas.h deleted file mode 100644 index 664f79e283b6..000000000000 --- a/libs/hwui/DisplayListCanvas.h +++ /dev/null @@ -1,359 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ANDROID_HWUI_DISPLAY_LIST_RENDERER_H -#define ANDROID_HWUI_DISPLAY_LIST_RENDERER_H - -#include "CanvasState.h" -#include "DisplayList.h" -#include "RenderNode.h" -#include "ResourceCache.h" -#include "SkiaCanvasProxy.h" -#include "hwui/Canvas.h" -#include "utils/Macros.h" - -#include <SkDrawFilter.h> -#include <SkMatrix.h> -#include <SkPaint.h> -#include <SkPath.h> -#include <SkRegion.h> -#include <SkTLazy.h> -#include <cutils/compiler.h> - -namespace android { -namespace uirenderer { - -/////////////////////////////////////////////////////////////////////////////// -// Defines -/////////////////////////////////////////////////////////////////////////////// - -// Debug -#if DEBUG_DISPLAY_LIST - #define DISPLAY_LIST_LOGD(...) ALOGD(__VA_ARGS__) -#else - #define DISPLAY_LIST_LOGD(...) -#endif - -/////////////////////////////////////////////////////////////////////////////// -// Display list -/////////////////////////////////////////////////////////////////////////////// - -class DeferredDisplayList; -class DeferredLayerUpdater; -class DisplayListOp; -class DrawOp; -class DrawRenderNodeOp; -class RenderNode; -class StateOp; - -/** - * Records drawing commands in a display list for later playback into an OpenGLRenderer. - */ -class ANDROID_API DisplayListCanvas: public Canvas, public CanvasStateClient { -public: - DisplayListCanvas(int width, int height); - virtual ~DisplayListCanvas(); - - virtual void resetRecording(int width, int height) override; - virtual WARN_UNUSED_RESULT DisplayList* finishRecording() override; - -// ---------------------------------------------------------------------------- -// HWUI Canvas state operations -// ---------------------------------------------------------------------------- - - virtual void insertReorderBarrier(bool enableReorder) override; - -// ---------------------------------------------------------------------------- -// HWUI Canvas draw operations -// ---------------------------------------------------------------------------- - - // Shapes - virtual void drawRoundRect(CanvasPropertyPrimitive* left, CanvasPropertyPrimitive* top, - CanvasPropertyPrimitive* right, CanvasPropertyPrimitive* bottom, - CanvasPropertyPrimitive* rx, CanvasPropertyPrimitive* ry, - CanvasPropertyPaint* paint) override; - virtual void drawCircle(CanvasPropertyPrimitive* x, CanvasPropertyPrimitive* y, - CanvasPropertyPrimitive* radius, CanvasPropertyPaint* paint) override; - -// ---------------------------------------------------------------------------- -// HWUI Canvas draw operations - special -// ---------------------------------------------------------------------------- - virtual void drawLayer(DeferredLayerUpdater* layerHandle) override; - virtual void drawRenderNode(RenderNode* renderNode) override; - virtual void callDrawGLFunction(Functor* functor, - GlFunctorLifecycleListener* listener) override; - -// ---------------------------------------------------------------------------- -// CanvasStateClient interface -// ---------------------------------------------------------------------------- - virtual void onViewportInitialized() override { } - virtual void onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) override { } - virtual GLuint getTargetFbo() const override { return -1; } - -// ---------------------------------------------------------------------------- -// android/graphics/Canvas interface -// ---------------------------------------------------------------------------- - virtual SkCanvas* asSkCanvas() override; - - virtual void setBitmap(const SkBitmap& bitmap) override { - LOG_ALWAYS_FATAL("DisplayListCanvas is not backed by a bitmap."); - } - - virtual bool isOpaque() override { return false; } - 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 -// ---------------------------------------------------------------------------- - // Save (layer) - virtual int getSaveCount() const override { return mState.getSaveCount(); } - 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 { - SkPaint paint; - paint.setAlpha(alpha); - return saveLayer(left, top, right, bottom, &paint, flags); - } - - // Matrix - virtual void getMatrix(SkMatrix* outMatrix) const override { mState.getMatrix(outMatrix); } - virtual void setMatrix(const SkMatrix& matrix) override; - - virtual void concat(const SkMatrix& matrix) override; - virtual void rotate(float degrees) override; - virtual void scale(float sx, float sy) override; - virtual void skew(float sx, float sy) override; - virtual void translate(float dx, float dy) override; - - // Clip - 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, SkRegion::Op op) override; - virtual bool clipPath(const SkPath* path, SkRegion::Op op) override; - virtual bool clipRegion(const SkRegion* region, SkRegion::Op op) override; - - // Misc - virtual SkDrawFilter* getDrawFilter() override { return mDrawFilter.get(); } - virtual void setDrawFilter(SkDrawFilter* filter) override; - -// ---------------------------------------------------------------------------- -// android/graphics/Canvas draw operations -// ---------------------------------------------------------------------------- - virtual void drawColor(int color, SkXfermode::Mode 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 }; - drawPoints(points, 2, paint); - } - 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 { - float points[4] = { startX, startY, stopX, stopY }; - drawLines(points, 4, paint); - } - 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; - 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 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 drawPath(const SkPath& path, const SkPaint& paint) override; - virtual void drawVertices(SkCanvas::VertexMode vertexMode, int vertexCount, - const float* verts, const float* tex, const int* colors, - const uint16_t* indices, int indexCount, const SkPaint& paint) override - { /* DisplayListCanvas does not support drawVertices(); ignore */ } - - // Bitmap-based - virtual void drawBitmap(const SkBitmap& bitmap, float left, float top, const SkPaint* paint) override; - virtual void drawBitmap(const SkBitmap& bitmap, const SkMatrix& matrix, - const SkPaint* paint) override; - virtual void drawBitmap(const SkBitmap& bitmap, float srcLeft, float srcTop, - float srcRight, float srcBottom, float dstLeft, float dstTop, - float dstRight, float dstBottom, const SkPaint* paint) override; - virtual void drawBitmapMesh(const SkBitmap& bitmap, int meshWidth, int meshHeight, - const float* vertices, const int* colors, const SkPaint* paint) override; - virtual void drawNinePatch(const SkBitmap& bitmap, const android::Res_png_9patch& chunk, - float dstLeft, float dstTop, float dstRight, float dstBottom, - const SkPaint* paint) override; - - virtual void drawVectorDrawable(VectorDrawableRoot* tree) override; - - // Text - virtual void drawGlyphs(const uint16_t* glyphs, const float* positions, int count, - const SkPaint& paint, float x, float y, float boundsLeft, float boundsTop, - float boundsRight, float boundsBottom, float totalAdvance) override; - virtual void drawGlyphsOnPath(const uint16_t* glyphs, int count, const SkPath& path, - float hOffset, float vOffset, const SkPaint& paint) override; - virtual bool drawTextAbsolutePos() const override { return false; } - -private: - - CanvasState mState; - std::unique_ptr<SkiaCanvasProxy> mSkiaCanvasProxy; - - enum DeferredBarrierType { - kBarrier_None, - kBarrier_InOrder, - kBarrier_OutOfOrder, - }; - - void drawBitmap(const SkBitmap* bitmap, const SkPaint* paint); - void drawRects(const float* rects, int count, const SkPaint* paint); - - void flushRestoreToCount(); - void flushTranslate(); - void flushReorderBarrier(); - - LinearAllocator& alloc() { return mDisplayList->allocator; } - - // Each method returns final index of op - size_t addOpAndUpdateChunk(DisplayListOp* op); - // flushes any deferred operations, and appends the op - size_t flushAndAddOp(DisplayListOp* op); - - size_t addStateOp(StateOp* op); - size_t addDrawOp(DrawOp* op); - size_t addRenderNodeOp(DrawRenderNodeOp* op); - - void refBitmapsInShader(const SkShader* shader); - - 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)); - memcpy(dstBuffer, srcBuffer, count * sizeof(T)); - return dstBuffer; - } - - inline const SkPath* refPath(const SkPath* path) { - if (!path) return nullptr; - - // The points/verbs within the path are refcounted so this copy operation - // is inexpensive and maintains the generationID of the original path. - const SkPath* cachedPath = new SkPath(*path); - mDisplayList->pathResources.push_back(cachedPath); - return cachedPath; - } - - inline const SkPaint* refPaint(const SkPaint* paint) { - if (!paint) return nullptr; - - // If there is a draw filter apply it here and store the modified paint - // so that we don't need to modify the paint every time we access it. - SkTLazy<SkPaint> filteredPaint; - if (mDrawFilter.get()) { - filteredPaint.set(*paint); - mDrawFilter->filter(filteredPaint.get(), SkDrawFilter::kPaint_Type); - paint = filteredPaint.get(); - } - - // compute the hash key for the paint and check the cache. - const uint32_t key = paint->getHash(); - const SkPaint* cachedPaint = mPaintMap.valueFor(key); - // In the unlikely event that 2 unique paints have the same hash we do a - // object equality check to ensure we don't erroneously dedup them. - if (cachedPaint == nullptr || *cachedPaint != *paint) { - cachedPaint = new SkPaint(*paint); - std::unique_ptr<const SkPaint> copy(cachedPaint); - mDisplayList->paints.push_back(std::move(copy)); - - // replaceValueFor() performs an add if the entry doesn't exist - mPaintMap.replaceValueFor(key, cachedPaint); - refBitmapsInShader(cachedPaint->getShader()); - } - - return cachedPaint; - } - - inline const SkRegion* refRegion(const SkRegion* region) { - if (!region) { - return region; - } - - const SkRegion* cachedRegion = mRegionMap.valueFor(region); - // TODO: Add generation ID to SkRegion - if (cachedRegion == nullptr) { - std::unique_ptr<const SkRegion> copy(new SkRegion(*region)); - cachedRegion = copy.get(); - mDisplayList->regions.push_back(std::move(copy)); - - // replaceValueFor() performs an add if the entry doesn't exist - mRegionMap.replaceValueFor(region, cachedRegion); - } - - return cachedRegion; - } - - inline const SkBitmap* refBitmap(const SkBitmap& bitmap) { - // Note that this assumes the bitmap is immutable. There are cases this won't handle - // correctly, such as creating the bitmap from scratch, drawing with it, changing its - // contents, and drawing again. The only fix would be to always copy it the first time, - // which doesn't seem worth the extra cycles for this unlikely case. - SkBitmap* localBitmap = alloc().create<SkBitmap>(bitmap); - mDisplayList->bitmapResources.push_back(localBitmap); - return localBitmap; - } - - inline const Res_png_9patch* refPatch(const Res_png_9patch* patch) { - mDisplayList->patchResources.push_back(patch); - mResourceCache.incrementRefcount(patch); - return patch; - } - - DefaultKeyedVector<uint32_t, const SkPaint*> mPaintMap; - DefaultKeyedVector<const SkPath*, const SkPath*> mPathMap; - DefaultKeyedVector<const SkRegion*, const SkRegion*> mRegionMap; - - ResourceCache& mResourceCache; - DisplayList* mDisplayList; - - float mTranslateX; - float mTranslateY; - bool mHasDeferredTranslate; - DeferredBarrierType mDeferredBarrierType; - bool mHighContrastText; - - int mRestoreSaveCount; - - SkAutoTUnref<SkDrawFilter> mDrawFilter; - - friend class RenderNode; - -}; // class DisplayListCanvas - -}; // namespace uirenderer -}; // namespace android - -#endif // ANDROID_HWUI_DISPLAY_LIST_RENDERER_H diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h deleted file mode 100644 index 51b987c7ed85..000000000000 --- a/libs/hwui/DisplayListOp.h +++ /dev/null @@ -1,1555 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ANDROID_HWUI_DISPLAY_OPERATION_H -#define ANDROID_HWUI_DISPLAY_OPERATION_H - -#include "OpenGLRenderer.h" -#include "AssetAtlas.h" -#include "DeferredDisplayList.h" -#include "DisplayListCanvas.h" -#include "GammaFontRenderer.h" -#include "Patch.h" -#include "RenderNode.h" -#include "renderstate/RenderState.h" -#include "UvMapper.h" -#include "utils/LinearAllocator.h" -#include "utils/PaintUtils.h" -#include "VectorDrawable.h" - -#include <algorithm> - -#include <SkColor.h> -#include <SkPath.h> -#include <SkPathOps.h> -#include <SkXfermode.h> - -#include <private/hwui/DrawGlInfo.h> - -// Use OP_LOG for logging with arglist, OP_LOGS if just printing char* -#define OP_LOGS(s) OP_LOG("%s", (s)) -#define OP_LOG(s, ...) ALOGD( "%*s" s, level * 2, "", __VA_ARGS__ ) - -namespace android { -namespace uirenderer { - -/** - * Structure for storing canvas operations when they are recorded into a DisplayList, so that they - * may be replayed to an OpenGLRenderer. - * - * To avoid individual memory allocations, DisplayListOps may only be allocated into a - * LinearAllocator's managed memory buffers. Each pointer held by a DisplayListOp is either a - * pointer into memory also allocated in the LinearAllocator (mostly for text and float buffers) or - * references a externally refcounted object (Sk... and Skia... objects). ~DisplayListOp() is - * never called as LinearAllocators are simply discarded, so no memory management should be done in - * this class. - */ -class DisplayListOp { -public: - // These objects should always be allocated with a LinearAllocator, and never destroyed/deleted. - // standard new() intentionally not implemented, and delete/deconstructor should never be used. - virtual ~DisplayListOp() { LOG_ALWAYS_FATAL("Destructor not supported"); } - static void operator delete(void* ptr) { LOG_ALWAYS_FATAL("delete not supported"); } - static void* operator new(size_t size) = delete; /** PURPOSELY OMITTED **/ - static void* operator new(size_t size, LinearAllocator& allocator) { - // FIXME: Quick hack to keep old pipeline working, delete this when - // we no longer need to support HWUI_NEWOPS := false - return allocator.alloc<char>(size); - } - - enum OpLogFlag { - kOpLogFlag_Recurse = 0x1, - kOpLogFlag_JSON = 0x2 // TODO: add? - }; - - virtual void defer(DeferStateStruct& deferStruct, int saveCount, int level, - bool useQuickReject) = 0; - - virtual void replay(ReplayStateStruct& replayStruct, int saveCount, int level, - bool useQuickReject) = 0; - - virtual void output(int level, uint32_t logFlags = 0) const = 0; - - // NOTE: it would be nice to declare constants and overriding the implementation in each op to - // point at the constants, but that seems to require a .cpp file - virtual const char* name() = 0; -}; - -class StateOp : public DisplayListOp { -public: - virtual void defer(DeferStateStruct& deferStruct, int saveCount, int level, - bool useQuickReject) override { - // default behavior only affects immediate, deferrable state, issue directly to renderer - applyState(deferStruct.mRenderer, saveCount); - } - - /** - * State operations are applied directly to the renderer, but can cause the deferred drawing op - * list to flush - */ - virtual void replay(ReplayStateStruct& replayStruct, int saveCount, int level, - bool useQuickReject) override { - applyState(replayStruct.mRenderer, saveCount); - } - - virtual void applyState(OpenGLRenderer& renderer, int saveCount) const = 0; -}; - -class DrawOp : public DisplayListOp { -friend class MergingDrawBatch; -public: - explicit DrawOp(const SkPaint* paint) - : mPaint(paint), mQuickRejected(false) {} - - virtual void defer(DeferStateStruct& deferStruct, int saveCount, int level, - bool useQuickReject) override { - if (mQuickRejected && CC_LIKELY(useQuickReject)) { - return; - } - - deferStruct.mDeferredList.addDrawOp(deferStruct.mRenderer, this); - } - - virtual void replay(ReplayStateStruct& replayStruct, int saveCount, int level, - bool useQuickReject) override { - if (mQuickRejected && CC_LIKELY(useQuickReject)) { - return; - } - - applyDraw(replayStruct.mRenderer, replayStruct.mDirty); - } - - virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) = 0; - - /** - * Draw multiple instances of an operation, must be overidden for operations that merge - * - * Currently guarantees certain similarities between ops (see MergingDrawBatch::canMergeWith), - * and pure translation transformations. Other guarantees of similarity should be enforced by - * reducing which operations are tagged as mergeable. - */ - virtual void multiDraw(OpenGLRenderer& renderer, Rect& dirty, - const std::vector<OpStatePair>& ops, const Rect& bounds) { - for (unsigned int i = 0; i < ops.size(); i++) { - renderer.restoreDisplayState(*(ops[i].state), true); - ops[i].op->applyDraw(renderer, dirty); - } - } - - /** - * When this method is invoked the state field is initialized to have the - * final rendering state. We can thus use it to process data as it will be - * used at draw time. - * - * Additionally, this method allows subclasses to provide defer-time preferences for batching - * and merging. - * - * if a subclass can set deferInfo.mergeable to true, it should implement multiDraw() - */ - virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo, - const DeferredDisplayState& state) {} - - /** - * Query the conservative, local bounds (unmapped) bounds of the op. - * - * returns true if bounds exist - */ - virtual bool getLocalBounds(Rect& localBounds) { - return false; - } - - // TODO: better refine localbounds usage - void setQuickRejected(bool quickRejected) { mQuickRejected = quickRejected; } - bool getQuickRejected() { return mQuickRejected; } - - virtual bool hasTextShadow() const { - return false; - } - - inline float strokeWidthOutset() { - // since anything AA stroke with less than 1.0 pixel width is drawn with an alpha-reduced - // 1.0 stroke, treat 1.0 as minimum. - - // TODO: it would be nice if this could take scale into account, but scale isn't stable - // since higher levels of the view hierarchy can change scale out from underneath it. - return std::max(mPaint->getStrokeWidth(), 1.0f) * 0.5f; - } - -protected: - // Helper method for determining op opaqueness. Assumes op fills its bounds in local - // coordinates, and that paint's alpha is used - inline bool isOpaqueOverBounds(const DeferredDisplayState& state) { - // ensure that local bounds cover mapped bounds - if (!state.mMatrix.isSimple()) return false; - - if (state.mRoundRectClipState) return false; - - // check state/paint for transparency - if (mPaint) { - if (mPaint->getAlpha() != 0xFF) { - return false; - } - if (mPaint->getShader() && !mPaint->getShader()->isOpaque()) { - return false; - } - if (PaintUtils::isBlendedColorFilter(mPaint->getColorFilter())) { - return false; - } - } - - if (state.mAlpha != 1.0f) return false; - - SkXfermode::Mode mode = PaintUtils::getXfermodeDirect(mPaint); - return (mode == SkXfermode::kSrcOver_Mode || - mode == SkXfermode::kSrc_Mode); - - } - - const SkPaint* mPaint; - bool mQuickRejected; -}; - -class DrawBoundedOp : public DrawOp { -public: - DrawBoundedOp(float left, float top, float right, float bottom, const SkPaint* paint) - : DrawOp(paint), mLocalBounds(left, top, right, bottom) {} - - DrawBoundedOp(const Rect& localBounds, const SkPaint* paint) - : DrawOp(paint), mLocalBounds(localBounds) {} - - // Calculates bounds as smallest rect encompassing all points - // NOTE: requires at least 1 vertex, and doesn't account for stroke size (should be handled in - // subclass' constructor) - DrawBoundedOp(const float* points, int count, const SkPaint* paint) - : DrawOp(paint), mLocalBounds(points[0], points[1], points[0], points[1]) { - for (int i = 2; i < count; i += 2) { - mLocalBounds.left = std::min(mLocalBounds.left, points[i]); - mLocalBounds.right = std::max(mLocalBounds.right, points[i]); - mLocalBounds.top = std::min(mLocalBounds.top, points[i + 1]); - mLocalBounds.bottom = std::max(mLocalBounds.bottom, points[i + 1]); - } - } - - // default empty constructor for bounds, to be overridden in child constructor body - explicit DrawBoundedOp(const SkPaint* paint): DrawOp(paint) { } - - virtual bool getLocalBounds(Rect& localBounds) override { - localBounds.set(mLocalBounds); - PaintUtils::TextShadow textShadow; - if (PaintUtils::getTextShadow(mPaint, &textShadow)) { - Rect shadow(mLocalBounds); - shadow.translate(textShadow.dx, textShadow.dx); - shadow.outset(textShadow.radius); - localBounds.unionWith(shadow); - } - return true; - } - -protected: - Rect mLocalBounds; // displayed area in LOCAL coord. doesn't incorporate stroke, so check paint -}; - -/////////////////////////////////////////////////////////////////////////////// -// STATE OPERATIONS - these may affect the state of the canvas/renderer, but do -// not directly draw or alter output -/////////////////////////////////////////////////////////////////////////////// - -class SaveOp : public StateOp { -public: - explicit SaveOp(int flags) - : mFlags(flags) {} - - virtual void defer(DeferStateStruct& deferStruct, int saveCount, int level, - bool useQuickReject) override { - int newSaveCount = deferStruct.mRenderer.save(mFlags); - deferStruct.mDeferredList.addSave(deferStruct.mRenderer, this, newSaveCount); - } - - virtual void applyState(OpenGLRenderer& renderer, int saveCount) const override { - renderer.save(mFlags); - } - - virtual void output(int level, uint32_t logFlags) const override { - OP_LOG("Save flags %x", mFlags); - } - - virtual const char* name() override { return "Save"; } - - int getFlags() const { return mFlags; } -private: - int mFlags; -}; - -class RestoreToCountOp : public StateOp { -public: - explicit RestoreToCountOp(int count) - : mCount(count) {} - - virtual void defer(DeferStateStruct& deferStruct, int saveCount, int level, - bool useQuickReject) override { - deferStruct.mDeferredList.addRestoreToCount(deferStruct.mRenderer, - this, saveCount + mCount); - deferStruct.mRenderer.restoreToCount(saveCount + mCount); - } - - virtual void applyState(OpenGLRenderer& renderer, int saveCount) const override { - renderer.restoreToCount(saveCount + mCount); - } - - virtual void output(int level, uint32_t logFlags) const override { - OP_LOG("Restore to count %d", mCount); - } - - virtual const char* name() override { return "RestoreToCount"; } - -private: - int mCount; -}; - -class SaveLayerOp : public StateOp { -public: - SaveLayerOp(float left, float top, float right, float bottom, int alpha, int flags) - : mArea(left, top, right, bottom) - , mPaint(&mCachedPaint) - , mFlags(flags) - , mConvexMask(nullptr) { - mCachedPaint.setAlpha(alpha); - } - - SaveLayerOp(float left, float top, float right, float bottom, const SkPaint* paint, int flags) - : mArea(left, top, right, bottom) - , mPaint(paint) - , mFlags(flags) - , mConvexMask(nullptr) - {} - - virtual void defer(DeferStateStruct& deferStruct, int saveCount, int level, - bool useQuickReject) override { - // NOTE: don't bother with actual saveLayer, instead issuing it at flush time - int newSaveCount = deferStruct.mRenderer.getSaveCount(); - deferStruct.mDeferredList.addSaveLayer(deferStruct.mRenderer, this, newSaveCount); - - // NOTE: don't issue full saveLayer, since that has side effects/is costly. instead just - // setup the snapshot for deferral, and re-issue the op at flush time - deferStruct.mRenderer.saveLayerDeferred(mArea.left, mArea.top, mArea.right, mArea.bottom, - mPaint, mFlags); - } - - virtual void applyState(OpenGLRenderer& renderer, int saveCount) const override { - renderer.saveLayer(mArea.left, mArea.top, mArea.right, mArea.bottom, - mPaint, mFlags, mConvexMask); - } - - virtual void output(int level, uint32_t logFlags) const override { - OP_LOG("SaveLayer%s of area " RECT_STRING, - (isSaveLayerAlpha() ? "Alpha" : ""),RECT_ARGS(mArea)); - } - - virtual const char* name() override { - return isSaveLayerAlpha() ? "SaveLayerAlpha" : "SaveLayer"; - } - - int getFlags() { return mFlags; } - - // Called to make SaveLayerOp clip to the provided mask when drawing back/restored - void setMask(const SkPath* convexMask) { - mConvexMask = convexMask; - } - -private: - bool isSaveLayerAlpha() const { - SkXfermode::Mode mode = PaintUtils::getXfermodeDirect(mPaint); - int alpha = PaintUtils::getAlphaDirect(mPaint); - return alpha < 255 && mode == SkXfermode::kSrcOver_Mode; - } - - Rect mArea; - const SkPaint* mPaint; - SkPaint mCachedPaint; - int mFlags; - - // Convex path, points at data in RenderNode, valid for the duration of the frame only - // Only used for masking the SaveLayer which wraps projected RenderNodes - const SkPath* mConvexMask; -}; - -class TranslateOp : public StateOp { -public: - TranslateOp(float dx, float dy) - : mDx(dx), mDy(dy) {} - - virtual void applyState(OpenGLRenderer& renderer, int saveCount) const override { - renderer.translate(mDx, mDy); - } - - virtual void output(int level, uint32_t logFlags) const override { - OP_LOG("Translate by %f %f", mDx, mDy); - } - - virtual const char* name() override { return "Translate"; } - -private: - float mDx; - float mDy; -}; - -class RotateOp : public StateOp { -public: - explicit RotateOp(float degrees) - : mDegrees(degrees) {} - - virtual void applyState(OpenGLRenderer& renderer, int saveCount) const override { - renderer.rotate(mDegrees); - } - - virtual void output(int level, uint32_t logFlags) const override { - OP_LOG("Rotate by %f degrees", mDegrees); - } - - virtual const char* name() override { return "Rotate"; } - -private: - float mDegrees; -}; - -class ScaleOp : public StateOp { -public: - ScaleOp(float sx, float sy) - : mSx(sx), mSy(sy) {} - - virtual void applyState(OpenGLRenderer& renderer, int saveCount) const override { - renderer.scale(mSx, mSy); - } - - virtual void output(int level, uint32_t logFlags) const override { - OP_LOG("Scale by %f %f", mSx, mSy); - } - - virtual const char* name() override { return "Scale"; } - -private: - float mSx; - float mSy; -}; - -class SkewOp : public StateOp { -public: - SkewOp(float sx, float sy) - : mSx(sx), mSy(sy) {} - - virtual void applyState(OpenGLRenderer& renderer, int saveCount) const override { - renderer.skew(mSx, mSy); - } - - virtual void output(int level, uint32_t logFlags) const override { - OP_LOG("Skew by %f %f", mSx, mSy); - } - - virtual const char* name() override { return "Skew"; } - -private: - float mSx; - float mSy; -}; - -class SetMatrixOp : public StateOp { -public: - explicit SetMatrixOp(const SkMatrix& matrix) - : mMatrix(matrix) {} - - virtual void applyState(OpenGLRenderer& renderer, int saveCount) const override { - // Setting a matrix on a Canvas isn't equivalent to setting a total matrix on the scene. - // Set a canvas-relative matrix on the renderer instead. - renderer.setLocalMatrix(mMatrix); - } - - virtual void output(int level, uint32_t logFlags) const override { - if (mMatrix.isIdentity()) { - OP_LOGS("SetMatrix (reset)"); - } else { - OP_LOG("SetMatrix " SK_MATRIX_STRING, SK_MATRIX_ARGS(&mMatrix)); - } - } - - virtual const char* name() override { return "SetMatrix"; } - -private: - const SkMatrix mMatrix; -}; - -class ConcatMatrixOp : public StateOp { -public: - explicit ConcatMatrixOp(const SkMatrix& matrix) - : mMatrix(matrix) {} - - virtual void applyState(OpenGLRenderer& renderer, int saveCount) const override { - renderer.concatMatrix(mMatrix); - } - - virtual void output(int level, uint32_t logFlags) const override { - OP_LOG("ConcatMatrix " SK_MATRIX_STRING, SK_MATRIX_ARGS(&mMatrix)); - } - - virtual const char* name() override { return "ConcatMatrix"; } - -private: - const SkMatrix mMatrix; -}; - -class ClipOp : public StateOp { -public: - explicit ClipOp(SkRegion::Op op) : mOp(op) {} - - virtual void defer(DeferStateStruct& deferStruct, int saveCount, int level, - bool useQuickReject) override { - // NOTE: must defer op BEFORE applying state, since it may read clip - deferStruct.mDeferredList.addClip(deferStruct.mRenderer, this); - - // TODO: Can we avoid applying complex clips at defer time? - applyState(deferStruct.mRenderer, saveCount); - } - - bool canCauseComplexClip() { - return ((mOp != SkRegion::kIntersect_Op) && (mOp != SkRegion::kReplace_Op)) || !isRect(); - } - -protected: - virtual bool isRect() { return false; } - - SkRegion::Op mOp; -}; - -class ClipRectOp : public ClipOp { -public: - ClipRectOp(float left, float top, float right, float bottom, SkRegion::Op op) - : ClipOp(op), mArea(left, top, right, bottom) {} - - virtual void applyState(OpenGLRenderer& renderer, int saveCount) const override { - renderer.clipRect(mArea.left, mArea.top, mArea.right, mArea.bottom, mOp); - } - - virtual void output(int level, uint32_t logFlags) const override { - OP_LOG("ClipRect " RECT_STRING, RECT_ARGS(mArea)); - } - - virtual const char* name() override { return "ClipRect"; } - -protected: - virtual bool isRect() override { return true; } - -private: - Rect mArea; -}; - -class ClipPathOp : public ClipOp { -public: - ClipPathOp(const SkPath* path, SkRegion::Op op) - : ClipOp(op), mPath(path) {} - - virtual void applyState(OpenGLRenderer& renderer, int saveCount) const override { - renderer.clipPath(mPath, mOp); - } - - virtual void output(int level, uint32_t logFlags) const override { - SkRect bounds = mPath->getBounds(); - OP_LOG("ClipPath bounds " RECT_STRING, - bounds.left(), bounds.top(), bounds.right(), bounds.bottom()); - } - - virtual const char* name() override { return "ClipPath"; } - -private: - const SkPath* mPath; -}; - -class ClipRegionOp : public ClipOp { -public: - ClipRegionOp(const SkRegion* region, SkRegion::Op op) - : ClipOp(op), mRegion(region) {} - - virtual void applyState(OpenGLRenderer& renderer, int saveCount) const override { - renderer.clipRegion(mRegion, mOp); - } - - virtual void output(int level, uint32_t logFlags) const override { - SkIRect bounds = mRegion->getBounds(); - OP_LOG("ClipRegion bounds %d %d %d %d", - bounds.left(), bounds.top(), bounds.right(), bounds.bottom()); - } - - virtual const char* name() override { return "ClipRegion"; } - -private: - const SkRegion* mRegion; -}; - -/////////////////////////////////////////////////////////////////////////////// -// DRAW OPERATIONS - these are operations that can draw to the canvas's device -/////////////////////////////////////////////////////////////////////////////// - -class DrawBitmapOp : public DrawBoundedOp { -public: - DrawBitmapOp(const SkBitmap* bitmap, const SkPaint* paint) - : DrawBoundedOp(0, 0, bitmap->width(), bitmap->height(), paint) - , mBitmap(bitmap) - , mEntryValid(false), mEntry(nullptr) { - } - - virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override { - renderer.drawBitmap(mBitmap, mPaint); - } - - AssetAtlas::Entry* getAtlasEntry(OpenGLRenderer& renderer) { - if (!mEntryValid) { - mEntryValid = true; - mEntry = renderer.renderState().assetAtlas().getEntry(mBitmap->pixelRef()); - } - return mEntry; - } - -#define SET_TEXTURE(ptr, posRect, offsetRect, texCoordsRect, xDim, yDim) \ - TextureVertex::set((ptr)++, (posRect).xDim - (offsetRect).left, (posRect).yDim - (offsetRect).top, \ - (texCoordsRect).xDim, (texCoordsRect).yDim) - - /** - * This multi-draw operation builds a mesh on the stack by generating a quad - * for each bitmap in the batch. This method is also responsible for dirtying - * the current layer, if any. - */ - virtual void multiDraw(OpenGLRenderer& renderer, Rect& dirty, - const std::vector<OpStatePair>& ops, const Rect& bounds) override { - const DeferredDisplayState& firstState = *(ops[0].state); - renderer.restoreDisplayState(firstState, true); // restore all but the clip - - TextureVertex vertices[6 * ops.size()]; - TextureVertex* vertex = &vertices[0]; - - const bool hasLayer = renderer.hasLayer(); - bool pureTranslate = true; - - // TODO: manually handle rect clip for bitmaps by adjusting texCoords per op, - // and allowing them to be merged in getBatchId() - for (unsigned int i = 0; i < ops.size(); i++) { - const DeferredDisplayState& state = *(ops[i].state); - const Rect& opBounds = state.mBounds; - // When we reach multiDraw(), the matrix can be either - // pureTranslate or simple (translate and/or scale). - // If the matrix is not pureTranslate, then we have a scale - pureTranslate &= state.mMatrix.isPureTranslate(); - - Rect texCoords(0, 0, 1, 1); - ((DrawBitmapOp*) ops[i].op)->uvMap(renderer, texCoords); - - SET_TEXTURE(vertex, opBounds, bounds, texCoords, left, top); - SET_TEXTURE(vertex, opBounds, bounds, texCoords, right, top); - SET_TEXTURE(vertex, opBounds, bounds, texCoords, left, bottom); - - SET_TEXTURE(vertex, opBounds, bounds, texCoords, left, bottom); - SET_TEXTURE(vertex, opBounds, bounds, texCoords, right, top); - SET_TEXTURE(vertex, opBounds, bounds, texCoords, right, bottom); - - if (hasLayer) { - renderer.dirtyLayer(opBounds.left, opBounds.top, opBounds.right, opBounds.bottom); - } - } - - renderer.drawBitmaps(mBitmap, mEntry, ops.size(), &vertices[0], - pureTranslate, bounds, mPaint); - } - - virtual void output(int level, uint32_t logFlags) const override { - OP_LOG("Draw bitmap %p of size %dx%d%s", - mBitmap, mBitmap->width(), mBitmap->height(), - mEntry ? " using AssetAtlas" : ""); - } - - virtual const char* name() override { return "DrawBitmap"; } - - virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo, - const DeferredDisplayState& state) override { - deferInfo.batchId = DeferredDisplayList::kOpBatch_Bitmap; - deferInfo.mergeId = getAtlasEntry(renderer) ? - (mergeid_t) mEntry->getMergeId() : (mergeid_t) mBitmap; - - // 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() - // TODO: support clipped bitmaps by handling them in SET_TEXTURE - deferInfo.mergeable = state.mMatrix.isSimple() && state.mMatrix.positiveScale() && - !state.mClipSideFlags && - PaintUtils::getXfermodeDirect(mPaint) == SkXfermode::kSrcOver_Mode && - (mBitmap->colorType() != kAlpha_8_SkColorType); - } - - void uvMap(OpenGLRenderer& renderer, Rect& texCoords) { - if (getAtlasEntry(renderer)) { - mEntry->uvMapper.map(texCoords); - } - } - - const SkBitmap* bitmap() { return mBitmap; } -protected: - const SkBitmap* mBitmap; - bool mEntryValid; - AssetAtlas::Entry* mEntry; -}; - -class DrawBitmapRectOp : public DrawBoundedOp { -public: - DrawBitmapRectOp(const SkBitmap* bitmap, - float srcLeft, float srcTop, float srcRight, float srcBottom, - float dstLeft, float dstTop, float dstRight, float dstBottom, const SkPaint* paint) - : DrawBoundedOp(dstLeft, dstTop, dstRight, dstBottom, paint), - mBitmap(bitmap), mSrc(srcLeft, srcTop, srcRight, srcBottom) {} - - virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override { - renderer.drawBitmap(mBitmap, mSrc, mLocalBounds, mPaint); - } - - virtual void output(int level, uint32_t logFlags) const override { - OP_LOG("Draw bitmap %p src=" RECT_STRING ", dst=" RECT_STRING, - mBitmap, RECT_ARGS(mSrc), RECT_ARGS(mLocalBounds)); - } - - virtual const char* name() override { return "DrawBitmapRect"; } - - virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo, - const DeferredDisplayState& state) override { - deferInfo.batchId = DeferredDisplayList::kOpBatch_Bitmap; - } - -private: - const SkBitmap* mBitmap; - Rect mSrc; -}; - -class DrawBitmapMeshOp : public DrawBoundedOp { -public: - DrawBitmapMeshOp(const SkBitmap* bitmap, int meshWidth, int meshHeight, - const float* vertices, const int* colors, const SkPaint* paint) - : DrawBoundedOp(vertices, 2 * (meshWidth + 1) * (meshHeight + 1), paint), - mBitmap(bitmap), mMeshWidth(meshWidth), mMeshHeight(meshHeight), - mVertices(vertices), mColors(colors) {} - - virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override { - renderer.drawBitmapMesh(mBitmap, mMeshWidth, mMeshHeight, - mVertices, mColors, mPaint); - } - - virtual void output(int level, uint32_t logFlags) const override { - OP_LOG("Draw bitmap %p mesh %d x %d", mBitmap, mMeshWidth, mMeshHeight); - } - - virtual const char* name() override { return "DrawBitmapMesh"; } - - virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo, - const DeferredDisplayState& state) override { - deferInfo.batchId = DeferredDisplayList::kOpBatch_Bitmap; - } - -private: - const SkBitmap* mBitmap; - int mMeshWidth; - int mMeshHeight; - const float* mVertices; - const int* mColors; -}; - -class DrawPatchOp : public DrawBoundedOp { -public: - DrawPatchOp(const SkBitmap* bitmap, const Res_png_9patch* patch, - float left, float top, float right, float bottom, const SkPaint* paint) - : DrawBoundedOp(left, top, right, bottom, paint), - mBitmap(bitmap), mPatch(patch), mGenerationId(0), mMesh(nullptr), - mEntryValid(false), mEntry(nullptr) { - }; - - AssetAtlas::Entry* getAtlasEntry(OpenGLRenderer& renderer) { - if (!mEntryValid) { - mEntryValid = true; - mEntry = renderer.renderState().assetAtlas().getEntry(mBitmap->pixelRef()); - } - return mEntry; - } - - const Patch* getMesh(OpenGLRenderer& renderer) { - if (!mMesh || renderer.getCaches().patchCache.getGenerationId() != mGenerationId) { - PatchCache& cache = renderer.getCaches().patchCache; - mMesh = cache.get(getAtlasEntry(renderer), mBitmap->width(), mBitmap->height(), - mLocalBounds.getWidth(), mLocalBounds.getHeight(), mPatch); - mGenerationId = cache.getGenerationId(); - } - return mMesh; - } - - /** - * This multi-draw operation builds an indexed mesh on the stack by copying - * and transforming the vertices of each 9-patch in the batch. This method - * is also responsible for dirtying the current layer, if any. - */ - virtual void multiDraw(OpenGLRenderer& renderer, Rect& dirty, - const std::vector<OpStatePair>& ops, const Rect& bounds) override { - const DeferredDisplayState& firstState = *(ops[0].state); - renderer.restoreDisplayState(firstState, true); // restore all but the clip - - // Batches will usually contain a small number of items so it's - // worth performing a first iteration to count the exact number - // of vertices we need in the new mesh - uint32_t totalVertices = 0; - for (unsigned int i = 0; i < ops.size(); i++) { - totalVertices += ((DrawPatchOp*) ops[i].op)->getMesh(renderer)->verticesCount; - } - - const bool hasLayer = renderer.hasLayer(); - - uint32_t indexCount = 0; - - TextureVertex vertices[totalVertices]; - TextureVertex* vertex = &vertices[0]; - - // Create a mesh that contains the transformed vertices for all the - // 9-patch objects that are part of the batch. Note that onDefer() - // enforces ops drawn by this function to have a pure translate or - // identity matrix - for (unsigned int i = 0; i < ops.size(); i++) { - DrawPatchOp* patchOp = (DrawPatchOp*) ops[i].op; - const DeferredDisplayState* state = ops[i].state; - const Patch* opMesh = patchOp->getMesh(renderer); - uint32_t vertexCount = opMesh->verticesCount; - if (vertexCount == 0) continue; - - // 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 = (int) floorf(state->mMatrix.getTranslateX() + - patchOp->mLocalBounds.left + 0.5f); - const float ty = (int) floorf(state->mMatrix.getTranslateY() + - patchOp->mLocalBounds.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); - } - - // Dirty the current layer if possible. When the 9-patch does not - // contain empty quads we can take a shortcut and simply set the - // dirty rect to the object's bounds. - if (hasLayer) { - if (!opMesh->hasEmptyQuads) { - renderer.dirtyLayer(tx, ty, - tx + patchOp->mLocalBounds.getWidth(), - ty + patchOp->mLocalBounds.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.dirtyLayer(x, y, - x + quadBounds.getWidth(), y + quadBounds.getHeight()); - } - } - } - - indexCount += opMesh->indexCount; - } - - renderer.drawPatches(mBitmap, getAtlasEntry(renderer), - &vertices[0], indexCount, mPaint); - } - - virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override { - // We're not calling the public variant of drawPatch() here - // This method won't perform the quickReject() since we've already done it at this point - renderer.drawPatch(mBitmap, getMesh(renderer), getAtlasEntry(renderer), - mLocalBounds.left, mLocalBounds.top, mLocalBounds.right, mLocalBounds.bottom, - mPaint); - } - - virtual void output(int level, uint32_t logFlags) const override { - OP_LOG("Draw patch " RECT_STRING "%s", RECT_ARGS(mLocalBounds), - mEntry ? " with AssetAtlas" : ""); - } - - virtual const char* name() override { return "DrawPatch"; } - - virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo, - const DeferredDisplayState& state) override { - deferInfo.batchId = DeferredDisplayList::kOpBatch_Patch; - deferInfo.mergeId = getAtlasEntry(renderer) ? (mergeid_t) mEntry->getMergeId() : (mergeid_t) mBitmap; - deferInfo.mergeable = state.mMatrix.isPureTranslate() && - PaintUtils::getXfermodeDirect(mPaint) == SkXfermode::kSrcOver_Mode; - deferInfo.opaqueOverBounds = isOpaqueOverBounds(state) && mBitmap->isOpaque(); - } - -private: - const SkBitmap* mBitmap; - const Res_png_9patch* mPatch; - - uint32_t mGenerationId; - const Patch* mMesh; - - bool mEntryValid; - AssetAtlas::Entry* mEntry; -}; - -class DrawColorOp : public DrawOp { -public: - DrawColorOp(int color, SkXfermode::Mode mode) - : DrawOp(nullptr), mColor(color), mMode(mode) {}; - - virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override { - renderer.drawColor(mColor, mMode); - } - - virtual void output(int level, uint32_t logFlags) const override { - OP_LOG("Draw color %#x, mode %d", mColor, mMode); - } - - virtual const char* name() override { return "DrawColor"; } - -private: - int mColor; - SkXfermode::Mode mMode; -}; - -class DrawStrokableOp : public DrawBoundedOp { -public: - DrawStrokableOp(float left, float top, float right, float bottom, const SkPaint* paint) - : DrawBoundedOp(left, top, right, bottom, paint) {}; - DrawStrokableOp(const Rect& localBounds, const SkPaint* paint) - : DrawBoundedOp(localBounds, paint) {}; - - virtual bool getLocalBounds(Rect& localBounds) override { - localBounds.set(mLocalBounds); - if (mPaint && mPaint->getStyle() != SkPaint::kFill_Style) { - localBounds.outset(strokeWidthOutset()); - } - return true; - } - - virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo, - const DeferredDisplayState& state) override { - if (mPaint->getPathEffect()) { - deferInfo.batchId = DeferredDisplayList::kOpBatch_AlphaMaskTexture; - } else { - deferInfo.batchId = mPaint->isAntiAlias() ? - DeferredDisplayList::kOpBatch_AlphaVertices : - DeferredDisplayList::kOpBatch_Vertices; - } - } -}; - -class DrawRectOp : public DrawStrokableOp { -public: - DrawRectOp(float left, float top, float right, float bottom, const SkPaint* paint) - : DrawStrokableOp(left, top, right, bottom, paint) {} - - virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override { - renderer.drawRect(mLocalBounds.left, mLocalBounds.top, - mLocalBounds.right, mLocalBounds.bottom, mPaint); - } - - virtual void output(int level, uint32_t logFlags) const override { - OP_LOG("Draw Rect " RECT_STRING, RECT_ARGS(mLocalBounds)); - } - - virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo, - const DeferredDisplayState& state) override { - DrawStrokableOp::onDefer(renderer, deferInfo, state); - deferInfo.opaqueOverBounds = isOpaqueOverBounds(state) && - mPaint->getStyle() == SkPaint::kFill_Style; - } - - virtual const char* name() override { return "DrawRect"; } -}; - -class DrawRectsOp : public DrawBoundedOp { -public: - DrawRectsOp(const float* rects, int count, const SkPaint* paint) - : DrawBoundedOp(rects, count, paint), - mRects(rects), mCount(count) {} - - virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override { - renderer.drawRects(mRects, mCount, mPaint); - } - - virtual void output(int level, uint32_t logFlags) const override { - OP_LOG("Draw Rects count %d", mCount); - } - - virtual const char* name() override { return "DrawRects"; } - - virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo, - const DeferredDisplayState& state) override { - deferInfo.batchId = DeferredDisplayList::kOpBatch_Vertices; - } - -private: - const float* mRects; - int mCount; -}; - -class DrawRoundRectOp : public DrawStrokableOp { -public: - DrawRoundRectOp(float left, float top, float right, float bottom, - float rx, float ry, const SkPaint* paint) - : DrawStrokableOp(left, top, right, bottom, paint), mRx(rx), mRy(ry) {} - - virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override { - renderer.drawRoundRect(mLocalBounds.left, mLocalBounds.top, - mLocalBounds.right, mLocalBounds.bottom, mRx, mRy, mPaint); - } - - virtual void output(int level, uint32_t logFlags) const override { - OP_LOG("Draw RoundRect " RECT_STRING ", rx %f, ry %f", RECT_ARGS(mLocalBounds), mRx, mRy); - } - - virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo, - const DeferredDisplayState& state) override { - DrawStrokableOp::onDefer(renderer, deferInfo, state); - if (!mPaint->getPathEffect()) { - renderer.getCaches().tessellationCache.precacheRoundRect(state.mMatrix, *mPaint, - mLocalBounds.getWidth(), mLocalBounds.getHeight(), mRx, mRy); - } - } - - virtual const char* name() override { return "DrawRoundRect"; } - -private: - float mRx; - float mRy; -}; - -class DrawRoundRectPropsOp : public DrawOp { -public: - DrawRoundRectPropsOp(float* left, float* top, float* right, float* bottom, - float *rx, float *ry, const SkPaint* paint) - : DrawOp(paint), mLeft(left), mTop(top), mRight(right), mBottom(bottom), - mRx(rx), mRy(ry) {} - - virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override { - renderer.drawRoundRect(*mLeft, *mTop, *mRight, *mBottom, - *mRx, *mRy, mPaint); - } - - virtual void output(int level, uint32_t logFlags) const override { - OP_LOG("Draw RoundRect Props " RECT_STRING ", rx %f, ry %f", - *mLeft, *mTop, *mRight, *mBottom, *mRx, *mRy); - } - - virtual const char* name() override { return "DrawRoundRectProps"; } - -private: - float* mLeft; - float* mTop; - float* mRight; - float* mBottom; - float* mRx; - float* mRy; -}; - -class DrawCircleOp : public DrawStrokableOp { -public: - DrawCircleOp(float x, float y, float radius, const SkPaint* paint) - : DrawStrokableOp(x - radius, y - radius, x + radius, y + radius, paint), - mX(x), mY(y), mRadius(radius) {} - - virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override { - renderer.drawCircle(mX, mY, mRadius, mPaint); - } - - virtual void output(int level, uint32_t logFlags) const override { - OP_LOG("Draw Circle x %f, y %f, r %f", mX, mY, mRadius); - } - - virtual const char* name() override { return "DrawCircle"; } - -private: - float mX; - float mY; - float mRadius; -}; - -class DrawCirclePropsOp : public DrawOp { -public: - DrawCirclePropsOp(float* x, float* y, float* radius, const SkPaint* paint) - : DrawOp(paint), mX(x), mY(y), mRadius(radius) {} - - virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override { - renderer.drawCircle(*mX, *mY, *mRadius, mPaint); - } - - virtual void output(int level, uint32_t logFlags) const override { - OP_LOG("Draw Circle Props x %p, y %p, r %p", mX, mY, mRadius); - } - - virtual const char* name() override { return "DrawCircleProps"; } - -private: - float* mX; - float* mY; - float* mRadius; -}; - -class DrawVectorDrawableOp : public DrawOp { -public: - DrawVectorDrawableOp(VectorDrawableRoot* tree, const SkRect& bounds) - : DrawOp(nullptr), mTree(tree), mDst(bounds) {} - - virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override { - const SkBitmap& bitmap = mTree->getBitmapUpdateIfDirty(); - SkPaint* paint = mTree->getPaint(); - renderer.drawBitmap(&bitmap, Rect(0, 0, bitmap.width(), bitmap.height()), - mDst, paint); - } - - virtual void output(int level, uint32_t logFlags) const override { - OP_LOG("Draw Vector Drawable %p", mTree); - } - - virtual const char* name() override { return "DrawVectorDrawable"; } - -private: - VectorDrawableRoot* mTree; - SkRect mDst; - -}; - -class DrawOvalOp : public DrawStrokableOp { -public: - DrawOvalOp(float left, float top, float right, float bottom, const SkPaint* paint) - : DrawStrokableOp(left, top, right, bottom, paint) {} - - virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override { - renderer.drawOval(mLocalBounds.left, mLocalBounds.top, - mLocalBounds.right, mLocalBounds.bottom, mPaint); - } - - virtual void output(int level, uint32_t logFlags) const override { - OP_LOG("Draw Oval " RECT_STRING, RECT_ARGS(mLocalBounds)); - } - - virtual const char* name() override { return "DrawOval"; } -}; - -class DrawArcOp : public DrawStrokableOp { -public: - DrawArcOp(float left, float top, float right, float bottom, - float startAngle, float sweepAngle, bool useCenter, const SkPaint* paint) - : DrawStrokableOp(left, top, right, bottom, paint), - mStartAngle(startAngle), mSweepAngle(sweepAngle), mUseCenter(useCenter) {} - - virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override { - renderer.drawArc(mLocalBounds.left, mLocalBounds.top, - mLocalBounds.right, mLocalBounds.bottom, - mStartAngle, mSweepAngle, mUseCenter, mPaint); - } - - virtual void output(int level, uint32_t logFlags) const override { - OP_LOG("Draw Arc " RECT_STRING ", start %f, sweep %f, useCenter %d", - RECT_ARGS(mLocalBounds), mStartAngle, mSweepAngle, mUseCenter); - } - - virtual const char* name() override { return "DrawArc"; } - -private: - float mStartAngle; - float mSweepAngle; - bool mUseCenter; -}; - -class DrawPathOp : public DrawBoundedOp { -public: - DrawPathOp(const SkPath* path, const SkPaint* paint) - : DrawBoundedOp(paint), mPath(path) { - float left, top, offset; - uint32_t width, height; - PathCache::computePathBounds(path, paint, left, top, offset, width, height); - left -= offset; - top -= offset; - mLocalBounds.set(left, top, left + width, top + height); - } - - virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override { - renderer.drawPath(mPath, mPaint); - } - - virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo, - const DeferredDisplayState& state) override { - renderer.getCaches().pathCache.precache(mPath, mPaint); - - deferInfo.batchId = DeferredDisplayList::kOpBatch_AlphaMaskTexture; - } - - virtual void output(int level, uint32_t logFlags) const override { - OP_LOG("Draw Path %p in " RECT_STRING, mPath, RECT_ARGS(mLocalBounds)); - } - - virtual const char* name() override { return "DrawPath"; } - -private: - const SkPath* mPath; -}; - -class DrawLinesOp : public DrawBoundedOp { -public: - DrawLinesOp(const float* points, int count, const SkPaint* paint) - : DrawBoundedOp(points, count, paint), - mPoints(points), mCount(count) { - mLocalBounds.outset(strokeWidthOutset()); - } - - virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override { - renderer.drawLines(mPoints, mCount, mPaint); - } - - virtual void output(int level, uint32_t logFlags) const override { - OP_LOG("Draw Lines count %d", mCount); - } - - virtual const char* name() override { return "DrawLines"; } - - virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo, - const DeferredDisplayState& state) override { - deferInfo.batchId = mPaint->isAntiAlias() ? - DeferredDisplayList::kOpBatch_AlphaVertices : - DeferredDisplayList::kOpBatch_Vertices; - } - -protected: - const float* mPoints; - int mCount; -}; - -class DrawPointsOp : public DrawLinesOp { -public: - DrawPointsOp(const float* points, int count, const SkPaint* paint) - : DrawLinesOp(points, count, paint) {} - - virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override { - renderer.drawPoints(mPoints, mCount, mPaint); - } - - virtual void output(int level, uint32_t logFlags) const override { - OP_LOG("Draw Points count %d", mCount); - } - - virtual const char* name() override { return "DrawPoints"; } -}; - -class DrawSomeTextOp : public DrawOp { -public: - DrawSomeTextOp(const glyph_t* text, int bytesCount, int count, const SkPaint* paint) - : DrawOp(paint), mText(text), mBytesCount(bytesCount), mCount(count) {}; - - virtual void output(int level, uint32_t logFlags) const override { - OP_LOG("Draw some text, %d bytes", mBytesCount); - } - - virtual bool hasTextShadow() const override { - return PaintUtils::hasTextShadow(mPaint); - } - - virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo, - const DeferredDisplayState& state) override { - FontRenderer& fontRenderer = renderer.getCaches().fontRenderer.getFontRenderer(); - fontRenderer.precache(mPaint, mText, mCount, SkMatrix::I()); - - deferInfo.batchId = mPaint->getColor() == SK_ColorBLACK ? - DeferredDisplayList::kOpBatch_Text : - DeferredDisplayList::kOpBatch_ColorText; - } - -protected: - const glyph_t* mText; - int mBytesCount; - int mCount; -}; - -class DrawTextOnPathOp : public DrawSomeTextOp { -public: - DrawTextOnPathOp(const glyph_t* text, int bytesCount, int count, - const SkPath* path, float hOffset, float vOffset, const SkPaint* paint) - : DrawSomeTextOp(text, bytesCount, count, paint), - mPath(path), mHOffset(hOffset), mVOffset(vOffset) { - /* TODO: inherit from DrawBounded and init mLocalBounds */ - } - - virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override { - renderer.drawTextOnPath(mText, mBytesCount, mCount, mPath, - mHOffset, mVOffset, mPaint); - } - - virtual const char* name() override { return "DrawTextOnPath"; } - -private: - const SkPath* mPath; - float mHOffset; - float mVOffset; -}; - -class DrawTextOp : public DrawStrokableOp { -public: - DrawTextOp(const glyph_t* text, int bytesCount, int count, float x, float y, - const float* positions, const SkPaint* paint, float totalAdvance, const Rect& bounds) - : DrawStrokableOp(bounds, paint), mText(text), mBytesCount(bytesCount), mCount(count), - mX(x), mY(y), mPositions(positions), mTotalAdvance(totalAdvance) { - mPrecacheTransform = SkMatrix::InvalidMatrix(); - } - - virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo, - const DeferredDisplayState& state) override { - FontRenderer& fontRenderer = renderer.getCaches().fontRenderer.getFontRenderer(); - SkMatrix transform; - renderer.findBestFontTransform(state.mMatrix, &transform); - if (mPrecacheTransform != transform) { - fontRenderer.precache(mPaint, mText, mCount, transform); - mPrecacheTransform = transform; - } - deferInfo.batchId = mPaint->getColor() == SK_ColorBLACK ? - DeferredDisplayList::kOpBatch_Text : - DeferredDisplayList::kOpBatch_ColorText; - - deferInfo.mergeId = reinterpret_cast<mergeid_t>(mPaint->getColor()); - - // don't merge decorated text - the decorations won't draw in order - bool hasDecorations = mPaint->getFlags() - & (SkPaint::kUnderlineText_Flag | SkPaint::kStrikeThruText_Flag); - - deferInfo.mergeable = state.mMatrix.isPureTranslate() - && !hasDecorations - && PaintUtils::getXfermodeDirect(mPaint) == SkXfermode::kSrcOver_Mode; - } - - virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override { - Rect bounds; - getLocalBounds(bounds); - renderer.drawText(mText, mBytesCount, mCount, mX, mY, - mPositions, mPaint, mTotalAdvance, bounds); - } - - virtual void multiDraw(OpenGLRenderer& renderer, Rect& dirty, - const std::vector<OpStatePair>& ops, const Rect& bounds) override { - for (unsigned int i = 0; i < ops.size(); i++) { - const DeferredDisplayState& state = *(ops[i].state); - DrawOpMode drawOpMode = (i == ops.size() - 1) ? DrawOpMode::kFlush : DrawOpMode::kDefer; - renderer.restoreDisplayState(state, true); // restore all but the clip - - DrawTextOp& op = *((DrawTextOp*)ops[i].op); - // quickReject() will not occure in drawText() so we can use mLocalBounds - // directly, we do not need to account for shadow by calling getLocalBounds() - renderer.drawText(op.mText, op.mBytesCount, op.mCount, op.mX, op.mY, - op.mPositions, op.mPaint, op.mTotalAdvance, op.mLocalBounds, - drawOpMode); - } - } - - virtual void output(int level, uint32_t logFlags) const override { - OP_LOG("Draw Text of count %d, bytes %d", mCount, mBytesCount); - } - - virtual const char* name() override { return "DrawText"; } - -private: - const glyph_t* mText; - int mBytesCount; - int mCount; - float mX; - float mY; - const float* mPositions; - float mTotalAdvance; - SkMatrix mPrecacheTransform; -}; - -/////////////////////////////////////////////////////////////////////////////// -// SPECIAL DRAW OPERATIONS -/////////////////////////////////////////////////////////////////////////////// - -class DrawFunctorOp : public DrawOp { -public: - explicit DrawFunctorOp(Functor* functor) - : DrawOp(nullptr), mFunctor(functor) {} - - virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override { - renderer.startMark("GL functor"); - renderer.callDrawGLFunction(mFunctor, dirty); - renderer.endMark(); - } - - virtual void output(int level, uint32_t logFlags) const override { - OP_LOG("Draw Functor %p", mFunctor); - } - - virtual const char* name() override { return "DrawFunctor"; } - -private: - Functor* mFunctor; -}; - -class DrawRenderNodeOp : public DrawBoundedOp { - friend class RenderNode; // grant RenderNode access to info of child - friend class DisplayList; // grant DisplayList access to info of child - friend class DisplayListCanvas; - friend class TestUtils; -public: - DrawRenderNodeOp(RenderNode* renderNode, const mat4& transformFromParent, bool clipIsSimple) - : DrawBoundedOp(0, 0, - renderNode->stagingProperties().getWidth(), - renderNode->stagingProperties().getHeight(), - nullptr) - , renderNode(renderNode) - , mRecordedWithPotentialStencilClip(!clipIsSimple || !transformFromParent.isSimple()) - , localMatrix(transformFromParent) - , skipInOrderDraw(false) {} - - virtual void defer(DeferStateStruct& deferStruct, int saveCount, int level, - bool useQuickReject) override { - if (renderNode->isRenderable() && !skipInOrderDraw) { - renderNode->defer(deferStruct, level + 1); - } - } - - virtual void replay(ReplayStateStruct& replayStruct, int saveCount, int level, - bool useQuickReject) override { - if (renderNode->isRenderable() && !skipInOrderDraw) { - renderNode->replay(replayStruct, level + 1); - } - } - - virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override { - LOG_ALWAYS_FATAL("should not be called, because replay() is overridden"); - } - - virtual void output(int level, uint32_t logFlags) const override { - OP_LOG("Draw RenderNode %p %s", renderNode, renderNode->getName()); - if (renderNode && (logFlags & kOpLogFlag_Recurse)) { - renderNode->output(level + 1); - } - } - - virtual const char* name() override { return "DrawRenderNode"; } - -private: - RenderNode* renderNode; - - /** - * This RenderNode was drawn into a DisplayList with the canvas in a state that will likely - * require rendering with stencil clipping. Either: - * - * 1) A path clip or rotated rect clip was in effect on the canvas at record time - * 2) The RenderNode was recorded with a non-simple canvas transform (e.g. rotation) - * - * Note: even if this is false, non-rect clipping may still be applied applied either due to - * property-driven rotation (either in this RenderNode, or any ancestor), or record time - * clipping in an ancestor. These are handled in RenderNode::prepareTreeImpl since they are - * dynamic (relative to a static DisplayList of a parent), and don't affect this flag. - */ - bool mRecordedWithPotentialStencilClip; - - /////////////////////////// - // Properties below are used by RenderNode::computeOrderingImpl() and issueOperations() - /////////////////////////// - /** - * Records transform vs parent, used for computing total transform without rerunning DL contents - */ - const mat4 localMatrix; - - /** - * Holds the transformation between the projection surface ViewGroup and this RenderNode - * drawing instance. Represents any translations / transformations done within the drawing of - * the compositing ancestor ViewGroup's draw, before the draw of the View represented by this - * DisplayList draw instance. - * - * Note: doesn't include transformation within the RenderNode, or its properties. - */ - mat4 transformFromCompositingAncestor; - bool skipInOrderDraw; -}; - -/** - * Not a canvas operation, used only by 3d / z ordering logic in RenderNode::iterate() - */ -class DrawShadowOp : public DrawOp { -public: - DrawShadowOp(const mat4& transformXY, const mat4& transformZ, - float casterAlpha, const SkPath* casterOutline) - : DrawOp(nullptr) - , mTransformXY(transformXY) - , mTransformZ(transformZ) - , mCasterAlpha(casterAlpha) - , mCasterOutline(casterOutline) { - } - - virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo, - const DeferredDisplayState& state) override { - renderer.getCaches().tessellationCache.precacheShadows(&state.mMatrix, - renderer.getLocalClipBounds(), isCasterOpaque(), mCasterOutline, - &mTransformXY, &mTransformZ, renderer.getLightCenter(), renderer.getLightRadius()); - } - - virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override { - TessellationCache::vertexBuffer_pair_t buffers; - Matrix4 drawTransform(*(renderer.currentTransform())); - renderer.getCaches().tessellationCache.getShadowBuffers(&drawTransform, - renderer.getLocalClipBounds(), isCasterOpaque(), mCasterOutline, - &mTransformXY, &mTransformZ, renderer.getLightCenter(), renderer.getLightRadius(), - buffers); - - renderer.drawShadow(mCasterAlpha, buffers.first, buffers.second); - } - - virtual void output(int level, uint32_t logFlags) const override { - OP_LOGS("DrawShadow"); - } - - virtual const char* name() override { return "DrawShadow"; } - -private: - bool isCasterOpaque() { return mCasterAlpha >= 1.0f; } - - const mat4 mTransformXY; - const mat4 mTransformZ; - const float mCasterAlpha; - const SkPath* mCasterOutline; -}; - -class DrawLayerOp : public DrawOp { -public: - DrawLayerOp(Layer* layer) - : DrawOp(nullptr), mLayer(layer) {} - - virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override { - renderer.drawLayer(mLayer); - } - - virtual void output(int level, uint32_t logFlags) const override { - OP_LOG("Draw Layer %p", mLayer); - } - - virtual const char* name() override { return "DrawLayer"; } - -private: - Layer* mLayer; -}; - -}; // namespace uirenderer -}; // namespace android - -#endif // ANDROID_HWUI_DISPLAY_OPERATION_H diff --git a/libs/hwui/Dither.cpp b/libs/hwui/Dither.cpp deleted file mode 100644 index ec2013e27401..000000000000 --- a/libs/hwui/Dither.cpp +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Caches.h" -#include "Dither.h" - -namespace android { -namespace uirenderer { - -/////////////////////////////////////////////////////////////////////////////// -// Lifecycle -/////////////////////////////////////////////////////////////////////////////// - -Dither::Dither(Caches& caches) - : mCaches(caches) - , mInitialized(false) - , mDitherTexture(0) { -} - -void Dither::bindDitherTexture() { - if (!mInitialized) { - glGenTextures(1, &mDitherTexture); - mCaches.textureState().bindTexture(mDitherTexture); - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); - - if (mCaches.extensions().hasFloatTextures()) { - // We use a R16F texture, let's remap the alpha channel to the - // red channel to avoid changing the shader sampling code on GL ES 3.0+ - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_A, GL_RED); - - float dither = 1.0f / (255.0f * DITHER_KERNEL_SIZE * DITHER_KERNEL_SIZE); - const GLfloat pattern[] = { - 0 * dither, 8 * dither, 2 * dither, 10 * dither, - 12 * dither, 4 * dither, 14 * dither, 6 * dither, - 3 * dither, 11 * dither, 1 * dither, 9 * dither, - 15 * dither, 7 * dither, 13 * dither, 5 * dither - }; - - glTexImage2D(GL_TEXTURE_2D, 0, GL_R16F, DITHER_KERNEL_SIZE, DITHER_KERNEL_SIZE, 0, - GL_RED, GL_FLOAT, &pattern); - } else { - const uint8_t pattern[] = { - 0, 8, 2, 10, - 12, 4, 14, 6, - 3, 11, 1, 9, - 15, 7, 13, 5 - }; - - glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, DITHER_KERNEL_SIZE, DITHER_KERNEL_SIZE, 0, - GL_ALPHA, GL_UNSIGNED_BYTE, &pattern); - } - - mInitialized = true; - } else { - mCaches.textureState().bindTexture(mDitherTexture); - } -} - -void Dither::clear() { - if (mInitialized) { - mCaches.textureState().deleteTexture(mDitherTexture); - mInitialized = false; - } -} - -/////////////////////////////////////////////////////////////////////////////// -// Program management -/////////////////////////////////////////////////////////////////////////////// - -void Dither::setupProgram(Program& program, GLuint* textureUnit) { - GLuint textureSlot = (*textureUnit)++; - mCaches.textureState().activateTexture(textureSlot); - - bindDitherTexture(); - - glUniform1i(program.getUniform("ditherSampler"), textureSlot); -} - -}; // namespace uirenderer -}; // namespace android diff --git a/libs/hwui/Dither.h b/libs/hwui/Dither.h deleted file mode 100644 index 6af3e8384472..000000000000 --- a/libs/hwui/Dither.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ANDROID_HWUI_DITHER_H -#define ANDROID_HWUI_DITHER_H - -#include <GLES3/gl3.h> - -namespace android { -namespace uirenderer { - -class Caches; -class Extensions; -class Program; - -// Must be a power of two -#define DITHER_KERNEL_SIZE 4 -// These must not use the .0f notation as they are used from GLSL -#define DITHER_KERNEL_SIZE_INV (1.0 / 4.0) -#define DITHER_KERNEL_SIZE_INV_SQUARE (1.0 / 16.0) - -/** - * Handles dithering for programs. - */ -class Dither { -public: - explicit Dither(Caches& caches); - - void clear(); - void setupProgram(Program& program, GLuint* textureUnit); - -private: - void bindDitherTexture(); - - Caches& mCaches; - bool mInitialized; - GLuint mDitherTexture; -}; - -}; // namespace uirenderer -}; // namespace android - -#endif // ANDROID_HWUI_DITHER_H diff --git a/libs/hwui/Extensions.cpp b/libs/hwui/Extensions.cpp index 02caaa49e99c..00238a25ebde 100644 --- a/libs/hwui/Extensions.cpp +++ b/libs/hwui/Extensions.cpp @@ -22,6 +22,7 @@ #include <GLES2/gl2.h> #include <GLES2/gl2ext.h> + #include <utils/Log.h> namespace android { @@ -36,15 +37,6 @@ namespace uirenderer { Extensions::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"); - mHasDebugMarker = extensions.has("GL_EXT_debug_marker"); - mHas1BitStencil = extensions.has("GL_OES_stencil1"); - mHas4BitStencil = extensions.has("GL_OES_stencil4"); - mHasUnpackSubImage = extensions.has("GL_EXT_unpack_subimage"); - const char* version = (const char*) glGetString(GL_VERSION); // Section 6.1.5 of the OpenGL ES specification indicates the GL version @@ -64,6 +56,28 @@ Extensions::Extensions() { mVersionMajor = 2; mVersionMinor = 0; } + + 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"); + mHasDebugMarker = extensions.has("GL_EXT_debug_marker"); + mHas1BitStencil = extensions.has("GL_OES_stencil1"); + mHas4BitStencil = extensions.has("GL_OES_stencil4"); + mHasUnpackSubImage = extensions.has("GL_EXT_unpack_subimage"); + +#ifdef ANDROID_ENABLE_LINEAR_BLENDING + mHasSRGB = mVersionMajor >= 3 || extensions.has("GL_EXT_sRGB"); + mHasSRGBWriteControl = extensions.has("GL_EXT_sRGB_write_control"); + + // If linear blending is enabled, the device must have (ES3.0 or EXT_sRGB) + // and EXT_sRGB_write_control + LOG_ALWAYS_FATAL_IF(!mHasSRGB, "Linear blending requires ES 3.0 or EXT_sRGB"); + LOG_ALWAYS_FATAL_IF(!mHasSRGBWriteControl, "Linear blending requires EXT_sRGB_write_control"); +#else + mHasSRGB = false; + mHasSRGBWriteControl = false; +#endif } }; // namespace uirenderer diff --git a/libs/hwui/Extensions.h b/libs/hwui/Extensions.h index 67cc747015e0..2c38507bd79a 100644 --- a/libs/hwui/Extensions.h +++ b/libs/hwui/Extensions.h @@ -43,6 +43,8 @@ public: inline bool hasPixelBufferObjects() const { return mVersionMajor >= 3; } inline bool hasOcclusionQueries() const { return mVersionMajor >= 3; } inline bool hasFloatTextures() const { return mVersionMajor >= 3; } + inline bool hasSRGB() const { return mHasSRGB; } + inline bool hasSRGBWriteControl() const { return hasSRGB() && mHasSRGBWriteControl; } inline int getMajorGlVersion() const { return mVersionMajor; } inline int getMinorGlVersion() const { return mVersionMinor; } @@ -55,6 +57,8 @@ private: bool mHas1BitStencil; bool mHas4BitStencil; bool mHasUnpackSubImage; + bool mHasSRGB; + bool mHasSRGBWriteControl; int mVersionMajor; int mVersionMinor; diff --git a/libs/hwui/FloatColor.h b/libs/hwui/FloatColor.h index 9a39ec28aa3d..d8afa35d32bf 100644 --- a/libs/hwui/FloatColor.h +++ b/libs/hwui/FloatColor.h @@ -16,6 +16,7 @@ #ifndef FLOATCOLOR_H #define FLOATCOLOR_H +#include "utils/Color.h" #include "utils/Macros.h" #include "utils/MathUtils.h" @@ -25,11 +26,25 @@ namespace android { namespace uirenderer { struct FloatColor { + // "color" is a gamma-encoded sRGB color + // After calling this method, the color is stored as a pre-multiplied linear color + // if linear blending is enabled. Otherwise, the color is stored as a pre-multiplied + // gamma-encoded sRGB color void set(uint32_t color) { a = ((color >> 24) & 0xff) / 255.0f; - r = a * ((color >> 16) & 0xff) / 255.0f; - g = a * ((color >> 8) & 0xff) / 255.0f; - b = a * ((color ) & 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); + } + + // "color" is a gamma-encoded sRGB color + // After calling this method, the color is stored as a linear color. The color + // is not pre-multiplied. + void setUnPreMultipliedSRGB(uint32_t color) { + a = ((color >> 24) & 0xff) / 255.0f; + r = EOCF_sRGB(((color >> 16) & 0xff) / 255.0f); + g = EOCF_sRGB(((color >> 8) & 0xff) / 255.0f); + b = EOCF_sRGB(((color ) & 0xff) / 255.0f); } bool isNotBlack() { diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp index 681cf55066b4..4f9a3de64fc2 100644 --- a/libs/hwui/FontRenderer.cpp +++ b/libs/hwui/FontRenderer.cpp @@ -16,9 +16,13 @@ #include "FontRenderer.h" +#include "BakedOpDispatcher.h" +#include "BakedOpRenderer.h" +#include "BakedOpState.h" #include "Caches.h" #include "Debug.h" #include "Extensions.h" +#include "font/Font.h" #include "Glop.h" #include "GlopBuilder.h" #include "PixelBuffer.h" @@ -27,15 +31,6 @@ #include "utils/Blur.h" #include "utils/Timing.h" - -#if HWUI_NEW_OPS -#include "BakedOpDispatcher.h" -#include "BakedOpRenderer.h" -#include "BakedOpState.h" -#else -#include "OpenGLRenderer.h" -#endif - #include <algorithm> #include <cutils/properties.h> #include <SkGlyph.h> @@ -66,27 +61,22 @@ void TextDrawFunctor::draw(CacheTexture& texture, bool linearFiltering) { } int transformFlags = pureTranslate ? TransformFlags::MeshIgnoresCanvasTransform : TransformFlags::None; +#ifdef ANDROID_ENABLE_LINEAR_BLENDING + bool gammaCorrection = true; +#else + bool gammaCorrection = false; +#endif Glop glop; -#if HWUI_NEW_OPS GlopBuilder(renderer->renderState(), renderer->caches(), &glop) .setRoundRectClipState(bakedState->roundRectClipState) .setMeshTexturedIndexedQuads(texture.mesh(), texture.meshElementCount()) .setFillTexturePaint(texture.getTexture(), textureFillFlags, paint, bakedState->alpha) + .setGammaCorrection(gammaCorrection) .setTransform(bakedState->computedState.transform, transformFlags) .setModelViewIdentityEmptyBounds() .build(); // Note: don't pass dirty bounds here, so user must manage passing dirty bounds to renderer renderer->renderGlop(nullptr, clip, glop); -#else - GlopBuilder(renderer->mRenderState, renderer->mCaches, &glop) - .setRoundRectClipState(renderer->currentSnapshot()->roundRectClipState) - .setMeshTexturedIndexedQuads(texture.mesh(), texture.meshElementCount()) - .setFillTexturePaint(texture.getTexture(), textureFillFlags, paint, renderer->currentSnapshot()->alpha) - .setTransform(*(renderer->currentSnapshot()), transformFlags) - .setModelViewOffsetRect(0, 0, Rect()) - .build(); - renderer->renderGlop(glop); -#endif } /////////////////////////////////////////////////////////////////////////////// @@ -304,24 +294,23 @@ void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyp // Copy the glyph image, taking the mask format into account switch (format) { case SkMask::kA8_Format: { - uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0; 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 if (mGammaTable) { - for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY += srcStride) { + for (uint32_t cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY += srcStride) { row = cacheY * cacheWidth; cacheBuffer[row + startX - TEXTURE_BORDER_SIZE] = 0; - for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) { + for (uint32_t cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) { uint8_t tempCol = bitmapBuffer[bY + bX]; cacheBuffer[row + cacheX] = mGammaTable[tempCol]; } cacheBuffer[row + endX + TEXTURE_BORDER_SIZE - 1] = 0; } } else { - for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY += srcStride) { + for (uint32_t cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY += srcStride) { row = cacheY * cacheWidth; memcpy(&cacheBuffer[row + startX], &bitmapBuffer[bY], glyph.fWidth); cacheBuffer[row + startX - TEXTURE_BORDER_SIZE] = 0; diff --git a/libs/hwui/FontRenderer.h b/libs/hwui/FontRenderer.h index 1e59a966750e..dd9c40f89ad0 100644 --- a/libs/hwui/FontRenderer.h +++ b/libs/hwui/FontRenderer.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef ANDROID_HWUI_FONT_RENDERER_H -#define ANDROID_HWUI_FONT_RENDERER_H +#pragma once #include "font/FontUtil.h" #include "font/CacheTexture.h" @@ -48,31 +47,21 @@ namespace RSC { namespace android { namespace uirenderer { -#if HWUI_NEW_OPS class BakedOpState; class BakedOpRenderer; struct ClipBase; -#else -class OpenGLRenderer; -#endif class TextDrawFunctor { public: TextDrawFunctor( -#if HWUI_NEW_OPS BakedOpRenderer* renderer, const BakedOpState* bakedState, const ClipBase* clip, -#else - OpenGLRenderer* renderer, -#endif float x, float y, bool pureTranslate, - int alpha, SkXfermode::Mode mode, const SkPaint* paint) + int alpha, SkBlendMode mode, const SkPaint* paint) : renderer(renderer) -#if HWUI_NEW_OPS , bakedState(bakedState) , clip(clip) -#endif , x(x) , y(y) , pureTranslate(pureTranslate) @@ -83,18 +72,14 @@ public: void draw(CacheTexture& texture, bool linearFiltering); -#if HWUI_NEW_OPS BakedOpRenderer* renderer; const BakedOpState* bakedState; const ClipBase* clip; -#else - OpenGLRenderer* renderer; -#endif float x; float y; bool pureTranslate; int alpha; - SkXfermode::Mode mode; + SkBlendMode mode; const SkPaint* paint; }; @@ -235,5 +220,3 @@ private: }; // namespace uirenderer }; // namespace android - -#endif // ANDROID_HWUI_FONT_RENDERER_H diff --git a/libs/hwui/FrameBuilder.cpp b/libs/hwui/FrameBuilder.cpp index 7524ba0dcea6..a53a55a06964 100644 --- a/libs/hwui/FrameBuilder.cpp +++ b/libs/hwui/FrameBuilder.cpp @@ -120,7 +120,7 @@ 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, - SkRegion::kIntersect_Op); + SkClipOp::kIntersect); deferNodePropsAndOps(renderNode); mCanvasState.restore(); } @@ -262,7 +262,7 @@ void FrameBuilder::deferNodePropsAndOps(RenderNode& node) { Rect clipRect; properties.getClippingRectForFlags(clipFlags, &clipRect); mCanvasState.clipRect(clipRect.left, clipRect.top, clipRect.right, clipRect.bottom, - SkRegion::kIntersect_Op); + SkClipOp::kIntersect); } if (properties.getRevealClip().willClip()) { @@ -477,7 +477,7 @@ void FrameBuilder::deferProjectedChildren(const RenderNode& renderNode) { projectionReceiverOutline->transform( skCurrentTransform, &transformedMaskPath); - mCanvasState.setProjectionPathMask(mAllocator, &transformedMaskPath); + mCanvasState.setProjectionPathMask(&transformedMaskPath); } for (size_t i = 0; i < renderNode.mProjectedNodes.size(); i++) { @@ -608,11 +608,10 @@ void FrameBuilder::deferBitmapOp(const BitmapOp& op) { // MergingDrawBatch::canMergeWith() if (bakedState->computedState.transform.isSimple() && bakedState->computedState.transform.positiveScale() - && PaintUtils::getXfermodeDirect(op.paint) == SkXfermode::kSrcOver_Mode + && PaintUtils::getBlendModeDirect(op.paint) == SkBlendMode::kSrcOver && op.bitmap->colorType() != kAlpha_8_SkColorType && hasMergeableClip(*bakedState)) { mergeid_t mergeId = reinterpret_cast<mergeid_t>(op.bitmap->getGenerationID()); - // TODO: AssetAtlas in mergeId currentLayer().deferMergeableOp(mAllocator, bakedState, OpBatchType::Bitmap, mergeId); } else { currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Bitmap); @@ -632,7 +631,7 @@ void FrameBuilder::deferBitmapRectOp(const BitmapRectOp& op) { } void FrameBuilder::deferVectorDrawableOp(const VectorDrawableOp& op) { - const SkBitmap& bitmap = op.vectorDrawable->getBitmapUpdateIfDirty(); + Bitmap& bitmap = op.vectorDrawable->getBitmapUpdateIfDirty(); SkPaint* paint = op.vectorDrawable->getPaint(); const BitmapRectOp* resolvedOp = mAllocator.create_trivial<BitmapRectOp>(op.unmappedBounds, op.localMatrix, @@ -684,10 +683,9 @@ void FrameBuilder::deferPatchOp(const PatchOp& op) { if (!bakedState) return; // quick rejected if (bakedState->computedState.transform.isPureTranslate() - && PaintUtils::getXfermodeDirect(op.paint) == SkXfermode::kSrcOver_Mode + && PaintUtils::getBlendModeDirect(op.paint) == SkBlendMode::kSrcOver && hasMergeableClip(*bakedState)) { mergeid_t mergeId = reinterpret_cast<mergeid_t>(op.bitmap->getGenerationID()); - // TODO: AssetAtlas in mergeId // Only use the MergedPatch batchId when merged, so Bitmap+Patch don't try to merge together currentLayer().deferMergeableOp(mAllocator, bakedState, OpBatchType::MergedPatch, mergeId); @@ -752,7 +750,7 @@ void FrameBuilder::deferTextOp(const TextOp& op) { batchid_t batchId = textBatchId(*(op.paint)); if (bakedState->computedState.transform.isPureTranslate() - && PaintUtils::getXfermodeDirect(op.paint) == SkXfermode::kSrcOver_Mode + && PaintUtils::getBlendModeDirect(op.paint) == SkBlendMode::kSrcOver && hasMergeableClip(*bakedState)) { mergeid_t mergeId = reinterpret_cast<mergeid_t>(op.paint->getColor()); currentLayer().deferMergeableOp(mAllocator, bakedState, batchId, mergeId); diff --git a/libs/hwui/FrameInfoVisualizer.cpp b/libs/hwui/FrameInfoVisualizer.cpp index adadd32a2fc0..d3adc32da848 100644 --- a/libs/hwui/FrameInfoVisualizer.cpp +++ b/libs/hwui/FrameInfoVisualizer.cpp @@ -15,11 +15,8 @@ */ #include "FrameInfoVisualizer.h" -#if HWUI_NEW_OPS #include "BakedOpRenderer.h" -#else -#include "OpenGLRenderer.h" -#endif +#include "IProfileRenderer.h" #include "utils/Color.h" #include <cutils/compiler.h> @@ -92,7 +89,7 @@ void FrameInfoVisualizer::unionDirty(SkRect* dirty) { } } -void FrameInfoVisualizer::draw(ContentRenderer* renderer) { +void FrameInfoVisualizer::draw(IProfileRenderer& renderer) { RETURN_IF_DISABLED(); if (mShowDirtyRegions) { @@ -100,8 +97,8 @@ void FrameInfoVisualizer::draw(ContentRenderer* 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); } } @@ -115,7 +112,7 @@ void FrameInfoVisualizer::draw(ContentRenderer* renderer) { info.markSwapBuffers(); info.markFrameCompleted(); - initializeRects(renderer->getViewportHeight(), renderer->getViewportWidth()); + initializeRects(renderer.getViewportHeight(), renderer.getViewportWidth()); drawGraph(renderer); drawThreshold(renderer); } @@ -198,26 +195,26 @@ void FrameInfoVisualizer::nextBarSegment(FrameInfoIndex start, FrameInfoIndex en } } -void FrameInfoVisualizer::drawGraph(ContentRenderer* renderer) { +void FrameInfoVisualizer::drawGraph(IProfileRenderer& renderer) { SkPaint paint; for (size_t i = 0; i < Bar.size(); i++) { nextBarSegment(Bar[i].start, Bar[i].end); paint.setColor(Bar[i].color & BAR_FAST_MASK); - renderer->drawRects(mFastRects.get(), mNumFastRects * 4, &paint); + renderer.drawRects(mFastRects.get(), mNumFastRects * 4, paint); paint.setColor(Bar[i].color & BAR_JANKY_MASK); - renderer->drawRects(mJankyRects.get(), mNumJankyRects * 4, &paint); + renderer.drawRects(mJankyRects.get(), mNumJankyRects * 4, paint); } } -void FrameInfoVisualizer::drawThreshold(ContentRenderer* renderer) { +void FrameInfoVisualizer::drawThreshold(IProfileRenderer& renderer) { SkPaint paint; paint.setColor(THRESHOLD_COLOR); - float yLocation = renderer->getViewportHeight() - (FRAME_THRESHOLD * mVerticalUnit); - renderer->drawRect(0.0f, + float yLocation = renderer.getViewportHeight() - (FRAME_THRESHOLD * mVerticalUnit); + renderer.drawRect(0.0f, yLocation - mThresholdStroke/2, - renderer->getViewportWidth(), + renderer.getViewportWidth(), yLocation + mThresholdStroke/2, - &paint); + paint); } bool FrameInfoVisualizer::consumeProperties() { diff --git a/libs/hwui/FrameInfoVisualizer.h b/libs/hwui/FrameInfoVisualizer.h index 719d0f8c5032..b98f50101483 100644 --- a/libs/hwui/FrameInfoVisualizer.h +++ b/libs/hwui/FrameInfoVisualizer.h @@ -13,8 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#ifndef DRAWPROFILER_H -#define DRAWPROFILER_H + +#pragma once #include "FrameInfo.h" #include "Properties.h" @@ -28,13 +28,7 @@ namespace android { namespace uirenderer { -#if HWUI_NEW_OPS -class BakedOpRenderer; -typedef BakedOpRenderer ContentRenderer; -#else -class OpenGLRenderer; -typedef OpenGLRenderer ContentRenderer; -#endif +class IProfileRenderer; // TODO: This is a bit awkward as it needs to match the thing in CanvasContext // A better abstraction here would be nice but iterators are painful @@ -52,7 +46,7 @@ public: void setDensity(float density); void unionDirty(SkRect* dirty); - void draw(ContentRenderer* renderer); + void draw(IProfileRenderer& renderer); void dumpData(int fd); @@ -62,8 +56,8 @@ private: void initializeRects(const int baseline, const int width); void nextBarSegment(FrameInfoIndex start, FrameInfoIndex end); - void drawGraph(ContentRenderer* renderer); - void drawThreshold(ContentRenderer* renderer); + void drawGraph(IProfileRenderer& renderer); + void drawThreshold(IProfileRenderer& renderer); inline float durationMS(size_t index, FrameInfoIndex start, FrameInfoIndex end) { float duration = mFrameSource[index].duration(start, end) * 0.000001f; @@ -93,5 +87,3 @@ private: } /* namespace uirenderer */ } /* namespace android */ - -#endif /* DRAWPROFILER_H */ diff --git a/libs/hwui/GammaFontRenderer.cpp b/libs/hwui/GammaFontRenderer.cpp index 96cac86386b5..8aff0a24b4f0 100644 --- a/libs/hwui/GammaFontRenderer.cpp +++ b/libs/hwui/GammaFontRenderer.cpp @@ -24,12 +24,13 @@ namespace uirenderer { GammaFontRenderer::GammaFontRenderer() { INIT_LOGD("Creating lookup gamma font renderer"); +#ifndef ANDROID_ENABLE_LINEAR_BLENDING // Compute the gamma tables const float gamma = 1.0f / Properties::textGamma; - for (uint32_t i = 0; i <= 255; i++) { mGammaTable[i] = uint8_t((float)::floor(pow(i / 255.0f, gamma) * 255.0f + 0.5f)); } +#endif } void GammaFontRenderer::endPrecaching() { diff --git a/libs/hwui/GammaFontRenderer.h b/libs/hwui/GammaFontRenderer.h index bd27a1a72060..c9cf69bc7d9f 100644 --- a/libs/hwui/GammaFontRenderer.h +++ b/libs/hwui/GammaFontRenderer.h @@ -18,11 +18,6 @@ #define ANDROID_HWUI_GAMMA_FONT_RENDERER_H #include "FontRenderer.h" -#include "Program.h" - -#include <SkPaint.h> - -#include <utils/String8.h> namespace android { namespace uirenderer { @@ -43,7 +38,11 @@ public: FontRenderer& getFontRenderer() { if (!mRenderer) { - mRenderer.reset(new FontRenderer(&mGammaTable[0])); + const uint8_t* table = nullptr; +#ifndef ANDROID_ENABLE_LINEAR_BLENDING + table = &mGammaTable[0]; +#endif + mRenderer.reset(new FontRenderer(table)); } return *mRenderer; } @@ -64,7 +63,9 @@ public: private: std::unique_ptr<FontRenderer> mRenderer; +#ifndef ANDROID_ENABLE_LINEAR_BLENDING uint8_t mGammaTable[256]; +#endif }; }; // namespace uirenderer diff --git a/libs/hwui/GlLayer.cpp b/libs/hwui/GlLayer.cpp new file mode 100644 index 000000000000..c0ab895260ab --- /dev/null +++ b/libs/hwui/GlLayer.cpp @@ -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. + */ + +#include "GlLayer.h" + +#include "Caches.h" +#include "RenderNode.h" +#include "renderstate/RenderState.h" +#include "utils/TraceUtils.h" + +#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()) + +namespace android { +namespace uirenderer { + +GlLayer::GlLayer(RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight) + : Layer(renderState, Api::OpenGL) + , caches(Caches::getInstance()) + , texture(caches) { + texture.mWidth = layerWidth; + texture.mHeight = layerHeight; +} + +GlLayer::~GlLayer() { + if (texture.mId) { + texture.deleteTexture(); + } +} + +void GlLayer::onGlContextLost() { + texture.deleteTexture(); +} + +void GlLayer::bindTexture() const { + if (texture.mId) { + caches.textureState().bindTexture(texture.target(), texture.mId); + } +} + +void GlLayer::generateTexture() { + if (!texture.mId) { + glGenTextures(1, &texture.mId); + } +} + +void GlLayer::clearTexture() { + // There's a rare possibility that Caches could have been destroyed already + // since this method is queued up as a task. + // Since this is a reset method, treat this as non-fatal. + if (caches.isInitialized()) { + caches.textureState().unbindTexture(texture.mId); + } + texture.mId = 0; +} + +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/GlLayer.h b/libs/hwui/GlLayer.h new file mode 100644 index 000000000000..54bf5adc3ace --- /dev/null +++ b/libs/hwui/GlLayer.h @@ -0,0 +1,112 @@ +/* + * 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 "Layer.h" + +#include "Texture.h" + +namespace android { +namespace uirenderer { + +// Forward declarations +class Caches; + +/** + * A layer has dimensions and is backed by an OpenGL texture or FBO. + */ +class GlLayer : public Layer { +public: + GlLayer(RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight); + virtual ~GlLayer(); + + uint32_t getWidth() const override { + return texture.mWidth; + } + + uint32_t getHeight() const override { + return texture.mHeight; + } + + void setSize(uint32_t width, uint32_t height) override { + texture.updateSize(width, height, texture.internalFormat(), texture.format(), + texture.target()); + } + + void setBlend(bool blend) override { + texture.blend = blend; + } + + bool isBlend() const override { + return texture.blend; + } + + inline GLuint getTextureId() const { + return texture.id(); + } + + inline Texture& getTexture() { + return texture; + } + + inline GLenum getRenderTarget() const { + return texture.target(); + } + + inline void setRenderTarget(GLenum renderTarget) { + texture.mTarget = renderTarget; + } + + inline bool isRenderable() const { + return texture.target() != GL_NONE; + } + + void setWrap(GLenum wrap, bool bindTexture = false, bool force = false) { + texture.setWrap(wrap, bindTexture, force); + } + + void setFilter(GLenum filter, bool bindTexture = false, bool force = false) { + texture.setFilter(filter, bindTexture, force); + } + + void bindTexture() const; + void generateTexture(); + + /** + * When the caller frees the texture itself, the caller + * must call this method to tell this layer that it lost + * the texture. + */ + void clearTexture(); + + /** + * Lost the GL context but the layer is still around, mark it invalid internally + * so the dtor knows not to do any GL work + */ + void onGlContextLost(); + +private: + Caches& caches; + + /** + * The texture backing this layer. + */ + Texture texture; +}; // struct GlLayer + +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/Glop.h b/libs/hwui/Glop.h index 6433c86908e7..34c7934db198 100644 --- a/libs/hwui/Glop.h +++ b/libs/hwui/Glop.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef ANDROID_HWUI_GLOP_H -#define ANDROID_HWUI_GLOP_H +#pragma once #include "FloatColor.h" #include "Matrix.h" @@ -26,7 +25,6 @@ #include <GLES2/gl2.h> #include <GLES2/gl2ext.h> -#include <SkXfermode.h> namespace android { namespace uirenderer { @@ -68,7 +66,7 @@ namespace TransformFlags { OffsetByFudgeFactor = 1 << 0, // Canvas transform isn't applied to the mesh at draw time, - //since it's already built in. + // since it's already built in. MeshIgnoresCanvasTransform = 1 << 1, // TODO: remove for HWUI_NEW_OPS }; }; @@ -120,7 +118,6 @@ public: struct TextureData { Texture* texture; - GLenum target; GLenum filter; GLenum clamp; Matrix4* textureTransform; @@ -168,14 +165,6 @@ public: GLenum dst; } blend; -#if !HWUI_NEW_OPS - /** - * Bounds of the drawing command in layer space. Only mapped into layer - * space once GlopBuilder::build() is called. - */ - Rect bounds; // TODO: remove for HWUI_NEW_OPS -#endif - /** * Additional render state to enumerate: * - scissor + (bits for whether each of LTRB needed?) @@ -185,5 +174,3 @@ public: } /* namespace uirenderer */ } /* namespace android */ - -#endif // ANDROID_HWUI_GLOP_H diff --git a/libs/hwui/GlopBuilder.cpp b/libs/hwui/GlopBuilder.cpp index e502725ddb09..8a6e038d8e0d 100644 --- a/libs/hwui/GlopBuilder.cpp +++ b/libs/hwui/GlopBuilder.cpp @@ -16,9 +16,12 @@ #include "GlopBuilder.h" #include "Caches.h" +#include "GlLayer.h" #include "Glop.h" +#include "Layer.h" #include "Matrix.h" #include "Patch.h" +#include "PathCache.h" #include "renderstate/MeshState.h" #include "renderstate/RenderState.h" #include "SkiaShader.h" @@ -165,20 +168,6 @@ GlopBuilder& GlopBuilder::setMeshTexturedIndexedQuads(TextureVertex* vertexData, return *this; } -GlopBuilder& GlopBuilder::setMeshTexturedMesh(TextureVertex* vertexData, int elementCount) { - TRIGGER_STAGE(kMeshStage); - - mOutGlop->mesh.primitiveMode = GL_TRIANGLES; - mOutGlop->mesh.indices = { 0, 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) { TRIGGER_STAGE(kMeshStage); @@ -232,19 +221,19 @@ GlopBuilder& GlopBuilder::setMeshPatchQuads(const Patch& patch) { //////////////////////////////////////////////////////////////////////////////// void GlopBuilder::setFill(int color, float alphaScale, - SkXfermode::Mode mode, Blend::ModeOrderSwap modeUsage, + SkBlendMode mode, Blend::ModeOrderSwap modeUsage, const SkShader* shader, const SkColorFilter* colorFilter) { - if (mode != SkXfermode::kClear_Mode) { - float alpha = (SkColorGetA(color) / 255.0f) * alphaScale; + if (mode != SkBlendMode::kClear) { if (!shader) { - float colorScale = alpha / 255.0f; - mOutGlop->fill.color = { - colorScale * SkColorGetR(color), - colorScale * SkColorGetG(color), - colorScale * SkColorGetB(color), - alpha - }; + FloatColor c; + c.set(color); + c.r *= alphaScale; + c.g *= alphaScale; + c.b *= alphaScale; + c.a *= alphaScale; + mOutGlop->fill.color = c; } else { + float alpha = (SkColorGetA(color) / 255.0f) * alphaScale; mOutGlop->fill.color = { 1, 1, 1, alpha }; } } else { @@ -258,8 +247,8 @@ void GlopBuilder::setFill(int color, float alphaScale, || mOutGlop->roundRectClipState || PaintUtils::isBlendedShader(shader) || PaintUtils::isBlendedColorFilter(colorFilter) - || mode != SkXfermode::kSrcOver_Mode) { - if (CC_LIKELY(mode <= SkXfermode::kScreen_Mode)) { + || mode != SkBlendMode::kSrcOver) { + if (CC_LIKELY(mode <= SkBlendMode::kScreen)) { Blend::getFactors(mode, modeUsage, &mOutGlop->blend.src, &mOutGlop->blend.dst); } else { @@ -274,7 +263,7 @@ void GlopBuilder::setFill(int color, float alphaScale, // blending in shader, don't enable } else { // unsupported - Blend::getFactors(SkXfermode::kSrcOver_Mode, modeUsage, + Blend::getFactors(SkBlendMode::kSrcOver, modeUsage, &mOutGlop->blend.src, &mOutGlop->blend.dst); } } @@ -283,20 +272,12 @@ void GlopBuilder::setFill(int color, float alphaScale, if (colorFilter) { SkColor color; - SkXfermode::Mode mode; + SkBlendMode bmode; SkScalar srcColorMatrix[20]; - if (colorFilter->asColorMode(&color, &mode)) { + if (colorFilter->asColorMode(&color, &bmode)) { mOutGlop->fill.filterMode = mDescription.colorOp = ProgramDescription::ColorFilterMode::Blend; - mDescription.colorMode = mode; - - const float alpha = SkColorGetA(color) / 255.0f; - float colorScale = alpha / 255.0f; - mOutGlop->fill.filter.color = { - colorScale * SkColorGetR(color), - colorScale * SkColorGetG(color), - colorScale * SkColorGetB(color), - alpha, - }; + mDescription.colorMode = bmode; + mOutGlop->fill.filter.color.set(color); } else if (colorFilter->asColorMatrix(srcColorMatrix)) { mOutGlop->fill.filterMode = mDescription.colorOp = ProgramDescription::ColorFilterMode::Matrix; @@ -309,10 +290,10 @@ 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] = srcColorMatrix[4] / 255.0f; - colorVector[1] = srcColorMatrix[9] / 255.0f; - colorVector[2] = srcColorMatrix[14] / 255.0f; - colorVector[3] = srcColorMatrix[19] / 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 } else { LOG_ALWAYS_FATAL("unsupported ColorFilter"); } @@ -328,8 +309,7 @@ GlopBuilder& GlopBuilder::setFillTexturePaint(Texture& texture, GLenum filter = (textureFillFlags & TextureFillFlags::ForceFilter) ? GL_LINEAR : PaintUtils::getFilter(paint); - mOutGlop->fill.texture = { &texture, - GL_TEXTURE_2D, filter, GL_CLAMP_TO_EDGE, nullptr }; + mOutGlop->fill.texture = { &texture, filter, GL_CLAMP_TO_EDGE, nullptr }; if (paint) { int color = paint->getColor(); @@ -341,7 +321,7 @@ GlopBuilder& GlopBuilder::setFillTexturePaint(Texture& texture, shader = nullptr; } setFill(color, alphaScale, - PaintUtils::getXfermode(paint->getXfermode()), Blend::ModeOrderSwap::NoSwap, + paint->getBlendMode(), Blend::ModeOrderSwap::NoSwap, shader, paint->getColorFilter()); } else { mOutGlop->fill.color = { alphaScale, alphaScale, alphaScale, alphaScale }; @@ -350,7 +330,7 @@ GlopBuilder& GlopBuilder::setFillTexturePaint(Texture& texture, || (mOutGlop->mesh.vertices.attribFlags & VertexAttribFlags::Alpha) || texture.blend || mOutGlop->roundRectClipState) { - Blend::getFactors(SkXfermode::kSrcOver_Mode, Blend::ModeOrderSwap::NoSwap, + Blend::getFactors(SkBlendMode::kSrcOver, Blend::ModeOrderSwap::NoSwap, &mOutGlop->blend.src, &mOutGlop->blend.dst); } else { mOutGlop->blend = { GL_ZERO, GL_ZERO }; @@ -372,15 +352,15 @@ GlopBuilder& GlopBuilder::setFillPaint(const SkPaint& paint, float alphaScale, b if (CC_LIKELY(!shadowInterp)) { mOutGlop->fill.texture = { - nullptr, GL_INVALID_ENUM, GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr }; + nullptr, GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr }; } else { mOutGlop->fill.texture = { - mCaches.textureState().getShadowLutTexture(), GL_TEXTURE_2D, + mCaches.textureState().getShadowLutTexture(), GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr }; } setFill(paint.getColor(), alphaScale, - PaintUtils::getXfermode(paint.getXfermode()), Blend::ModeOrderSwap::NoSwap, + paint.getBlendMode(), Blend::ModeOrderSwap::NoSwap, paint.getShader(), paint.getColorFilter()); mDescription.useShadowAlphaInterp = shadowInterp; mDescription.modulate = mOutGlop->fill.color.a < 1.0f; @@ -393,10 +373,10 @@ GlopBuilder& GlopBuilder::setFillPathTexturePaint(PathTexture& texture, REQUIRE_STAGES(kMeshStage | kRoundRectClipStage); //specify invalid filter/clamp, since these are always static for PathTextures - mOutGlop->fill.texture = { &texture, GL_TEXTURE_2D, GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr }; + mOutGlop->fill.texture = { &texture, GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr }; setFill(paint.getColor(), alphaScale, - PaintUtils::getXfermode(paint.getXfermode()), Blend::ModeOrderSwap::NoSwap, + paint.getBlendMode(), Blend::ModeOrderSwap::NoSwap, paint.getShader(), paint.getColorFilter()); mDescription.hasAlpha8Texture = true; @@ -410,7 +390,7 @@ GlopBuilder& GlopBuilder::setFillShadowTexturePaint(ShadowTexture& texture, int REQUIRE_STAGES(kMeshStage | kRoundRectClipStage); //specify invalid filter/clamp, since these are always static for ShadowTextures - mOutGlop->fill.texture = { &texture, GL_TEXTURE_2D, GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr }; + mOutGlop->fill.texture = { &texture, GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr }; const int ALPHA_BITMASK = SK_ColorBLACK; const int COLOR_BITMASK = ~ALPHA_BITMASK; @@ -420,7 +400,7 @@ GlopBuilder& GlopBuilder::setFillShadowTexturePaint(ShadowTexture& texture, int } setFill(shadowColor, alphaScale, - PaintUtils::getXfermode(paint.getXfermode()), Blend::ModeOrderSwap::NoSwap, + paint.getBlendMode(), Blend::ModeOrderSwap::NoSwap, paint.getShader(), paint.getColorFilter()); mDescription.hasAlpha8Texture = true; @@ -432,8 +412,8 @@ GlopBuilder& GlopBuilder::setFillBlack() { TRIGGER_STAGE(kFillStage); REQUIRE_STAGES(kMeshStage | kRoundRectClipStage); - mOutGlop->fill.texture = { nullptr, GL_INVALID_ENUM, GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr }; - setFill(SK_ColorBLACK, 1.0f, SkXfermode::kSrcOver_Mode, Blend::ModeOrderSwap::NoSwap, + 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; } @@ -442,19 +422,18 @@ GlopBuilder& GlopBuilder::setFillClear() { TRIGGER_STAGE(kFillStage); REQUIRE_STAGES(kMeshStage | kRoundRectClipStage); - mOutGlop->fill.texture = { nullptr, GL_INVALID_ENUM, GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr }; - setFill(SK_ColorBLACK, 1.0f, SkXfermode::kClear_Mode, Blend::ModeOrderSwap::NoSwap, + 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, SkXfermode::Mode mode, Blend::ModeOrderSwap modeUsage) { + float alpha, SkBlendMode mode, Blend::ModeOrderSwap modeUsage) { TRIGGER_STAGE(kFillStage); REQUIRE_STAGES(kMeshStage | kRoundRectClipStage); - mOutGlop->fill.texture = { &texture, - GL_TEXTURE_2D, 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); @@ -462,12 +441,12 @@ GlopBuilder& GlopBuilder::setFillLayer(Texture& texture, const SkColorFilter* co return *this; } -GlopBuilder& GlopBuilder::setFillTextureLayer(Layer& layer, float alpha) { +GlopBuilder& GlopBuilder::setFillTextureLayer(GlLayer& layer, float alpha) { TRIGGER_STAGE(kFillStage); REQUIRE_STAGES(kMeshStage | kRoundRectClipStage); mOutGlop->fill.texture = { &(layer.getTexture()), - layer.getRenderTarget(), GL_LINEAR, GL_CLAMP_TO_EDGE, &layer.getTexTransform() }; + GL_LINEAR, GL_CLAMP_TO_EDGE, &layer.getTexTransform() }; setFill(SK_ColorWHITE, alpha, layer.getMode(), Blend::ModeOrderSwap::NoSwap, nullptr, layer.getColorFilter()); @@ -481,11 +460,9 @@ GlopBuilder& GlopBuilder::setFillExternalTexture(Texture& texture, Matrix4& text TRIGGER_STAGE(kFillStage); REQUIRE_STAGES(kMeshStage | kRoundRectClipStage); - mOutGlop->fill.texture = { &texture, - GL_TEXTURE_EXTERNAL_OES, GL_LINEAR, GL_CLAMP_TO_EDGE, - &textureTransform }; + mOutGlop->fill.texture = { &texture, GL_LINEAR, GL_CLAMP_TO_EDGE, &textureTransform }; - setFill(SK_ColorWHITE, 1.0f, SkXfermode::kSrc_Mode, Blend::ModeOrderSwap::NoSwap, + setFill(SK_ColorWHITE, 1.0f, SkBlendMode::kSrc, Blend::ModeOrderSwap::NoSwap, nullptr, nullptr); mDescription.modulate = mOutGlop->fill.color.a < 1.0f; @@ -493,6 +470,13 @@ GlopBuilder& GlopBuilder::setFillExternalTexture(Texture& texture, Matrix4& text return *this; } +GlopBuilder& GlopBuilder::setGammaCorrection(bool enabled) { + REQUIRE_STAGES(kFillStage); + + mDescription.hasGammaCorrection = enabled; + return *this; +} + //////////////////////////////////////////////////////////////////////////////// // Transform //////////////////////////////////////////////////////////////////////////////// @@ -514,9 +498,6 @@ GlopBuilder& GlopBuilder::setModelViewMapUnitToRect(const Rect destination) { mOutGlop->transform.modelView.loadTranslate(destination.left, destination.top, 0.0f); mOutGlop->transform.modelView.scale(destination.getWidth(), destination.getHeight(), 1.0f); -#if !HWUI_NEW_OPS - mOutGlop->bounds = destination; -#endif return *this; } @@ -540,9 +521,6 @@ GlopBuilder& GlopBuilder::setModelViewMapUnitToRectSnap(const Rect destination) mOutGlop->transform.modelView.loadTranslate(left, top, 0.0f); mOutGlop->transform.modelView.scale(destination.getWidth(), destination.getHeight(), 1.0f); -#if !HWUI_NEW_OPS - mOutGlop->bounds = destination; -#endif return *this; } @@ -550,10 +528,6 @@ GlopBuilder& GlopBuilder::setModelViewOffsetRect(float offsetX, float offsetY, c TRIGGER_STAGE(kModelViewStage); mOutGlop->transform.modelView.loadTranslate(offsetX, offsetY, 0.0f); -#if !HWUI_NEW_OPS - mOutGlop->bounds = source; - mOutGlop->bounds.translate(offsetX, offsetY); -#endif return *this; } @@ -573,10 +547,6 @@ GlopBuilder& GlopBuilder::setModelViewOffsetRectSnap(float offsetX, float offset } mOutGlop->transform.modelView.loadTranslate(offsetX, offsetY, 0.0f); -#if !HWUI_NEW_OPS - mOutGlop->bounds = source; - mOutGlop->bounds.translate(offsetX, offsetY); -#endif return *this; } @@ -630,7 +600,7 @@ void verify(const ProgramDescription& description, const Glop& glop) { void GlopBuilder::build() { REQUIRE_STAGES(kAllStages); if (mOutGlop->mesh.vertices.attribFlags & VertexAttribFlags::TextureCoord) { - if (mOutGlop->fill.texture.target == GL_TEXTURE_2D) { + if (mOutGlop->fill.texture.texture->target() == GL_TEXTURE_2D) { mDescription.hasTexture = true; } else { mDescription.hasExternalTexture = true; @@ -676,9 +646,6 @@ void GlopBuilder::build() { // Final step: populate program and map bounds into render target space mOutGlop->fill.program = mCaches.programCache.get(mDescription); -#if !HWUI_NEW_OPS - mOutGlop->transform.meshTransform().mapRect(mOutGlop->bounds); -#endif } void GlopBuilder::dump(const Glop& glop) { @@ -698,7 +665,8 @@ void GlopBuilder::dump(const Glop& glop) { ALOGD(" program %p", fill.program); if (fill.texture.texture) { ALOGD(" texture %p, target %d, filter %d, clamp %d", - fill.texture.texture, fill.texture.target, fill.texture.filter, fill.texture.clamp); + fill.texture.texture, fill.texture.texture->target(), + fill.texture.filter, fill.texture.clamp); if (fill.texture.textureTransform) { fill.texture.textureTransform->dump("texture transform"); } @@ -718,9 +686,6 @@ void GlopBuilder::dump(const Glop& glop) { ALOGD_IF(glop.roundRectClipState, "Glop RRCS %p", glop.roundRectClipState); ALOGD("Glop blend %d %d", glop.blend.src, glop.blend.dst); -#if !HWUI_NEW_OPS - ALOGD("Glop bounds " RECT_STRING, RECT_ARGS(glop.bounds)); -#endif } } /* namespace uirenderer */ diff --git a/libs/hwui/GlopBuilder.h b/libs/hwui/GlopBuilder.h index 1c520c26110e..87b1568ed72b 100644 --- a/libs/hwui/GlopBuilder.h +++ b/libs/hwui/GlopBuilder.h @@ -13,11 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#ifndef RENDERSTATE_GLOPBUILDER_H -#define RENDERSTATE_GLOPBUILDER_H + +#pragma once #include "Glop.h" -#include "OpenGLRenderer.h" #include "Program.h" #include "renderstate/Blend.h" #include "utils/Macros.h" @@ -29,10 +28,15 @@ namespace android { namespace uirenderer { class Caches; +class GlLayer; class Matrix4; +class Patch; class RenderState; class Texture; +class UvMapper; class VertexBuffer; +struct PathTexture; +struct ShadowTexture; namespace TextureFillFlags { enum { @@ -53,7 +57,6 @@ public: GlopBuilder& setMeshTexturedUvQuad(const UvMapper* uvMapper, const Rect uvs); GlopBuilder& setMeshVertexBuffer(const VertexBuffer& vertexBuffer); GlopBuilder& setMeshIndexedQuads(Vertex* vertexData, int quadCount); - GlopBuilder& setMeshTexturedMesh(TextureVertex* vertexData, int elementCount); // TODO: delete GlopBuilder& setMeshColoredTexturedMesh(ColorTextureVertex* vertexData, int elementCount); // TODO: use indexed quads GlopBuilder& setMeshTexturedIndexedQuads(TextureVertex* vertexData, int elementCount); // TODO: take quadCount GlopBuilder& setMeshPatchQuads(const Patch& patch); @@ -68,16 +71,12 @@ public: GlopBuilder& setFillBlack(); GlopBuilder& setFillClear(); GlopBuilder& setFillLayer(Texture& texture, const SkColorFilter* colorFilter, - float alpha, SkXfermode::Mode mode, Blend::ModeOrderSwap modeUsage); - GlopBuilder& setFillTextureLayer(Layer& layer, float alpha); - // TODO: Texture should probably know and own its target. - // setFillLayer() forces it to GL_TEXTURE which isn't always correct. - // Similarly setFillLayer normally forces its own wrap & filter mode + 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); - GlopBuilder& setTransform(const Snapshot& snapshot, const int transformFlags) { - return setTransform(*snapshot.transform, transformFlags); - } GlopBuilder& setTransform(const Matrix4& canvas, const int transformFlags); GlopBuilder& setModelViewMapUnitToRect(const Rect destination); @@ -106,12 +105,14 @@ public: GlopBuilder& setRoundRectClipState(const RoundRectClipState* roundRectClipState); + GlopBuilder& setGammaCorrection(bool enabled); + void build(); static void dump(const Glop& glop); private: void setFill(int color, float alphaScale, - SkXfermode::Mode mode, Blend::ModeOrderSwap modeUsage, + SkBlendMode mode, Blend::ModeOrderSwap modeUsage, const SkShader* shader, const SkColorFilter* colorFilter); enum StageFlags { @@ -133,5 +134,3 @@ private: } /* namespace uirenderer */ } /* namespace android */ - -#endif // RENDERSTATE_GLOPBUILDER_H diff --git a/libs/hwui/GpuMemoryTracker.cpp b/libs/hwui/GpuMemoryTracker.cpp index 4fb57019264d..a52ec8738015 100644 --- a/libs/hwui/GpuMemoryTracker.cpp +++ b/libs/hwui/GpuMemoryTracker.cpp @@ -67,13 +67,13 @@ void GpuMemoryTracker::stopTrackingObject() { gObjectStats[static_cast<int>(mType)].count--; } -void GpuMemoryTracker::onGLContextCreated() { - LOG_ALWAYS_FATAL_IF(gGpuThread != 0, "We already have a GL thread? " - "current = %lu, gl thread = %lu", pthread_self(), gGpuThread); +void GpuMemoryTracker::onGpuContextCreated() { + LOG_ALWAYS_FATAL_IF(gGpuThread != 0, "We already have a gpu thread? " + "current = %lu, gpu thread = %lu", pthread_self(), gGpuThread); gGpuThread = pthread_self(); } -void GpuMemoryTracker::onGLContextDestroyed() { +void GpuMemoryTracker::onGpuContextDestroyed() { gGpuThread = 0; if (CC_UNLIKELY(gObjectSet.size() > 0)) { std::stringstream os; diff --git a/libs/hwui/GpuMemoryTracker.h b/libs/hwui/GpuMemoryTracker.h index 352f3d785c54..18e2330668b0 100644 --- a/libs/hwui/GpuMemoryTracker.h +++ b/libs/hwui/GpuMemoryTracker.h @@ -44,8 +44,8 @@ public: GpuObjectType objectType() { return mType; } int objectSize() { return mSize; } - static void onGLContextCreated(); - static void onGLContextDestroyed(); + static void onGpuContextCreated(); + static void onGpuContextDestroyed(); static void dump(); static void dump(std::ostream& stream); static int getInstanceCount(GpuObjectType type); diff --git a/libs/hwui/GradientCache.cpp b/libs/hwui/GradientCache.cpp index c8f5e9435594..1dad58fd64b9 100644 --- a/libs/hwui/GradientCache.cpp +++ b/libs/hwui/GradientCache.cpp @@ -67,7 +67,8 @@ GradientCache::GradientCache(Extensions& extensions) , mSize(0) , mMaxSize(Properties::gradientCacheSize) , mUseFloatTexture(extensions.hasFloatTextures()) - , mHasNpot(extensions.hasNPot()){ + , mHasNpot(extensions.hasNPot()) + , mHasSRGB(extensions.hasSRGB()) { glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize); mCache.setOnEntryRemovedListener(this); @@ -176,71 +177,61 @@ Texture* GradientCache::addLinearGradient(GradientCacheEntry& gradient, size_t GradientCache::bytesPerPixel() const { // We use 4 channels (RGBA) - return 4 * (mUseFloatTexture ? sizeof(float) : sizeof(uint8_t)); -} - -void GradientCache::splitToBytes(uint32_t inColor, GradientColor& outColor) const { - outColor.r = (inColor >> 16) & 0xff; - outColor.g = (inColor >> 8) & 0xff; - outColor.b = (inColor >> 0) & 0xff; - outColor.a = (inColor >> 24) & 0xff; + return 4 * (mUseFloatTexture ? /* fp16 */ 2 : sizeof(uint8_t)); } -void GradientCache::splitToFloats(uint32_t inColor, GradientColor& outColor) const { - outColor.r = ((inColor >> 16) & 0xff) / 255.0f; - outColor.g = ((inColor >> 8) & 0xff) / 255.0f; - outColor.b = ((inColor >> 0) & 0xff) / 255.0f; - outColor.a = ((inColor >> 24) & 0xff) / 255.0f; +size_t GradientCache::sourceBytesPerPixel() const { + // We use 4 channels (RGBA) and upload from floats (not half floats) + return 4 * (mUseFloatTexture ? sizeof(float) : sizeof(uint8_t)); } -void GradientCache::mixBytes(GradientColor& start, GradientColor& 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; - const float alpha = start.a * oppAmount + end.a * amount; - const float a = alpha / 255.0f; - - *dst++ = uint8_t(a * (start.r * oppAmount + end.r * amount)); - *dst++ = uint8_t(a * (start.g * oppAmount + end.g * amount)); - *dst++ = uint8_t(a * (start.b * oppAmount + end.b * amount)); - *dst++ = uint8_t(alpha); + float a = start.a * oppAmount + end.a * amount; + *dst++ = uint8_t(a * OECF_sRGB((start.r * oppAmount + end.r * amount)) * 255.0f); + *dst++ = uint8_t(a * OECF_sRGB((start.g * oppAmount + end.g * amount)) * 255.0f); + *dst++ = uint8_t(a * OECF_sRGB((start.b * oppAmount + end.b * amount)) * 255.0f); + *dst++ = uint8_t(a * 255.0f); } -void GradientCache::mixFloats(GradientColor& start, GradientColor& 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; - const float a = start.a * oppAmount + end.a * amount; - + float a = start.a * oppAmount + end.a * amount; float* d = (float*) dst; +#ifdef ANDROID_ENABLE_LINEAR_BLENDING *d++ = a * (start.r * oppAmount + end.r * amount); *d++ = a * (start.g * oppAmount + end.g * amount); *d++ = a * (start.b * oppAmount + end.b * amount); +#else + *d++ = a * OECF_sRGB(start.r * oppAmount + end.r * amount); + *d++ = a * OECF_sRGB(start.g * oppAmount + end.g * amount); + *d++ = a * OECF_sRGB(start.b * oppAmount + end.b * amount); +#endif *d++ = a; - dst += 4 * sizeof(float); } void GradientCache::generateTexture(uint32_t* colors, float* positions, const uint32_t width, const uint32_t height, Texture* texture) { - const GLsizei rowBytes = width * bytesPerPixel(); + const GLsizei rowBytes = width * sourceBytesPerPixel(); uint8_t pixels[rowBytes * height]; - static ChannelSplitter gSplitters[] = { - &android::uirenderer::GradientCache::splitToBytes, - &android::uirenderer::GradientCache::splitToFloats, - }; - ChannelSplitter split = gSplitters[mUseFloatTexture]; - static ChannelMixer gMixers[] = { + // colors are stored gamma-encoded &android::uirenderer::GradientCache::mixBytes, + // colors are stored in linear (linear blending on) + // or gamma-encoded (linear blending off) &android::uirenderer::GradientCache::mixFloats, }; ChannelMixer mix = gMixers[mUseFloatTexture]; - GradientColor start; - (this->*split)(colors[0], start); + FloatColor start; + start.setUnPreMultipliedSRGB(colors[0]); - GradientColor end; - (this->*split)(colors[1], end); + FloatColor end; + end.setUnPreMultipliedSRGB(colors[1]); int currentPos = 1; float startPos = positions[0]; @@ -255,7 +246,7 @@ void GradientCache::generateTexture(uint32_t* colors, float* positions, currentPos++; - (this->*split)(colors[currentPos], end); + end.setUnPreMultipliedSRGB(colors[currentPos]); distance = positions[currentPos] - startPos; } @@ -266,10 +257,10 @@ void GradientCache::generateTexture(uint32_t* colors, float* positions, memcpy(pixels + rowBytes, pixels, rowBytes); if (mUseFloatTexture) { - // We have to use GL_RGBA16F because GL_RGBA32F does not support filtering texture->upload(GL_RGBA16F, width, height, GL_RGBA, GL_FLOAT, pixels); } else { - texture->upload(GL_RGBA, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels); + GLint internalFormat = mHasSRGB ? GL_SRGB8_ALPHA8 : GL_RGBA; + texture->upload(internalFormat, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels); } texture->setFilter(GL_LINEAR); diff --git a/libs/hwui/GradientCache.h b/libs/hwui/GradientCache.h index 49be19ab1c81..5e35435ed64c 100644 --- a/libs/hwui/GradientCache.h +++ b/libs/hwui/GradientCache.h @@ -26,6 +26,8 @@ #include <utils/LruCache.h> #include <utils/Mutex.h> +#include "FloatColor.h" + namespace android { namespace uirenderer { @@ -150,25 +152,15 @@ private: void getGradientInfo(const uint32_t* colors, const int count, GradientInfo& info); size_t bytesPerPixel() const; + size_t sourceBytesPerPixel() const; - struct GradientColor { - float r; - float g; - float b; - float a; - }; - - typedef void (GradientCache::*ChannelSplitter)(uint32_t inColor, - GradientColor& outColor) const; - - void splitToBytes(uint32_t inColor, GradientColor& outColor) const; - void splitToFloats(uint32_t inColor, GradientColor& outColor) const; - - typedef void (GradientCache::*ChannelMixer)(GradientColor& start, GradientColor& end, + typedef void (GradientCache::*ChannelMixer)(const FloatColor& start, const FloatColor& end, float amount, uint8_t*& dst) const; - void mixBytes(GradientColor& start, GradientColor& end, float amount, uint8_t*& dst) const; - void mixFloats(GradientColor& start, GradientColor& 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; @@ -178,6 +170,7 @@ private: GLint mMaxTextureSize; bool mUseFloatTexture; bool mHasNpot; + bool mHasSRGB; mutable Mutex mLock; }; // class GradientCache diff --git a/libs/hwui/IProfileRenderer.h b/libs/hwui/IProfileRenderer.h new file mode 100644 index 000000000000..947ed34cc070 --- /dev/null +++ b/libs/hwui/IProfileRenderer.h @@ -0,0 +1,34 @@ +/* + * 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 "SkPaint.h" + +namespace android { +namespace uirenderer { + +class IProfileRenderer { +public: + virtual void drawRect(float left, float top, float right, float bottom, + 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; + + virtual ~IProfileRenderer() {} +}; + +} /* namespace uirenderer */ +} /* namespace android */ diff --git a/libs/hwui/Interpolator.cpp b/libs/hwui/Interpolator.cpp index b5a33afccdc7..d740c038f36d 100644 --- a/libs/hwui/Interpolator.cpp +++ b/libs/hwui/Interpolator.cpp @@ -89,6 +89,39 @@ float OvershootInterpolator::interpolate(float t) { return t * t * ((mTension + 1) * t + mTension) + 1.0f; } +float PathInterpolator::interpolate(float t) { + if (t <= 0) { + return 0; + } else if (t >= 1) { + return 1; + } + // Do a binary search for the correct x to interpolate between. + size_t startIndex = 0; + size_t endIndex = mX.size() - 1; + + while (endIndex > startIndex + 1) { + int midIndex = (startIndex + endIndex) / 2; + if (t < mX[midIndex]) { + endIndex = midIndex; + } else { + startIndex = midIndex; + } + } + + float xRange = mX[endIndex] - mX[startIndex]; + if (xRange == 0) { + return mY[startIndex]; + } + + float tInRange = t - mX[startIndex]; + float fraction = tInRange / xRange; + + float startY = mY[startIndex]; + float endY = mY[endIndex]; + return startY + (fraction * (endY - startY)); + +} + LUTInterpolator::LUTInterpolator(float* values, size_t size) : mValues(values) , mSize(size) { diff --git a/libs/hwui/Interpolator.h b/libs/hwui/Interpolator.h index 65120087ff60..224cee70acc7 100644 --- a/libs/hwui/Interpolator.h +++ b/libs/hwui/Interpolator.h @@ -20,6 +20,7 @@ #include <memory> #include <cutils/compiler.h> +#include <vector> namespace android { namespace uirenderer { @@ -100,6 +101,16 @@ 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) {} + virtual float interpolate(float input) override; +private: + std::vector<float> mX; + std::vector<float> mY; +}; + class ANDROID_API LUTInterpolator : public Interpolator { public: LUTInterpolator(float* values, size_t size); diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp index cdbbbab7730d..331bb81208b1 100644 --- a/libs/hwui/Layer.cpp +++ b/libs/hwui/Layer.cpp @@ -16,278 +16,36 @@ #include "Layer.h" -#include "Caches.h" -#include "DeferredDisplayList.h" -#include "LayerRenderer.h" -#include "OpenGLRenderer.h" -#include "RenderNode.h" #include "renderstate/RenderState.h" -#include "utils/TraceUtils.h" -#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()) +#include <SkColorFilter.h> namespace android { namespace uirenderer { -Layer::Layer(Type layerType, RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight) +Layer::Layer(RenderState& renderState, Api api) : GpuMemoryTracker(GpuObjectType::Layer) - , state(State::Uncached) - , caches(Caches::getInstance()) - , renderState(renderState) - , texture(caches) - , type(layerType) { + , mRenderState(renderState) + , mApi(api) { // TODO: This is a violation of Android's typical ref counting, but it // preserves the old inc/dec ref locations. This should be changed... incStrong(nullptr); - renderTarget = GL_TEXTURE_2D; - texture.mWidth = layerWidth; - texture.mHeight = layerHeight; + renderState.registerLayer(this); } Layer::~Layer() { - renderState.unregisterLayer(this); SkSafeUnref(colorFilter); - if (stencil || fbo || texture.mId) { - removeFbo(); - texture.deleteTexture(); - } - - delete[] mesh; -} - -void Layer::onGlContextLost() { - removeFbo(); - texture.deleteTexture(); -} - -uint32_t Layer::computeIdealWidth(uint32_t layerWidth) { - return uint32_t(ceilf(layerWidth / float(LAYER_SIZE)) * LAYER_SIZE); -} - -uint32_t Layer::computeIdealHeight(uint32_t layerHeight) { - return uint32_t(ceilf(layerHeight / float(LAYER_SIZE)) * LAYER_SIZE); -} - -void Layer::requireRenderer() { - if (!renderer) { - renderer.reset(new LayerRenderer(renderState, this)); - renderer->initProperties(); - } -} - -void Layer::updateLightPosFromRenderer(const OpenGLRenderer& rootRenderer) { - if (renderer && rendererLightPosDirty) { - // re-init renderer's light position, based upon last cached location in window - Vector3 lightPos = rootRenderer.getLightCenter(); - cachedInvTransformInWindow.mapPoint3d(lightPos); - renderer->initLight(rootRenderer.getLightRadius(), - rootRenderer.getAmbientShadowAlpha(), - rootRenderer.getSpotShadowAlpha()); - renderer->setLightCenter(lightPos); - rendererLightPosDirty = false; - } -} - -bool Layer::resize(const uint32_t width, const uint32_t height) { - uint32_t desiredWidth = computeIdealWidth(width); - uint32_t desiredHeight = computeIdealWidth(height); - - if (desiredWidth <= getWidth() && desiredHeight <= getHeight()) { - return true; - } - - ATRACE_NAME("resizeLayer"); - - const uint32_t maxTextureSize = caches.maxTextureSize; - if (desiredWidth > maxTextureSize || desiredHeight > maxTextureSize) { - ALOGW("Layer exceeds max. dimensions supported by the GPU (%dx%d, max=%dx%d)", - desiredWidth, desiredHeight, maxTextureSize, maxTextureSize); - return false; - } - - uint32_t oldWidth = getWidth(); - uint32_t oldHeight = getHeight(); - - setSize(desiredWidth, desiredHeight); - - if (fbo) { - caches.textureState().activateTexture(0); - bindTexture(); - allocateTexture(); - - if (glGetError() != GL_NO_ERROR) { - setSize(oldWidth, oldHeight); - return false; - } - } - - if (stencil) { - stencil->bind(); - stencil->resize(desiredWidth, desiredHeight); - - if (glGetError() != GL_NO_ERROR) { - setSize(oldWidth, oldHeight); - return false; - } - } - - return true; -} - -void Layer::removeFbo(bool flush) { - if (stencil) { - GLuint previousFbo = renderState.getFramebuffer(); - renderState.bindFramebuffer(fbo); - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, 0); - renderState.bindFramebuffer(previousFbo); - - caches.renderBufferCache.put(stencil); - stencil = nullptr; - } - - if (fbo) { - if (flush) LayerRenderer::flushLayer(renderState, this); - renderState.deleteFramebuffer(fbo); - fbo = 0; - } -} - -void Layer::updateDeferred(RenderNode* renderNode, int left, int top, int right, int bottom) { - requireRenderer(); - this->renderNode = renderNode; - const Rect r(left, top, right, bottom); - dirtyRect.unionWith(r); - deferredUpdateScheduled = true; -} - -void Layer::setPaint(const SkPaint* paint) { - alpha = PaintUtils::getAlphaDirect(paint); - mode = PaintUtils::getXfermodeDirect(paint); - setColorFilter((paint) ? paint->getColorFilter() : nullptr); + mRenderState.unregisterLayer(this); } void Layer::setColorFilter(SkColorFilter* filter) { SkRefCnt_SafeAssign(colorFilter, filter); } -void Layer::bindTexture() const { - if (texture.mId) { - caches.textureState().bindTexture(renderTarget, texture.mId); - } -} - -void Layer::bindStencilRenderBuffer() const { - if (stencil) { - stencil->bind(); - } -} - -void Layer::generateTexture() { - if (!texture.mId) { - glGenTextures(1, &texture.mId); - } -} - -void Layer::clearTexture() { - // There's a rare possibility that Caches could have been destroyed already - // since this method is queued up as a task. - // Since this is a reset method, treat this as non-fatal. - if (caches.isInitialized()) { - caches.textureState().unbindTexture(texture.mId); - } - texture.mId = 0; -} - -void Layer::allocateTexture() { -#if DEBUG_LAYERS - ALOGD(" Allocate layer: %dx%d", getWidth(), getHeight()); -#endif - if (texture.mId) { - texture.updateSize(getWidth(), getHeight(), GL_RGBA); - glTexImage2D(renderTarget, 0, GL_RGBA, getWidth(), getHeight(), 0, - GL_RGBA, GL_UNSIGNED_BYTE, nullptr); - } -} - -void Layer::defer(const OpenGLRenderer& rootRenderer) { - ATRACE_LAYER_WORK("Optimize"); - - updateLightPosFromRenderer(rootRenderer); - const float width = layer.getWidth(); - const float height = layer.getHeight(); - - if (dirtyRect.isEmpty() || (dirtyRect.left <= 0 && dirtyRect.top <= 0 && - dirtyRect.right >= width && dirtyRect.bottom >= height)) { - dirtyRect.set(0, 0, width, height); - } - - deferredList.reset(new DeferredDisplayList(dirtyRect)); - - DeferStateStruct deferredState(*deferredList, *renderer, - RenderNode::kReplayFlag_ClipChildren); - - renderer->setupFrameState(width, height, dirtyRect.left, dirtyRect.top, - dirtyRect.right, dirtyRect.bottom, !isBlend()); - - renderNode->computeOrdering(); - renderNode->defer(deferredState, 0); - - deferredUpdateScheduled = false; -} - -void Layer::cancelDefer() { - renderNode = nullptr; - deferredUpdateScheduled = false; - deferredList.reset(nullptr); -} - -void Layer::flush() { - // renderer is checked as layer may be destroyed/put in layer cache with flush scheduled - if (deferredList && renderer) { - ATRACE_LAYER_WORK("Issue"); - renderer->startMark((renderNode.get() != nullptr) ? renderNode->getName() : "Layer"); - - renderer->prepareDirty(layer.getWidth(), layer.getHeight(), - dirtyRect.left, dirtyRect.top, dirtyRect.right, dirtyRect.bottom, !isBlend()); - - deferredList->flush(*renderer, dirtyRect); - - renderer->finish(); - - dirtyRect.setEmpty(); - renderNode = nullptr; - - renderer->endMark(); - } -} - -void Layer::render(const OpenGLRenderer& rootRenderer) { - ATRACE_LAYER_WORK("Direct-Issue"); - - updateLightPosFromRenderer(rootRenderer); - renderer->prepareDirty(layer.getWidth(), layer.getHeight(), - dirtyRect.left, dirtyRect.top, dirtyRect.right, dirtyRect.bottom, !isBlend()); - - renderer->drawRenderNode(renderNode.get(), dirtyRect, RenderNode::kReplayFlag_ClipChildren); - - renderer->finish(); - - dirtyRect.setEmpty(); - - deferredUpdateScheduled = false; - renderNode = nullptr; -} - void Layer::postDecStrong() { - renderState.postDecStrong(this); + mRenderState.postDecStrong(this); } }; // namespace uirenderer diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h index 1e5498bb3d21..3b639ee49334 100644 --- a/libs/hwui/Layer.h +++ b/libs/hwui/Layer.h @@ -14,28 +14,15 @@ * limitations under the License. */ -#ifndef ANDROID_HWUI_LAYER_H -#define ANDROID_HWUI_LAYER_H +#pragma once -#include <cutils/compiler.h> -#include <sys/types.h> -#include <utils/StrongPointer.h> #include <utils/RefBase.h> -#include <memory> - -#include <GLES2/gl2.h> #include <GpuMemoryTracker.h> -#include <ui/Region.h> - #include <SkPaint.h> -#include <SkXfermode.h> +#include <SkBlendMode.h> #include "Matrix.h" -#include "Rect.h" -#include "RenderBuffer.h" -#include "Texture.h" -#include "Vertex.h" namespace android { namespace uirenderer { @@ -44,106 +31,33 @@ namespace uirenderer { // Layers /////////////////////////////////////////////////////////////////////////////// -// Forward declarations -class Caches; -class RenderNode; class RenderState; -class OpenGLRenderer; -class DeferredDisplayList; -struct DeferStateStruct; /** - * A layer has dimensions and is backed by an OpenGL texture or FBO. + * A layer has dimensions and is backed by a backend specific texture or framebuffer. */ class Layer : public VirtualLightRefBase, GpuMemoryTracker { public: - enum class Type { - Texture, - DisplayList, - }; - - // layer lifecycle, controlled from outside - enum class State { - Uncached = 0, - InCache = 1, - FailedToCache = 2, - RemovedFromCache = 3, - DeletedFromCache = 4, - InGarbageList = 5, + enum class Api { + OpenGL = 0, + Vulkan = 1, }; - State state; // public for logging/debugging purposes - - Layer(Type type, RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight); - ~Layer(); - - static uint32_t computeIdealWidth(uint32_t layerWidth); - static uint32_t computeIdealHeight(uint32_t layerHeight); - - /** - * Calling this method will remove (either by recycling or - * destroying) the associated FBO, if present, and any render - * buffer (stencil for instance.) - */ - void removeFbo(bool flush = true); - - /** - * Sets this layer's region to a rectangle. Computes the appropriate - * texture coordinates. - */ - void setRegionAsRect() { - const android::Rect& bounds = region.getBounds(); - regionRect.set(bounds.leftTop().x, bounds.leftTop().y, - bounds.rightBottom().x, bounds.rightBottom().y); - - const float texX = 1.0f / float(texture.mWidth); - const float texY = 1.0f / float(texture.mHeight); - const float height = layer.getHeight(); - texCoords.set( - regionRect.left * texX, (height - regionRect.top) * texY, - regionRect.right * texX, (height - regionRect.bottom) * texY); - - regionRect.translate(layer.left, layer.top); - } - - void setWindowTransform(Matrix4& windowTransform) { - cachedInvTransformInWindow.loadInverse(windowTransform); - rendererLightPosDirty = true; - } - void updateDeferred(RenderNode* renderNode, int left, int top, int right, int bottom); - - inline uint32_t getWidth() const { - return texture.mWidth; + Api getApi() const { + return mApi; } - inline uint32_t getHeight() const { - return texture.mHeight; - } + ~Layer(); - /** - * Resize the layer and its texture if needed. - * - * @param width The new width of the layer - * @param height The new height of the layer - * - * @return True if the layer was resized or nothing happened, false if - * a failure occurred during the resizing operation - */ - bool resize(const uint32_t width, const uint32_t height); + virtual uint32_t getWidth() const = 0; - void setSize(uint32_t width, uint32_t height) { - texture.updateSize(width, height, texture.format()); - } + virtual uint32_t getHeight() const = 0; - ANDROID_API void setPaint(const SkPaint* paint); + virtual void setSize(uint32_t width, uint32_t height) = 0; - inline void setBlend(bool blend) { - texture.blend = blend; - } + virtual void setBlend(bool blend) = 0; - inline bool isBlend() const { - return texture.blend; - } + virtual bool isBlend() const = 0; inline void setForceFilter(bool forceFilter) { this->forceFilter = forceFilter; @@ -157,7 +71,7 @@ public: this->alpha = alpha; } - inline void setAlpha(int alpha, SkXfermode::Mode mode) { + inline void setAlpha(int alpha, SkBlendMode mode) { this->alpha = alpha; this->mode = mode; } @@ -166,114 +80,15 @@ public: return alpha; } - inline SkXfermode::Mode getMode() const { + inline SkBlendMode getMode() const { return mode; } - inline void setEmpty(bool empty) { - this->empty = empty; - } - - inline bool isEmpty() const { - return empty; - } - - inline void setFbo(GLuint fbo) { - this->fbo = fbo; - } - - inline GLuint getFbo() const { - return fbo; - } - - inline void setStencilRenderBuffer(RenderBuffer* renderBuffer) { - if (RenderBuffer::isStencilBuffer(renderBuffer->getFormat())) { - this->stencil = renderBuffer; - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, - GL_RENDERBUFFER, stencil->getName()); - } else { - ALOGE("The specified render buffer is not a stencil buffer"); - } - } - - inline RenderBuffer* getStencilRenderBuffer() const { - return stencil; - } - - inline GLuint getTextureId() const { - return texture.id(); - } - - inline Texture& getTexture() { - return texture; - } - - inline GLenum getRenderTarget() const { - return renderTarget; - } - - inline void setRenderTarget(GLenum renderTarget) { - this->renderTarget = renderTarget; - } - - inline bool isRenderable() const { - return renderTarget != GL_NONE; - } - - void setWrap(GLenum wrap, bool bindTexture = false, bool force = false) { - texture.setWrap(wrap, bindTexture, force, renderTarget); - } - - void setFilter(GLenum filter, bool bindTexture = false, bool force = false) { - texture.setFilter(filter, bindTexture, force, renderTarget); - } - - inline bool isCacheable() const { - return cacheable; - } - - inline void setCacheable(bool cacheable) { - this->cacheable = cacheable; - } - - inline bool isDirty() const { - return dirty; - } - - inline void setDirty(bool dirty) { - this->dirty = dirty; - } - - inline bool isTextureLayer() const { - return type == Type::Texture; - } - inline SkColorFilter* getColorFilter() const { return colorFilter; } - ANDROID_API void setColorFilter(SkColorFilter* filter); - - inline void setConvexMask(const SkPath* convexMask) { - this->convexMask = convexMask; - } - - inline const SkPath* getConvexMask() { - return convexMask; - } - - void bindStencilRenderBuffer() const; - - void bindTexture() const; - void generateTexture(); - void allocateTexture(); - - /** - * When the caller frees the texture itself, the caller - * must call this method to tell this layer that it lost - * the texture. - */ - ANDROID_API void clearTexture(); + void setColorFilter(SkColorFilter* filter); inline mat4& getTexTransform() { return texTransform; @@ -283,113 +98,19 @@ public: return transform; } - void defer(const OpenGLRenderer& rootRenderer); - void cancelDefer(); - void flush(); - void render(const OpenGLRenderer& rootRenderer); - /** * Posts a decStrong call to the appropriate thread. * Thread-safe. */ void postDecStrong(); - /** - * Lost the GL context but the layer is still around, mark it invalid internally - * so the dtor knows not to do any GL work - */ - void onGlContextLost(); +protected: + Layer(RenderState& renderState, Api api); - /** - * Bounds of the layer. - */ - Rect layer; - /** - * Texture coordinates of the layer. - */ - Rect texCoords; - /** - * Clipping rectangle. - */ - Rect clipRect; - - /** - * Dirty region indicating what parts of the layer - * have been drawn. - */ - Region region; - /** - * If the region is a rectangle, coordinates of the - * region are stored here. - */ - Rect regionRect; - - /** - * If the layer can be rendered as a mesh, this is non-null. - */ - TextureVertex* mesh = nullptr; - GLsizei meshElementCount = 0; - - /** - * Used for deferred updates. - */ - bool deferredUpdateScheduled = false; - std::unique_ptr<OpenGLRenderer> renderer; - sp<RenderNode> renderNode; - Rect dirtyRect; - bool debugDrawUpdate = false; - bool hasDrawnSinceUpdate = false; - bool wasBuildLayered = false; + RenderState& mRenderState; private: - void requireRenderer(); - void updateLightPosFromRenderer(const OpenGLRenderer& rootRenderer); - - Caches& caches; - - RenderState& renderState; - - /** - * Name of the FBO used to render the layer. If the name is 0 - * this layer is not backed by an FBO, but a simple texture. - */ - GLuint fbo = 0; - - /** - * The render buffer used as the stencil buffer. - */ - RenderBuffer* stencil = nullptr; - - /** - * Indicates whether this layer has been used already. - */ - bool empty = true; - - /** - * The texture backing this layer. - */ - Texture texture; - - /** - * If set to true (by default), the layer can be reused. - */ - bool cacheable = true; - - /** - * Denotes whether the layer is a DisplayList, or Texture layer. - */ - const Type type; - - /** - * When set to true, this layer is dirty and should be cleared - * before any rendering occurs. - */ - bool dirty = false; - - /** - * Indicates the render target. - */ - GLenum renderTarget = GL_TEXTURE_2D; + Api mApi; /** * Color filter used to draw this layer. Optional. @@ -409,7 +130,7 @@ private: /** * Blending mode of the layer. */ - SkXfermode::Mode mode = SkXfermode::kSrcOver_Mode; + SkBlendMode mode = SkBlendMode::kSrcOver; /** * Optional texture coordinates transform. @@ -421,28 +142,7 @@ private: */ mat4 transform; - /** - * Cached transform of layer in window, updated only on creation / resize - */ - mat4 cachedInvTransformInWindow; - bool rendererLightPosDirty = true; - - /** - * Used to defer display lists when the layer is updated with a - * display list. - */ - std::unique_ptr<DeferredDisplayList> deferredList; - - /** - * This convex path should be used to mask the layer's draw to the screen. - * - * Data not owned/managed by layer object. - */ - const SkPath* convexMask = nullptr; - }; // struct Layer }; // namespace uirenderer }; // namespace android - -#endif // ANDROID_HWUI_LAYER_H diff --git a/libs/hwui/LayerBuilder.cpp b/libs/hwui/LayerBuilder.cpp index 66413dcf0d97..c5d5492d4fd1 100644 --- a/libs/hwui/LayerBuilder.cpp +++ b/libs/hwui/LayerBuilder.cpp @@ -274,7 +274,7 @@ void LayerBuilder::flushLayerClears(LinearAllocator& allocator) { // 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->setXfermodeMode(SkXfermode::kClear_Mode); + paint->setBlendMode(SkBlendMode::kClear); SimpleRectsOp* op = allocator.create_trivial<SimpleRectsOp>(bounds, Matrix4::identity(), nullptr, paint, verts, vertCount); diff --git a/libs/hwui/LayerCache.cpp b/libs/hwui/LayerCache.cpp deleted file mode 100644 index f5681ce712d5..000000000000 --- a/libs/hwui/LayerCache.cpp +++ /dev/null @@ -1,162 +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. - */ - -#include "LayerCache.h" - -#include "Caches.h" -#include "Properties.h" - -#include <utils/Log.h> - -#include <GLES2/gl2.h> - -namespace android { -namespace uirenderer { - -/////////////////////////////////////////////////////////////////////////////// -// Constructors/destructor -/////////////////////////////////////////////////////////////////////////////// - -LayerCache::LayerCache() - : mSize(0) - , mMaxSize(Properties::layerPoolSize) {} - -LayerCache::~LayerCache() { - clear(); -} - -/////////////////////////////////////////////////////////////////////////////// -// Size management -/////////////////////////////////////////////////////////////////////////////// - -size_t LayerCache::getCount() { - return mCache.size(); -} - -uint32_t LayerCache::getSize() { - return mSize; -} - -uint32_t LayerCache::getMaxSize() { - return mMaxSize; -} - -void LayerCache::setMaxSize(uint32_t maxSize) { - clear(); - mMaxSize = maxSize; -} - -/////////////////////////////////////////////////////////////////////////////// -// Caching -/////////////////////////////////////////////////////////////////////////////// - -int LayerCache::LayerEntry::compare(const LayerCache::LayerEntry& lhs, - const LayerCache::LayerEntry& rhs) { - int deltaInt = int(lhs.mWidth) - int(rhs.mWidth); - if (deltaInt != 0) return deltaInt; - - return int(lhs.mHeight) - int(rhs.mHeight); -} - -void LayerCache::deleteLayer(Layer* layer) { - if (layer) { - LAYER_LOGD("Destroying layer %dx%d, fbo %d", layer->getWidth(), layer->getHeight(), - layer->getFbo()); - mSize -= layer->getWidth() * layer->getHeight() * 4; - layer->state = Layer::State::DeletedFromCache; - layer->decStrong(nullptr); - } -} - -void LayerCache::clear() { - for (auto entry : mCache) { - deleteLayer(entry.mLayer); - } - mCache.clear(); -} - -Layer* LayerCache::get(RenderState& renderState, const uint32_t width, const uint32_t height) { - Layer* layer = nullptr; - - LayerEntry entry(width, height); - auto iter = mCache.find(entry); - - if (iter != mCache.end()) { - entry = *iter; - mCache.erase(iter); - - layer = entry.mLayer; - layer->state = Layer::State::RemovedFromCache; - mSize -= layer->getWidth() * layer->getHeight() * 4; - - LAYER_LOGD("Reusing layer %dx%d", layer->getWidth(), layer->getHeight()); - } else { - LAYER_LOGD("Creating new layer %dx%d", entry.mWidth, entry.mHeight); - - layer = new Layer(Layer::Type::DisplayList, renderState, entry.mWidth, entry.mHeight); - layer->setBlend(true); - layer->generateTexture(); - layer->bindTexture(); - layer->setFilter(GL_NEAREST); - layer->setWrap(GL_CLAMP_TO_EDGE, false); - -#if DEBUG_LAYERS - dump(); -#endif - } - - return layer; -} - -void LayerCache::dump() { - for (auto entry : mCache) { - ALOGD(" Layer size %dx%d", entry.mWidth, entry.mHeight); - } -} - -bool LayerCache::put(Layer* layer) { - if (!layer->isCacheable()) return false; - - const uint32_t size = layer->getWidth() * layer->getHeight() * 4; - // Don't even try to cache a layer that's bigger than the cache - if (size < mMaxSize) { - // TODO: Use an LRU - while (mSize + size > mMaxSize) { - Layer* victim = mCache.begin()->mLayer; - deleteLayer(victim); - mCache.erase(mCache.begin()); - - LAYER_LOGD(" Deleting layer %.2fx%.2f", victim->layer.getWidth(), - victim->layer.getHeight()); - } - - layer->cancelDefer(); - - LayerEntry entry(layer); - - mCache.insert(entry); - mSize += size; - - layer->state = Layer::State::InCache; - return true; - } - - layer->state = Layer::State::FailedToCache; - return false; -} - -}; // namespace uirenderer -}; // namespace android diff --git a/libs/hwui/LayerCache.h b/libs/hwui/LayerCache.h deleted file mode 100644 index 7a1a9ae3dd5d..000000000000 --- a/libs/hwui/LayerCache.h +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ANDROID_HWUI_LAYER_CACHE_H -#define ANDROID_HWUI_LAYER_CACHE_H - -#include "Debug.h" -#include "Layer.h" - -#include <set> - -namespace android { -namespace uirenderer { - -class RenderState; - -/////////////////////////////////////////////////////////////////////////////// -// Defines -/////////////////////////////////////////////////////////////////////////////// - -#if DEBUG_LAYERS - #define LAYER_LOGD(...) ALOGD(__VA_ARGS__) -#else - #define LAYER_LOGD(...) -#endif - -/////////////////////////////////////////////////////////////////////////////// -// Cache -/////////////////////////////////////////////////////////////////////////////// - -class LayerCache { -public: - LayerCache(); - ~LayerCache(); - - /** - * Returns a layer large enough for the specified dimensions. If no suitable - * layer can be found, a new one is created and returned. If creating a new - * layer fails, NULL is returned. - * - * When a layer is obtained from the cache, it is removed and the total - * size of the cache goes down. - * - * @param width The desired width of the layer - * @param height The desired height of the layer - */ - Layer* get(RenderState& renderState, const uint32_t width, const uint32_t height); - - /** - * Adds the layer to the cache. The layer will not be added if there is - * not enough space available. Adding a layer can cause other layers to - * be removed from the cache. - * - * @param layer The layer to add to the cache - * - * @return True if the layer was added, false otherwise. - */ - bool put(Layer* layer); - /** - * Clears the cache. This causes all layers to be deleted. - */ - void clear(); - - /** - * Sets the maximum size of the cache in bytes. - */ - void setMaxSize(uint32_t maxSize); - /** - * Returns the maximum size of the cache in bytes. - */ - uint32_t getMaxSize(); - /** - * Returns the current size of the cache in bytes. - */ - uint32_t getSize(); - - size_t getCount(); - - /** - * Prints out the content of the cache. - */ - void dump(); - -private: - struct LayerEntry { - LayerEntry(): - mLayer(nullptr), mWidth(0), mHeight(0) { - } - - LayerEntry(const uint32_t layerWidth, const uint32_t layerHeight): mLayer(nullptr) { - mWidth = Layer::computeIdealWidth(layerWidth); - mHeight = Layer::computeIdealHeight(layerHeight); - } - - explicit LayerEntry(Layer* layer): - mLayer(layer), mWidth(layer->getWidth()), mHeight(layer->getHeight()) { - } - - static int compare(const LayerEntry& lhs, const LayerEntry& rhs); - - bool operator==(const LayerEntry& other) const { - return compare(*this, other) == 0; - } - - bool operator!=(const LayerEntry& other) const { - return compare(*this, other) != 0; - } - - bool operator<(const LayerEntry& other) const { - return LayerEntry::compare(*this, other) < 0; - } - - Layer* mLayer; - uint32_t mWidth; - uint32_t mHeight; - }; // struct LayerEntry - - void deleteLayer(Layer* layer); - - std::multiset<LayerEntry> mCache; - - uint32_t mSize; - uint32_t mMaxSize; -}; // class LayerCache - -}; // namespace uirenderer -}; // namespace android - -#endif // ANDROID_HWUI_LAYER_CACHE_H diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp deleted file mode 100644 index 137316f57725..000000000000 --- a/libs/hwui/LayerRenderer.cpp +++ /dev/null @@ -1,465 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "LayerCache.h" -#include "LayerRenderer.h" -#include "Matrix.h" -#include "Properties.h" -#include "Rect.h" -#include "renderstate/RenderState.h" -#include "utils/GLUtils.h" -#include "utils/TraceUtils.h" - -#include <ui/Rect.h> - -#include <private/hwui/DrawGlInfo.h> - - -namespace android { -namespace uirenderer { - -/////////////////////////////////////////////////////////////////////////////// -// Rendering -/////////////////////////////////////////////////////////////////////////////// - -LayerRenderer::LayerRenderer(RenderState& renderState, Layer* layer) - : OpenGLRenderer(renderState) - , mLayer(layer) { -} - -LayerRenderer::~LayerRenderer() { -} - -void LayerRenderer::prepareDirty(int viewportWidth, int viewportHeight, - float left, float top, float right, float bottom, bool opaque) { - LAYER_RENDERER_LOGD("Rendering into layer, fbo = %d", mLayer->getFbo()); - - mRenderState.bindFramebuffer(mLayer->getFbo()); - - const float width = mLayer->layer.getWidth(); - const float height = mLayer->layer.getHeight(); - - Rect dirty(left, top, right, bottom); - if (dirty.isEmpty() || (dirty.left <= 0 && dirty.top <= 0 && - dirty.right >= width && dirty.bottom >= height)) { - mLayer->region.clear(); - dirty.set(0.0f, 0.0f, width, height); - } else { - dirty.doIntersect(0.0f, 0.0f, width, height); - android::Rect r(dirty.left, dirty.top, dirty.right, dirty.bottom); - mLayer->region.subtractSelf(r); - } - mLayer->clipRect.set(dirty); - - OpenGLRenderer::prepareDirty(viewportWidth, viewportHeight, - dirty.left, dirty.top, dirty.right, dirty.bottom, opaque); -} - -void LayerRenderer::clear(float left, float top, float right, float bottom, bool opaque) { - if (mLayer->isDirty()) { - mRenderState.scissor().setEnabled(false); - glClear(GL_COLOR_BUFFER_BIT); - - mRenderState.scissor().reset(); - mLayer->setDirty(false); - } else { - OpenGLRenderer::clear(left, top, right, bottom, opaque); - } -} - -bool LayerRenderer::finish() { - bool retval = OpenGLRenderer::finish(); - - generateMesh(); - - LAYER_RENDERER_LOGD("Finished rendering into layer, fbo = %d", mLayer->getFbo()); - - // No need to unbind our FBO, this will be taken care of by the caller - // who will invoke OpenGLRenderer::resume() - return retval; -} - -GLuint LayerRenderer::getTargetFbo() const { - return mLayer->getFbo(); -} - -bool LayerRenderer::suppressErrorChecks() const { - return true; -} - -/////////////////////////////////////////////////////////////////////////////// -// Layer support -/////////////////////////////////////////////////////////////////////////////// - -bool LayerRenderer::hasLayer() const { - return true; -} - -void LayerRenderer::ensureStencilBuffer() { - attachStencilBufferToLayer(mLayer); -} - -/////////////////////////////////////////////////////////////////////////////// -// Dirty region tracking -/////////////////////////////////////////////////////////////////////////////// - -Region* LayerRenderer::getRegion() const { - if (mState.currentFlags() & Snapshot::kFlagFboTarget) { - return OpenGLRenderer::getRegion(); - } - return &mLayer->region; -} - -// TODO: This implementation uses a very simple approach to fixing T-junctions which keeps the -// results as rectangles, and is thus not necessarily efficient in the geometry -// produced. Eventually, it may be better to develop triangle-based mechanism. -void LayerRenderer::generateMesh() { - if (mLayer->region.isRect() || mLayer->region.isEmpty()) { - if (mLayer->mesh) { - delete[] mLayer->mesh; - mLayer->mesh = nullptr; - mLayer->meshElementCount = 0; - } - - mLayer->setRegionAsRect(); - return; - } - - // avoid T-junctions as they cause artifacts in between the resultant - // geometry when complex transforms occur. - // TODO: generate the safeRegion only if necessary based on drawing transform (see - // OpenGLRenderer::composeLayerRegion()) - Region safeRegion = Region::createTJunctionFreeRegion(mLayer->region); - - size_t count; - const android::Rect* rects = safeRegion.getArray(&count); - - GLsizei elementCount = count * 6; - - if (mLayer->mesh && mLayer->meshElementCount < elementCount) { - delete[] mLayer->mesh; - mLayer->mesh = nullptr; - } - - if (!mLayer->mesh) { - mLayer->mesh = new TextureVertex[count * 4]; - } - mLayer->meshElementCount = elementCount; - - const float texX = 1.0f / float(mLayer->getWidth()); - const float texY = 1.0f / float(mLayer->getHeight()); - const float height = mLayer->layer.getHeight(); - - TextureVertex* mesh = mLayer->mesh; - - for (size_t i = 0; i < count; i++) { - const android::Rect* r = &rects[i]; - - const float u1 = r->left * texX; - const float v1 = (height - r->top) * texY; - const float u2 = r->right * texX; - const float v2 = (height - r->bottom) * texY; - - TextureVertex::set(mesh++, r->left, r->top, u1, v1); - TextureVertex::set(mesh++, r->right, r->top, u2, v1); - TextureVertex::set(mesh++, r->left, r->bottom, u1, v2); - TextureVertex::set(mesh++, r->right, r->bottom, u2, v2); - } -} - -/////////////////////////////////////////////////////////////////////////////// -// Layers management -/////////////////////////////////////////////////////////////////////////////// - -Layer* LayerRenderer::createRenderLayer(RenderState& renderState, uint32_t width, uint32_t height) { - ATRACE_FORMAT("Allocate %ux%u HW Layer", width, height); - LAYER_RENDERER_LOGD("Requesting new render layer %dx%d", width, height); - - Caches& caches = Caches::getInstance(); - GLuint fbo = renderState.createFramebuffer(); - if (!fbo) { - ALOGW("Could not obtain an FBO"); - return nullptr; - } - - caches.textureState().activateTexture(0); - Layer* layer = caches.layerCache.get(renderState, width, height); - if (!layer) { - ALOGW("Could not obtain a layer"); - return nullptr; - } - - // We first obtain a layer before comparing against the max texture size - // because layers are not allocated at the exact desired size. They are - // always created slightly larger to improve recycling - const uint32_t maxTextureSize = caches.maxTextureSize; - if (layer->getWidth() > maxTextureSize || layer->getHeight() > maxTextureSize) { - ALOGW("Layer exceeds max. dimensions supported by the GPU (%dx%d, max=%dx%d)", - width, height, maxTextureSize, maxTextureSize); - - // Creating a new layer always increment its refcount by 1, this allows - // us to destroy the layer object if one was created for us - layer->decStrong(nullptr); - - return nullptr; - } - - layer->setFbo(fbo); - layer->layer.set(0.0f, 0.0f, width, height); - layer->texCoords.set(0.0f, height / float(layer->getHeight()), - width / float(layer->getWidth()), 0.0f); - layer->setAlpha(255, SkXfermode::kSrcOver_Mode); - layer->setColorFilter(nullptr); - layer->setDirty(true); - layer->region.clear(); - - GLuint previousFbo = renderState.getFramebuffer(); - - renderState.bindFramebuffer(layer->getFbo()); - layer->bindTexture(); - - // Initialize the texture if needed - if (layer->isEmpty()) { - layer->setEmpty(false); - layer->allocateTexture(); - - // This should only happen if we run out of memory - if (CC_UNLIKELY(GLUtils::dumpGLErrors())) { - LOG_ALWAYS_FATAL("Could not allocate texture for layer (fbo=%d %dx%d)", - fbo, width, height); - renderState.bindFramebuffer(previousFbo); - layer->decStrong(nullptr); - return nullptr; - } - } - - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, - layer->getTextureId(), 0); - - renderState.bindFramebuffer(previousFbo); - - return layer; -} - -bool LayerRenderer::resizeLayer(Layer* layer, uint32_t width, uint32_t height) { - if (layer) { - LAYER_RENDERER_LOGD("Resizing layer fbo = %d to %dx%d", layer->getFbo(), width, height); - - if (layer->resize(width, height)) { - layer->layer.set(0.0f, 0.0f, width, height); - layer->texCoords.set(0.0f, height / float(layer->getHeight()), - width / float(layer->getWidth()), 0.0f); - } else { - return false; - } - } - - return true; -} - -Layer* LayerRenderer::createTextureLayer(RenderState& renderState) { - LAYER_RENDERER_LOGD("Creating new texture layer"); - - Layer* layer = new Layer(Layer::Type::Texture, renderState, 0, 0); - layer->setCacheable(false); - layer->layer.set(0.0f, 0.0f, 0.0f, 0.0f); - layer->texCoords.set(0.0f, 1.0f, 1.0f, 0.0f); - layer->region.clear(); - layer->setRenderTarget(GL_NONE); // see ::updateTextureLayer() - - Caches::getInstance().textureState().activateTexture(0); - layer->generateTexture(); - - return layer; -} - -void LayerRenderer::updateTextureLayer(Layer* layer, uint32_t width, uint32_t height, - bool isOpaque, bool forceFilter, GLenum renderTarget, const float* textureTransform) { - if (layer) { - layer->setBlend(!isOpaque); - layer->setForceFilter(forceFilter); - layer->setSize(width, height); - layer->layer.set(0.0f, 0.0f, width, height); - layer->region.set(width, height); - layer->regionRect.set(0.0f, 0.0f, width, height); - layer->getTexTransform().load(textureTransform); - - if (renderTarget != layer->getRenderTarget()) { - layer->setRenderTarget(renderTarget); - layer->bindTexture(); - layer->setFilter(GL_NEAREST, false, true); - layer->setWrap(GL_CLAMP_TO_EDGE, false, true); - } - } -} - -void LayerRenderer::destroyLayer(Layer* layer) { - if (layer) { - ATRACE_FORMAT("Destroy %ux%u HW Layer", layer->getWidth(), layer->getHeight()); - LAYER_RENDERER_LOGD("Recycling layer, %dx%d fbo = %d", - layer->getWidth(), layer->getHeight(), layer->getFbo()); - - if (!Caches::getInstance().layerCache.put(layer)) { - LAYER_RENDERER_LOGD(" Destroyed!"); - layer->decStrong(nullptr); - } else { - LAYER_RENDERER_LOGD(" Cached!"); -#if DEBUG_LAYER_RENDERER - Caches::getInstance().layerCache.dump(); -#endif - layer->removeFbo(); - layer->region.clear(); - } - } -} - -void LayerRenderer::flushLayer(RenderState& renderState, Layer* layer) { -#ifdef GL_EXT_discard_framebuffer - if (!layer) return; - - GLuint fbo = layer->getFbo(); - if (fbo) { - // If possible, discard any enqueud operations on deferred - // rendering architectures - if (Caches::getInstance().extensions().hasDiscardFramebuffer()) { - GLuint previousFbo = renderState.getFramebuffer(); - if (fbo != previousFbo) { - renderState.bindFramebuffer(fbo); - } - - const GLenum attachments[] = { GL_COLOR_ATTACHMENT0 }; - glDiscardFramebufferEXT(GL_FRAMEBUFFER, 1, attachments); - - if (fbo != previousFbo) { - renderState.bindFramebuffer(previousFbo); - } - } - } -#endif -} - -bool LayerRenderer::copyLayer(RenderState& renderState, Layer* layer, SkBitmap* bitmap) { - Caches& caches = Caches::getInstance(); - if (layer && layer->isRenderable() - && bitmap->width() <= caches.maxTextureSize - && bitmap->height() <= caches.maxTextureSize) { - - GLuint fbo = renderState.createFramebuffer(); - if (!fbo) { - ALOGW("Could not obtain an FBO"); - return false; - } - - SkAutoLockPixels alp(*bitmap); - - GLuint texture; - GLuint previousFbo; - GLsizei previousViewportWidth; - GLsizei previousViewportHeight; - - GLenum format; - GLenum type; - - bool status = false; - - switch (bitmap->colorType()) { - case kAlpha_8_SkColorType: - format = GL_ALPHA; - type = GL_UNSIGNED_BYTE; - break; - case kRGB_565_SkColorType: - format = GL_RGB; - type = GL_UNSIGNED_SHORT_5_6_5; - break; - case kARGB_4444_SkColorType: - format = GL_RGBA; - type = GL_UNSIGNED_SHORT_4_4_4_4; - break; - case kN32_SkColorType: - default: - format = GL_RGBA; - type = GL_UNSIGNED_BYTE; - break; - } - - float alpha = layer->getAlpha(); - SkXfermode::Mode mode = layer->getMode(); - GLuint previousLayerFbo = layer->getFbo(); - - layer->setAlpha(255, SkXfermode::kSrc_Mode); - layer->setFbo(fbo); - - previousFbo = renderState.getFramebuffer(); - renderState.getViewport(&previousViewportWidth, &previousViewportHeight); - renderState.bindFramebuffer(fbo); - - glGenTextures(1, &texture); - - caches.textureState().activateTexture(0); - caches.textureState().bindTexture(texture); - - glPixelStorei(GL_PACK_ALIGNMENT, bitmap->bytesPerPixel()); - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - 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, format, bitmap->width(), bitmap->height(), - 0, format, type, nullptr); - - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, - GL_TEXTURE_2D, texture, 0); - - { - LayerRenderer renderer(renderState, layer); - renderer.OpenGLRenderer::prepareDirty(bitmap->width(), bitmap->height(), - 0.0f, 0.0f, bitmap->width(), bitmap->height(), !layer->isBlend()); - - renderState.scissor().setEnabled(false); - renderer.translate(0.0f, bitmap->height()); - renderer.scale(1.0f, -1.0f); - - { - Rect bounds; - bounds.set(0.0f, 0.0f, bitmap->width(), bitmap->height()); - renderer.drawTextureLayer(layer, bounds); - - glReadPixels(0, 0, bitmap->width(), bitmap->height(), format, - type, bitmap->getPixels()); - - } - - status = true; - } - - renderState.bindFramebuffer(previousFbo); - layer->setAlpha(alpha, mode); - layer->setFbo(previousLayerFbo); - caches.textureState().deleteTexture(texture); - renderState.deleteFramebuffer(fbo); - renderState.setViewport(previousViewportWidth, previousViewportHeight); - - GL_CHECKPOINT(MODERATE); - - return status; - } - return false; -} - -}; // namespace uirenderer -}; // namespace android diff --git a/libs/hwui/LayerRenderer.h b/libs/hwui/LayerRenderer.h deleted file mode 100644 index 38c3705cfa25..000000000000 --- a/libs/hwui/LayerRenderer.h +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ANDROID_HWUI_LAYER_RENDERER_H -#define ANDROID_HWUI_LAYER_RENDERER_H - -#include <cutils/compiler.h> - -#include "OpenGLRenderer.h" -#include "Layer.h" - -#include <SkBitmap.h> - -namespace android { -namespace uirenderer { - -class RenderState; - -/////////////////////////////////////////////////////////////////////////////// -// Defines -/////////////////////////////////////////////////////////////////////////////// - -// Debug -#if DEBUG_LAYER_RENDERER - #define LAYER_RENDERER_LOGD(...) ALOGD(__VA_ARGS__) -#else - #define LAYER_RENDERER_LOGD(...) -#endif - -/////////////////////////////////////////////////////////////////////////////// -// Renderer -/////////////////////////////////////////////////////////////////////////////// - -class LayerRenderer: public OpenGLRenderer { -public: - LayerRenderer(RenderState& renderState, Layer* layer); - virtual ~LayerRenderer(); - - virtual void onViewportInitialized() override { /* do nothing */ } - virtual void prepareDirty(int viewportWidth, int viewportHeight, - float left, float top, float right, float bottom, bool opaque) override; - virtual void clear(float left, float top, float right, float bottom, bool opaque) override; - virtual bool finish() override; - - static Layer* createTextureLayer(RenderState& renderState); - static Layer* createRenderLayer(RenderState& renderState, uint32_t width, uint32_t height); - static bool resizeLayer(Layer* layer, uint32_t width, uint32_t height); - static void updateTextureLayer(Layer* layer, uint32_t width, uint32_t height, - bool isOpaque, bool forceFilter, GLenum renderTarget, const float* textureTransform); - static void destroyLayer(Layer* layer); - static bool copyLayer(RenderState& renderState, Layer* layer, SkBitmap* bitmap); - - static void flushLayer(RenderState& renderState, Layer* layer); - -protected: - virtual void ensureStencilBuffer() override; - virtual bool hasLayer() const override; - virtual Region* getRegion() const override; - virtual GLuint getTargetFbo() const override; - virtual bool suppressErrorChecks() const override; - -private: - void generateMesh(); - - Layer* mLayer; -}; // class LayerRenderer - -}; // namespace uirenderer -}; // namespace android - -#endif // ANDROID_HWUI_LAYER_RENDERER_H diff --git a/libs/hwui/NinePatchUtils.h b/libs/hwui/NinePatchUtils.h new file mode 100644 index 000000000000..e989a4680a60 --- /dev/null +++ b/libs/hwui/NinePatchUtils.h @@ -0,0 +1,96 @@ +/* + * 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. + */ + +#pragma once + +namespace android { +namespace NinePatchUtils { + +static inline void SetLatticeDivs(SkCanvas::Lattice* lattice, const Res_png_9patch& chunk, + int width, int height) { + lattice->fXCount = chunk.numXDivs; + lattice->fYCount = chunk.numYDivs; + lattice->fXDivs = chunk.getXDivs(); + lattice->fYDivs = chunk.getYDivs(); + + // We'll often see ninepatches where the last div is equal to the width or height. + // This doesn't provide any additional information and is not supported by Skia. + if (lattice->fXCount > 0 && width == lattice->fXDivs[lattice->fXCount - 1]) { + lattice->fXCount--; + } + if (lattice->fYCount > 0 && height == lattice->fYDivs[lattice->fYCount - 1]) { + lattice->fYCount--; + } +} + +static inline int NumDistinctRects(const SkCanvas::Lattice& lattice) { + int xRects; + if (lattice.fXCount > 0) { + xRects = (0 == lattice.fXDivs[0]) ? lattice.fXCount : lattice.fXCount + 1; + } else { + xRects = 1; + } + + int yRects; + if (lattice.fYCount > 0) { + yRects = (0 == lattice.fYDivs[0]) ? lattice.fYCount : lattice.fYCount + 1; + } else { + yRects = 1; + } + 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)); + + bool needPadRow = lattice->fYCount > 0 && 0 == lattice->fYDivs[0]; + bool needPadCol = lattice->fXCount > 0 && 0 == lattice->fXDivs[0]; + + int yCount = lattice->fYCount; + if (needPadRow) { + // Skip flags for the degenerate first row of rects. + flags += lattice->fXCount + 1; + yCount--; + } + + int i = 0; + bool setFlags = false; + for (int y = 0; y < yCount + 1; y++) { + for (int x = 0; x < lattice->fXCount + 1; x++) { + if (0 == x && needPadCol) { + // First rect of each column is degenerate, skip the flag. + flags++; + continue; + } + + if (0 == chunk.getColors()[i++]) { + *flags = SkCanvas::Lattice::kTransparent_Flags; + setFlags = true; + } + + flags++; + } + } + + if (!setFlags) { + lattice->fFlags = nullptr; + } +} + +}; // namespace NinePatchUtils +}; // namespace android diff --git a/libs/hwui/OpDumper.cpp b/libs/hwui/OpDumper.cpp index ec9ffdeebb4c..f4b7ee0fe430 100644 --- a/libs/hwui/OpDumper.cpp +++ b/libs/hwui/OpDumper.cpp @@ -16,6 +16,7 @@ #include "OpDumper.h" +#include "ClipArea.h" #include "RecordedOp.h" namespace android { diff --git a/libs/hwui/Readback.cpp b/libs/hwui/OpenGLReadback.cpp index 0ab247dc8052..c460c0d2dfd4 100644 --- a/libs/hwui/Readback.cpp +++ b/libs/hwui/OpenGLReadback.cpp @@ -14,11 +14,12 @@ * limitations under the License. */ -#include "Readback.h" +#include "OpenGLReadback.h" #include "Caches.h" #include "Image.h" #include "GlopBuilder.h" +#include "GlLayer.h" #include "renderstate/RenderState.h" #include "renderthread/EglManager.h" #include "utils/GLUtils.h" @@ -30,14 +31,98 @@ namespace android { namespace uirenderer { -CopyResult Readback::copySurfaceInto(renderthread::RenderThread& renderThread, - Surface& surface, SkBitmap* bitmap) { - // TODO: Clean this up and unify it with LayerRenderer::copyLayer, - // of which most of this is copied from. - renderThread.eglManager().initialize(); +CopyResult OpenGLReadback::copySurfaceInto(Surface& surface, const Rect& srcRect, + SkBitmap* bitmap) { + ATRACE_CALL(); + // Setup the source + sp<GraphicBuffer> sourceBuffer; + sp<Fence> sourceFence; + Matrix4 texTransform; + 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); + return CopyResult::UnknownError; + } + if (!sourceBuffer.get()) { + ALOGW("Surface doesn't have any previously queued frames, nothing to readback from"); + return CopyResult::SourceEmpty; + } + if (sourceBuffer->getUsage() & GRALLOC_USAGE_PROTECTED) { + ALOGW("Surface is protected, unable to copy from it"); + return CopyResult::SourceInvalid; + } + err = sourceFence->wait(500 /* ms */); + if (err != NO_ERROR) { + ALOGE("Timeout (500ms) exceeded waiting for buffer fence, abandoning readback attempt"); + return CopyResult::Timeout; + } - Caches& caches = Caches::getInstance(); - RenderState& renderState = renderThread.renderState(); + return copyGraphicBufferInto(sourceBuffer.get(), texTransform, srcRect, bitmap); +} + +CopyResult OpenGLReadback::copyGraphicBufferInto(GraphicBuffer* graphicBuffer, + 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 + // to be able to properly sample from the buffer. + + // 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 }; + + EGLImageKHR sourceImage = eglCreateImageKHR(display, EGL_NO_CONTEXT, + EGL_NATIVE_BUFFER_ANDROID, clientBuffer, attrs); + + if (sourceImage == EGL_NO_IMAGE_KHR) { + ALOGW("eglCreateImageKHR failed (%#x)", eglGetError()); + return CopyResult::UnknownError; + } + + uint32_t width = graphicBuffer->getWidth(); + uint32_t height = graphicBuffer->getHeight(); + // If this is a 90 or 270 degree rotation we need to swap width/height + // This is a fuzzy way of checking that. + if (texTransform[Matrix4::kSkewX] >= 0.5f || texTransform[Matrix4::kSkewX] <= -0.5f) { + std::swap(width, height); + } + 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 + // part of glReadPixels, so this shouldn't pose any major stalls. + glFinish(); + eglDestroyImageKHR(display, sourceImage); + return copyResult; +} + +CopyResult OpenGLReadback::copyGraphicBufferInto(GraphicBuffer* graphicBuffer, SkBitmap* bitmap) { + Rect srcRect; + Matrix4 transform; + transform.loadScale(1, -1, 1); + transform.translate(0, -1); + return copyGraphicBufferInto(graphicBuffer, transform, srcRect, bitmap); +} + +static float sFlipVInit[16] = { + 1, 0, 0, 0, + 0, -1, 0, 0, + 0, 0, 1, 0, + 0, 1, 0, 1, +}; + +static const Matrix4 sFlipV(sFlipVInit); + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +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 @@ -46,6 +131,13 @@ CopyResult Readback::copySurfaceInto(renderthread::RenderThread& renderThread, destWidth, destHeight, caches.maxTextureSize); return CopyResult::DestinationInvalid; } + + // TODO: Add support for RGBA_F16 destinations + if (bitmap->colorType() == kRGBA_F16_SkColorType) { + ALOGW("Can't copy surface into bitmap, RGBA_F16 config is not supported"); + return CopyResult::DestinationInvalid; + } + GLuint fbo = renderState.createFramebuffer(); if (!fbo) { ALOGW("Could not obtain an FBO"); @@ -98,64 +190,6 @@ CopyResult Readback::copySurfaceInto(renderthread::RenderThread& renderThread, glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0); - // Setup the source - sp<GraphicBuffer> sourceBuffer; - sp<Fence> sourceFence; - Matrix4 texTransform; - 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); - return CopyResult::UnknownError; - } - if (!sourceBuffer.get()) { - ALOGW("Surface doesn't have any previously queued frames, nothing to readback from"); - return CopyResult::SourceEmpty; - } - if (sourceBuffer->getUsage() & GRALLOC_USAGE_PROTECTED) { - ALOGW("Surface is protected, unable to copy from it"); - return CopyResult::SourceInvalid; - } - err = sourceFence->wait(500 /* ms */); - if (err != NO_ERROR) { - ALOGE("Timeout (500ms) exceeded waiting for buffer fence, abandoning readback attempt"); - return CopyResult::Timeout; - } - - // 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 - // to be able to properly sample from the buffer. - - // Create the EGLImage object that maps the GraphicBuffer - EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); - EGLClientBuffer clientBuffer = (EGLClientBuffer) sourceBuffer->getNativeBuffer(); - EGLint attrs[] = { EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE }; - - EGLImageKHR sourceImage = eglCreateImageKHR(display, EGL_NO_CONTEXT, - EGL_NATIVE_BUFFER_ANDROID, clientBuffer, attrs); - - if (sourceImage == EGL_NO_IMAGE_KHR) { - ALOGW("eglCreateImageKHR failed (%#x)", eglGetError()); - return CopyResult::UnknownError; - } - GLuint sourceTexId; - // Create a 2D texture to sample from the EGLImage - glGenTextures(1, &sourceTexId); - Caches::getInstance().textureState().bindTexture(GL_TEXTURE_EXTERNAL_OES, sourceTexId); - glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, sourceImage); - - GLenum status = GL_NO_ERROR; - while ((status = glGetError()) != GL_NO_ERROR) { - ALOGW("glEGLImageTargetTexture2DOES failed (%#x)", status); - eglDestroyImageKHR(display, sourceImage); - return CopyResult::UnknownError; - } - - Texture sourceTexture(caches); - sourceTexture.wrap(sourceTexId, - sourceBuffer->getWidth(), sourceBuffer->getHeight(), 0 /* total lie */); - { // Draw & readback renderState.setViewport(destWidth, destHeight); @@ -163,14 +197,25 @@ CopyResult Readback::copySurfaceInto(renderthread::RenderThread& renderThread, renderState.blend().syncEnabled(); renderState.stencil().disable(); - Rect destRect(destWidth, destHeight); + Matrix4 croppedTexTransform(texTransform); + if (!srcRect.isEmpty()) { + // We flipV to convert to 0,0 top-left for the srcRect + // coordinates then flip back to 0,0 bottom-left for + // GLES coordinates. + croppedTexTransform.multiply(sFlipV); + croppedTexTransform.translate(srcRect.left / sourceTexture.width(), + srcRect.top / sourceTexture.height(), 0); + croppedTexTransform.scale(srcRect.getWidth() / sourceTexture.width(), + srcRect.getHeight() / sourceTexture.height(), 1); + croppedTexTransform.multiply(sFlipV); + } Glop glop; GlopBuilder(renderState, caches, &glop) .setRoundRectClipState(nullptr) .setMeshTexturedUnitQuad(nullptr) - .setFillExternalTexture(sourceTexture, texTransform) + .setFillExternalTexture(sourceTexture, croppedTexTransform) .setTransform(Matrix4::identity(), TransformFlags::None) - .setModelViewMapUnitToRect(destRect) + .setModelViewMapUnitToRect(Rect(destWidth, destHeight)) .build(); Matrix4 ortho; ortho.loadOrtho(destWidth, destHeight); @@ -184,17 +229,45 @@ CopyResult Readback::copySurfaceInto(renderthread::RenderThread& renderThread, caches.textureState().deleteTexture(texture); renderState.deleteFramebuffer(fbo); - sourceTexture.deleteTexture(); - // All we're flushing & finishing is the deletion of the texture since - // copyTextureInto already did a major flush & finish as an implicit - // part of glReadPixels, so this shouldn't pose any major stalls. - glFinish(); - eglDestroyImageKHR(display, sourceImage); - GL_CHECKPOINT(MODERATE); return CopyResult::Success; } +CopyResult OpenGLReadbackImpl::copyImageInto(EGLImageKHR eglImage, + const Matrix4& imgTransform, int imgWidth, int imgHeight, const Rect& srcRect, + SkBitmap* bitmap) { + + Caches& caches = Caches::getInstance(); + GLuint sourceTexId; + // Create a 2D texture to sample from the EGLImage + glGenTextures(1, &sourceTexId); + caches.textureState().bindTexture(GL_TEXTURE_EXTERNAL_OES, sourceTexId); + glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, eglImage); + + GLenum status = GL_NO_ERROR; + while ((status = glGetError()) != GL_NO_ERROR) { + ALOGW("glEGLImageTargetTexture2DOES failed (%#x)", status); + return CopyResult::UnknownError; + } + + Texture sourceTexture(caches); + sourceTexture.wrap(sourceTexId, imgWidth, imgHeight, 0, 0 /* total lie */, + GL_TEXTURE_EXTERNAL_OES); + + CopyResult copyResult = copyTextureInto(caches, mRenderThread.renderState(), + sourceTexture, imgTransform, srcRect, bitmap); + sourceTexture.deleteTexture(); + return copyResult; +} + +bool OpenGLReadbackImpl::copyLayerInto(renderthread::RenderThread& renderThread, + GlLayer& layer, SkBitmap* bitmap) { + return CopyResult::Success == copyTextureInto(Caches::getInstance(), + renderThread.renderState(), layer.getTexture(), layer.getTexTransform(), + Rect(), bitmap); +} + + } // namespace uirenderer } // namespace android diff --git a/libs/hwui/OpenGLReadback.h b/libs/hwui/OpenGLReadback.h new file mode 100644 index 000000000000..c9222cff51da --- /dev/null +++ b/libs/hwui/OpenGLReadback.h @@ -0,0 +1,61 @@ +/* + * 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. + */ + +#pragma once + +#include "Readback.h" + +namespace android { +namespace uirenderer { + +class Matrix4; +class GlLayer; + +class OpenGLReadback : public Readback { +public: + virtual CopyResult copySurfaceInto(Surface& surface, const Rect& srcRect, + SkBitmap* bitmap) override; + virtual CopyResult copyGraphicBufferInto(GraphicBuffer* graphicBuffer, + 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; +private: + CopyResult copyGraphicBufferInto(GraphicBuffer* graphicBuffer, Matrix4& texTransform, + const Rect& srcRect, SkBitmap* bitmap); +}; + +class OpenGLReadbackImpl : public OpenGLReadback { +public: + OpenGLReadbackImpl(renderthread::RenderThread& thread) : OpenGLReadback(thread) {} + + /** + * Copies the layer's contents into the provided bitmap. + */ + static bool copyLayerInto(renderthread::RenderThread& renderThread, GlLayer& layer, + SkBitmap* bitmap); + +protected: + virtual CopyResult copyImageInto(EGLImageKHR eglImage, const Matrix4& imgTransform, + int imgWidth, int imgHeight, const Rect& srcRect, SkBitmap* bitmap) override; +}; + +} // namespace uirenderer +} // namespace android diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp deleted file mode 100644 index 9d821f38fab6..000000000000 --- a/libs/hwui/OpenGLRenderer.cpp +++ /dev/null @@ -1,2451 +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. - */ - -#include <GpuMemoryTracker.h> -#include "OpenGLRenderer.h" - -#include "DeferredDisplayList.h" -#include "GammaFontRenderer.h" -#include "Glop.h" -#include "GlopBuilder.h" -#include "Patch.h" -#include "PathTessellator.h" -#include "Properties.h" -#include "RenderNode.h" -#include "renderstate/MeshState.h" -#include "renderstate/RenderState.h" -#include "ShadowTessellator.h" -#include "SkiaShader.h" -#include "Vector.h" -#include "VertexBuffer.h" -#include "hwui/Canvas.h" -#include "utils/GLUtils.h" -#include "utils/PaintUtils.h" -#include "utils/TraceUtils.h" - -#include <stdlib.h> -#include <stdint.h> -#include <sys/types.h> - -#include <SkColor.h> -#include <SkPaintDefaults.h> -#include <SkPathOps.h> -#include <SkShader.h> -#include <SkTypeface.h> - -#include <utils/Log.h> -#include <utils/StopWatch.h> - -#include <private/hwui/DrawGlInfo.h> - -#include <ui/Rect.h> - -#if DEBUG_DETAILED_EVENTS - #define EVENT_LOGD(...) eventMarkDEBUG(__VA_ARGS__) -#else - #define EVENT_LOGD(...) -#endif - -namespace android { -namespace uirenderer { - -/////////////////////////////////////////////////////////////////////////////// -// Constructors/destructor -/////////////////////////////////////////////////////////////////////////////// - -OpenGLRenderer::OpenGLRenderer(RenderState& renderState) - : mState(*this) - , mCaches(Caches::getInstance()) - , mRenderState(renderState) - , mFrameStarted(false) - , mScissorOptimizationDisabled(false) - , mDirty(false) - , mLightCenter((Vector3){FLT_MIN, FLT_MIN, FLT_MIN}) - , mLightRadius(FLT_MIN) - , mAmbientShadowAlpha(0) - , mSpotShadowAlpha(0) { -} - -OpenGLRenderer::~OpenGLRenderer() { - // The context has already been destroyed at this point, do not call - // GL APIs. All GL state should be kept in Caches.h -} - -void OpenGLRenderer::initProperties() { - char property[PROPERTY_VALUE_MAX]; - if (property_get(PROPERTY_DISABLE_SCISSOR_OPTIMIZATION, property, "false")) { - mScissorOptimizationDisabled = !strcasecmp(property, "true"); - INIT_LOGD(" Scissor optimization %s", - mScissorOptimizationDisabled ? "disabled" : "enabled"); - } else { - INIT_LOGD(" Scissor optimization enabled"); - } -} - -void OpenGLRenderer::initLight(float lightRadius, uint8_t ambientShadowAlpha, - uint8_t spotShadowAlpha) { - mLightRadius = lightRadius; - mAmbientShadowAlpha = ambientShadowAlpha; - mSpotShadowAlpha = spotShadowAlpha; -} - -void OpenGLRenderer::setLightCenter(const Vector3& lightCenter) { - mLightCenter = lightCenter; -} - -/////////////////////////////////////////////////////////////////////////////// -// Setup -/////////////////////////////////////////////////////////////////////////////// - -void OpenGLRenderer::onViewportInitialized() { - glDisable(GL_DITHER); - glClearColor(0.0f, 0.0f, 0.0f, 0.0f); -} - -void OpenGLRenderer::setupFrameState(int viewportWidth, int viewportHeight, - float left, float top, float right, float bottom, bool opaque) { - mCaches.clearGarbage(); - mState.initializeSaveStack(viewportWidth, viewportHeight, - left, top, right, bottom, mLightCenter); - mOpaque = opaque; - mTilingClip.set(left, top, right, bottom); -} - -void OpenGLRenderer::startFrame() { - if (mFrameStarted) return; - mFrameStarted = true; - - mState.setDirtyClip(true); - - discardFramebuffer(mTilingClip.left, mTilingClip.top, mTilingClip.right, mTilingClip.bottom); - - mRenderState.setViewport(mState.getWidth(), mState.getHeight()); - - debugOverdraw(true, true); - - clear(mTilingClip.left, mTilingClip.top, - mTilingClip.right, mTilingClip.bottom, mOpaque); -} - -void OpenGLRenderer::prepareDirty(int viewportWidth, int viewportHeight, - float left, float top, float right, float bottom, bool opaque) { - - setupFrameState(viewportWidth, viewportHeight, left, top, right, bottom, opaque); - - // Layer renderers will start the frame immediately - // The framebuffer renderer will first defer the display list - // for each layer and wait until the first drawing command - // to start the frame - if (currentSnapshot()->fbo == 0) { - mRenderState.blend().syncEnabled(); - updateLayers(); - } else { - startFrame(); - } -} - -void OpenGLRenderer::discardFramebuffer(float left, float top, float right, float bottom) { - // If we know that we are going to redraw the entire framebuffer, - // perform a discard to let the driver know we don't need to preserve - // the back buffer for this frame. - if (mCaches.extensions().hasDiscardFramebuffer() && - left <= 0.0f && top <= 0.0f && right >= mState.getWidth() && bottom >= mState.getHeight()) { - const bool isFbo = getTargetFbo() == 0; - const GLenum attachments[] = { - isFbo ? (const GLenum) GL_COLOR_EXT : (const GLenum) GL_COLOR_ATTACHMENT0, - isFbo ? (const GLenum) GL_STENCIL_EXT : (const GLenum) GL_STENCIL_ATTACHMENT }; - glDiscardFramebufferEXT(GL_FRAMEBUFFER, 1, attachments); - } -} - -void OpenGLRenderer::clear(float left, float top, float right, float bottom, bool opaque) { - if (!opaque) { - mRenderState.scissor().setEnabled(true); - mRenderState.scissor().set(left, getViewportHeight() - bottom, right - left, bottom - top); - glClear(GL_COLOR_BUFFER_BIT); - mDirty = true; - return; - } - - mRenderState.scissor().reset(); -} - -bool OpenGLRenderer::finish() { - renderOverdraw(); - mTempPaths.clear(); - - // When finish() is invoked on FBO 0 we've reached the end - // of the current frame - if (getTargetFbo() == 0) { - mCaches.pathCache.trim(); - mCaches.tessellationCache.trim(); - } - - if (!suppressErrorChecks()) { - GL_CHECKPOINT(MODERATE); - -#if DEBUG_MEMORY_USAGE - mCaches.dumpMemoryUsage(); - GPUMemoryTracker::dump(); -#else - if (Properties::debugLevel & kDebugMemory) { - mCaches.dumpMemoryUsage(); - } -#endif - } - - mFrameStarted = false; - - return reportAndClearDirty(); -} - -void OpenGLRenderer::resumeAfterLayer() { - mRenderState.setViewport(getViewportWidth(), getViewportHeight()); - mRenderState.bindFramebuffer(currentSnapshot()->fbo); - debugOverdraw(true, false); - - mRenderState.scissor().reset(); - dirtyClip(); -} - -void OpenGLRenderer::callDrawGLFunction(Functor* functor, Rect& dirty) { - if (mState.currentlyIgnored()) return; - - Rect clip(mState.currentRenderTargetClip()); - clip.snapToPixelBoundaries(); - - // Since we don't know what the functor will draw, let's dirty - // the entire clip region - if (hasLayer()) { - dirtyLayerUnchecked(clip, getRegion()); - } - - DrawGlInfo info; - info.clipLeft = clip.left; - info.clipTop = clip.top; - info.clipRight = clip.right; - info.clipBottom = clip.bottom; - info.isLayer = hasLayer(); - info.width = getViewportWidth(); - info.height = getViewportHeight(); - currentTransform()->copyTo(&info.transform[0]); - - bool prevDirtyClip = mState.getDirtyClip(); - // setup GL state for functor - if (mState.getDirtyClip()) { - setStencilFromClip(); // can issue draws, so must precede enableScissor()/interrupt() - } - if (mRenderState.scissor().setEnabled(true) || prevDirtyClip) { - setScissorFromClip(); - } - - mRenderState.invokeFunctor(functor, DrawGlInfo::kModeDraw, &info); - // Scissor may have been modified, reset dirty clip - dirtyClip(); - - mDirty = true; -} - -/////////////////////////////////////////////////////////////////////////////// -// Debug -/////////////////////////////////////////////////////////////////////////////// - -void OpenGLRenderer::eventMarkDEBUG(const char* fmt, ...) const { -#if DEBUG_DETAILED_EVENTS - const int BUFFER_SIZE = 256; - va_list ap; - char buf[BUFFER_SIZE]; - - va_start(ap, fmt); - vsnprintf(buf, BUFFER_SIZE, fmt, ap); - va_end(ap); - - eventMark(buf); -#endif -} - - -void OpenGLRenderer::eventMark(const char* name) const { - mCaches.eventMark(0, name); -} - -void OpenGLRenderer::startMark(const char* name) const { - mCaches.startMark(0, name); -} - -void OpenGLRenderer::endMark() const { - mCaches.endMark(); -} - -void OpenGLRenderer::debugOverdraw(bool enable, bool clear) { - mRenderState.debugOverdraw(enable, clear); -} - -void OpenGLRenderer::renderOverdraw() { - if (Properties::debugOverdraw && getTargetFbo() == 0) { - const Rect* clip = &mTilingClip; - - mRenderState.scissor().setEnabled(true); - mRenderState.scissor().set(clip->left, - mState.firstSnapshot()->getViewportHeight() - clip->bottom, - clip->right - clip->left, - clip->bottom - clip->top); - - // 1x overdraw - mRenderState.stencil().enableDebugTest(2); - drawColor(mCaches.getOverdrawColor(1), SkXfermode::kSrcOver_Mode); - - // 2x overdraw - mRenderState.stencil().enableDebugTest(3); - drawColor(mCaches.getOverdrawColor(2), SkXfermode::kSrcOver_Mode); - - // 3x overdraw - mRenderState.stencil().enableDebugTest(4); - drawColor(mCaches.getOverdrawColor(3), SkXfermode::kSrcOver_Mode); - - // 4x overdraw and higher - mRenderState.stencil().enableDebugTest(4, true); - drawColor(mCaches.getOverdrawColor(4), SkXfermode::kSrcOver_Mode); - - mRenderState.stencil().disable(); - } -} - -/////////////////////////////////////////////////////////////////////////////// -// Layers -/////////////////////////////////////////////////////////////////////////////// - -bool OpenGLRenderer::updateLayer(Layer* layer, bool inFrame) { - if (layer->deferredUpdateScheduled && layer->renderer - && layer->renderNode.get() && layer->renderNode->isRenderable()) { - - if (inFrame) { - debugOverdraw(false, false); - } - - if (CC_UNLIKELY(inFrame || Properties::drawDeferDisabled)) { - layer->render(*this); - } else { - layer->defer(*this); - } - - if (inFrame) { - resumeAfterLayer(); - } - - layer->debugDrawUpdate = Properties::debugLayersUpdates; - layer->hasDrawnSinceUpdate = false; - - return true; - } - - return false; -} - -void OpenGLRenderer::updateLayers() { - // If draw deferring is enabled this method will simply defer - // the display list of each individual layer. The layers remain - // in the layer updates list which will be cleared by flushLayers(). - int count = mLayerUpdates.size(); - if (count > 0) { - if (CC_UNLIKELY(Properties::drawDeferDisabled)) { - startMark("Layer Updates"); - } else { - startMark("Defer Layer Updates"); - } - - // Note: it is very important to update the layers in order - for (int i = 0; i < count; i++) { - Layer* layer = mLayerUpdates[i].get(); - updateLayer(layer, false); - } - - if (CC_UNLIKELY(Properties::drawDeferDisabled)) { - mLayerUpdates.clear(); - mRenderState.bindFramebuffer(getTargetFbo()); - } - endMark(); - } -} - -void OpenGLRenderer::flushLayers() { - int count = mLayerUpdates.size(); - if (count > 0) { - startMark("Apply Layer Updates"); - - // Note: it is very important to update the layers in order - for (int i = 0; i < count; i++) { - mLayerUpdates[i]->flush(); - } - - mLayerUpdates.clear(); - mRenderState.bindFramebuffer(getTargetFbo()); - - endMark(); - } -} - -void OpenGLRenderer::pushLayerUpdate(Layer* layer) { - if (layer) { - // Make sure we don't introduce duplicates. - // SortedVector would do this automatically but we need to respect - // the insertion order. The linear search is not an issue since - // this list is usually very short (typically one item, at most a few) - for (int i = mLayerUpdates.size() - 1; i >= 0; i--) { - if (mLayerUpdates[i] == layer) { - return; - } - } - mLayerUpdates.push_back(layer); - } -} - -void OpenGLRenderer::cancelLayerUpdate(Layer* layer) { - if (layer) { - for (int i = mLayerUpdates.size() - 1; i >= 0; i--) { - if (mLayerUpdates[i] == layer) { - mLayerUpdates.erase(mLayerUpdates.begin() + i); - break; - } - } - } -} - -void OpenGLRenderer::flushLayerUpdates() { - ATRACE_NAME("Update HW Layers"); - mRenderState.blend().syncEnabled(); - updateLayers(); - flushLayers(); - // Wait for all the layer updates to be executed - glFinish(); -} - -void OpenGLRenderer::markLayersAsBuildLayers() { - for (size_t i = 0; i < mLayerUpdates.size(); i++) { - mLayerUpdates[i]->wasBuildLayered = true; - } -} - -/////////////////////////////////////////////////////////////////////////////// -// State management -/////////////////////////////////////////////////////////////////////////////// - -void OpenGLRenderer::onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) { - bool restoreViewport = removed.flags & Snapshot::kFlagIsFboLayer; - bool restoreClip = removed.flags & Snapshot::kFlagClipSet; - bool restoreLayer = removed.flags & Snapshot::kFlagIsLayer; - - if (restoreViewport) { - mRenderState.setViewport(getViewportWidth(), getViewportHeight()); - } - - if (restoreClip) { - dirtyClip(); - } - - if (restoreLayer) { - endMark(); // Savelayer - ATRACE_END(); // SaveLayer - startMark("ComposeLayer"); - composeLayer(removed, restored); - endMark(); - } -} - -/////////////////////////////////////////////////////////////////////////////// -// Layers -/////////////////////////////////////////////////////////////////////////////// - -int OpenGLRenderer::saveLayer(float left, float top, float right, float bottom, - const SkPaint* paint, int flags, const SkPath* convexMask) { - // force matrix/clip isolation for layer - flags |= SaveFlags::MatrixClip; - - const int count = mState.saveSnapshot(flags); - - if (!mState.currentlyIgnored()) { - createLayer(left, top, right, bottom, paint, flags, convexMask); - } - - return count; -} - -void OpenGLRenderer::calculateLayerBoundsAndClip(Rect& bounds, Rect& clip, bool fboLayer) { - const Rect untransformedBounds(bounds); - - currentTransform()->mapRect(bounds); - - // Layers only make sense if they are in the framebuffer's bounds - bounds.doIntersect(mState.currentRenderTargetClip()); - if (!bounds.isEmpty()) { - // We cannot work with sub-pixels in this case - bounds.snapToPixelBoundaries(); - - // When the layer is not an FBO, we may use glCopyTexImage so we - // need to make sure the layer does not extend outside the bounds - // of the framebuffer - const Snapshot& previous = *(currentSnapshot()->previous); - Rect previousViewport(0, 0, previous.getViewportWidth(), previous.getViewportHeight()); - - bounds.doIntersect(previousViewport); - if (!bounds.isEmpty() && fboLayer) { - clip.set(bounds); - mat4 inverse; - inverse.loadInverse(*currentTransform()); - inverse.mapRect(clip); - clip.snapToPixelBoundaries(); - clip.doIntersect(untransformedBounds); - if (!clip.isEmpty()) { - clip.translate(-untransformedBounds.left, -untransformedBounds.top); - bounds.set(untransformedBounds); - } - } - } -} - -void OpenGLRenderer::updateSnapshotIgnoreForLayer(const Rect& bounds, const Rect& clip, - bool fboLayer, int alpha) { - if (bounds.isEmpty() || bounds.getWidth() > mCaches.maxTextureSize || - bounds.getHeight() > mCaches.maxTextureSize || - (fboLayer && clip.isEmpty())) { - writableSnapshot()->empty = fboLayer; - } else { - writableSnapshot()->invisible = writableSnapshot()->invisible || (alpha <= 0 && fboLayer); - } -} - -int OpenGLRenderer::saveLayerDeferred(float left, float top, float right, float bottom, - const SkPaint* paint, int flags) { - const int count = mState.saveSnapshot(flags); - - if (!mState.currentlyIgnored() && (flags & SaveFlags::ClipToLayer)) { - // initialize the snapshot as though it almost represents an FBO layer so deferred draw - // operations will be able to store and restore the current clip and transform info, and - // quick rejection will be correct (for display lists) - - Rect bounds(left, top, right, bottom); - Rect clip; - calculateLayerBoundsAndClip(bounds, clip, true); - updateSnapshotIgnoreForLayer(bounds, clip, true, PaintUtils::getAlphaDirect(paint)); - - if (!mState.currentlyIgnored()) { - writableSnapshot()->resetTransform(-bounds.left, -bounds.top, 0.0f); - writableSnapshot()->resetClip(clip.left, clip.top, clip.right, clip.bottom); - writableSnapshot()->initializeViewport(bounds.getWidth(), bounds.getHeight()); - writableSnapshot()->roundRectClipState = nullptr; - } - } - - return count; -} - -/** - * Layers are viewed by Skia are slightly different than layers in image editing - * programs (for instance.) When a layer is created, previously created layers - * and the frame buffer still receive every drawing command. For instance, if a - * layer is created and a shape intersecting the bounds of the layers and the - * framebuffer is draw, the shape will be drawn on both (unless the layer was - * created with the SaveFlags::ClipToLayer flag.) - * - * A way to implement layers is to create an FBO for each layer, backed by an RGBA - * texture. Unfortunately, this is inefficient as it requires every primitive to - * be drawn n + 1 times, where n is the number of active layers. In practice this - * means, for every primitive: - * - Switch active frame buffer - * - Change viewport, clip and projection matrix - * - Issue the drawing - * - * Switching rendering target n + 1 times per drawn primitive is extremely costly. - * To avoid this, layers are implemented in a different way here, at least in the - * general case. FBOs are used, as an optimization, when the "clip to layer" flag - * is set. When this flag is set we can redirect all drawing operations into a - * single FBO. - * - * This implementation relies on the frame buffer being at least RGBA 8888. When - * a layer is created, only a texture is created, not an FBO. The content of the - * frame buffer contained within the layer's bounds is copied into this texture - * using glCopyTexImage2D(). The layer's region is then cleared(1) in the frame - * buffer and drawing continues as normal. This technique therefore treats the - * frame buffer as a scratch buffer for the layers. - * - * To compose the layers back onto the frame buffer, each layer texture - * (containing the original frame buffer data) is drawn as a simple quad over - * the frame buffer. The trick is that the quad is set as the composition - * destination in the blending equation, and the frame buffer becomes the source - * of the composition. - * - * Drawing layers with an alpha value requires an extra step before composition. - * An empty quad is drawn over the layer's region in the frame buffer. This quad - * is drawn with the rgba color (0,0,0,alpha). The alpha value offered by the - * quad is used to multiply the colors in the frame buffer. This is achieved by - * changing the GL blend functions for the GL_FUNC_ADD blend equation to - * GL_ZERO, GL_SRC_ALPHA. - * - * Because glCopyTexImage2D() can be slow, an alternative implementation might - * be use to draw a single clipped layer. The implementation described above - * is correct in every case. - * - * (1) The frame buffer is actually not cleared right away. To allow the GPU - * to potentially optimize series of calls to glCopyTexImage2D, the frame - * buffer is left untouched until the first drawing operation. Only when - * something actually gets drawn are the layers regions cleared. - */ -bool OpenGLRenderer::createLayer(float left, float top, float right, float bottom, - const SkPaint* paint, int flags, const SkPath* convexMask) { - LAYER_LOGD("Requesting layer %.2fx%.2f", right - left, bottom - top); - LAYER_LOGD("Layer cache size = %d", mCaches.layerCache.getSize()); - - const bool fboLayer = flags & SaveFlags::ClipToLayer; - - // Window coordinates of the layer - Rect clip; - Rect bounds(left, top, right, bottom); - calculateLayerBoundsAndClip(bounds, clip, fboLayer); - updateSnapshotIgnoreForLayer(bounds, clip, fboLayer, PaintUtils::getAlphaDirect(paint)); - - // Bail out if we won't draw in this snapshot - if (mState.currentlyIgnored()) { - return false; - } - - mCaches.textureState().activateTexture(0); - Layer* layer = mCaches.layerCache.get(mRenderState, bounds.getWidth(), bounds.getHeight()); - if (!layer) { - return false; - } - - layer->setPaint(paint); - layer->layer.set(bounds); - layer->texCoords.set(0.0f, bounds.getHeight() / float(layer->getHeight()), - bounds.getWidth() / float(layer->getWidth()), 0.0f); - - layer->setBlend(true); - layer->setDirty(false); - layer->setConvexMask(convexMask); // note: the mask must be cleared before returning to the cache - - // Save the layer in the snapshot - writableSnapshot()->flags |= Snapshot::kFlagIsLayer; - writableSnapshot()->layer = layer; - - ATRACE_FORMAT_BEGIN("%ssaveLayer %ux%u", - fboLayer ? "" : "unclipped ", - layer->getWidth(), layer->getHeight()); - startMark("SaveLayer"); - if (fboLayer) { - return createFboLayer(layer, bounds, clip); - } else { - // Copy the framebuffer into the layer - layer->bindTexture(); - if (!bounds.isEmpty()) { - if (layer->isEmpty()) { - // Workaround for some GL drivers. When reading pixels lying outside - // of the window we should get undefined values for those pixels. - // Unfortunately some drivers will turn the entire target texture black - // when reading outside of the window. - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, layer->getWidth(), layer->getHeight(), - 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); - layer->setEmpty(false); - } - - glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, - bounds.left, getViewportHeight() - bounds.bottom, - bounds.getWidth(), bounds.getHeight()); - - // Enqueue the buffer coordinates to clear the corresponding region later - mLayers.push_back(Rect(bounds)); - } - } - - return true; -} - -bool OpenGLRenderer::createFboLayer(Layer* layer, Rect& bounds, Rect& clip) { - layer->clipRect.set(clip); - layer->setFbo(mRenderState.createFramebuffer()); - - writableSnapshot()->region = &writableSnapshot()->layer->region; - writableSnapshot()->flags |= Snapshot::kFlagFboTarget | Snapshot::kFlagIsFboLayer; - writableSnapshot()->fbo = layer->getFbo(); - writableSnapshot()->resetTransform(-bounds.left, -bounds.top, 0.0f); - writableSnapshot()->resetClip(clip.left, clip.top, clip.right, clip.bottom); - writableSnapshot()->initializeViewport(bounds.getWidth(), bounds.getHeight()); - writableSnapshot()->roundRectClipState = nullptr; - - debugOverdraw(false, false); - // Bind texture to FBO - mRenderState.bindFramebuffer(layer->getFbo()); - layer->bindTexture(); - - // Initialize the texture if needed - if (layer->isEmpty()) { - layer->allocateTexture(); - layer->setEmpty(false); - } - - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, - layer->getTextureId(), 0); - - // Clear the FBO, expand the clear region by 1 to get nice bilinear filtering - mRenderState.scissor().setEnabled(true); - mRenderState.scissor().set(clip.left - 1.0f, bounds.getHeight() - clip.bottom - 1.0f, - clip.getWidth() + 2.0f, clip.getHeight() + 2.0f); - glClear(GL_COLOR_BUFFER_BIT); - - dirtyClip(); - - // Change the ortho projection - mRenderState.setViewport(bounds.getWidth(), bounds.getHeight()); - return true; -} - -/** - * Read the documentation of createLayer() before doing anything in this method. - */ -void OpenGLRenderer::composeLayer(const Snapshot& removed, const Snapshot& restored) { - if (!removed.layer) { - ALOGE("Attempting to compose a layer that does not exist"); - return; - } - - Layer* layer = removed.layer; - const Rect& rect = layer->layer; - const bool fboLayer = removed.flags & Snapshot::kFlagIsFboLayer; - - bool clipRequired = false; - mState.calculateQuickRejectForScissor(rect.left, rect.top, rect.right, rect.bottom, - &clipRequired, nullptr, false); // safely ignore return, should never be rejected - mRenderState.scissor().setEnabled(mScissorOptimizationDisabled || clipRequired); - - if (fboLayer) { - // Detach the texture from the FBO - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); - - layer->removeFbo(false); - - // Unbind current FBO and restore previous one - mRenderState.bindFramebuffer(restored.fbo); - debugOverdraw(true, false); - } - - if (!fboLayer && layer->getAlpha() < 255) { - SkPaint layerPaint; - layerPaint.setAlpha(layer->getAlpha()); - layerPaint.setXfermodeMode(SkXfermode::kDstIn_Mode); - layerPaint.setColorFilter(layer->getColorFilter()); - - drawColorRect(rect.left, rect.top, rect.right, rect.bottom, &layerPaint, true); - // Required below, composeLayerRect() will divide by 255 - layer->setAlpha(255); - } - - mRenderState.meshState().unbindMeshBuffer(); - - mCaches.textureState().activateTexture(0); - - // When the layer is stored in an FBO, we can save a bit of fillrate by - // drawing only the dirty region - if (fboLayer) { - dirtyLayer(rect.left, rect.top, rect.right, rect.bottom, *restored.transform); - composeLayerRegion(layer, rect); - } else if (!rect.isEmpty()) { - dirtyLayer(rect.left, rect.top, rect.right, rect.bottom); - - save(0); - // the layer contains screen buffer content that shouldn't be alpha modulated - // (and any necessary alpha modulation was handled drawing into the layer) - writableSnapshot()->alpha = 1.0f; - composeLayerRectSwapped(layer, rect); - restore(); - } - - dirtyClip(); - - // Failing to add the layer to the cache should happen only if the layer is too large - layer->setConvexMask(nullptr); - if (!mCaches.layerCache.put(layer)) { - LAYER_LOGD("Deleting layer"); - layer->decStrong(nullptr); - } -} - -void OpenGLRenderer::drawTextureLayer(Layer* layer, const Rect& rect) { - const bool tryToSnap = !layer->getForceFilter() - && layer->getWidth() == (uint32_t) rect.getWidth() - && layer->getHeight() == (uint32_t) rect.getHeight(); - Glop glop; - GlopBuilder(mRenderState, mCaches, &glop) - .setRoundRectClipState(currentSnapshot()->roundRectClipState) - .setMeshTexturedUvQuad(nullptr, Rect(0, 1, 1, 0)) // TODO: simplify with VBO - .setFillTextureLayer(*layer, getLayerAlpha(layer)) - .setTransform(*currentSnapshot(), TransformFlags::None) - .setModelViewMapUnitToRectOptionalSnap(tryToSnap, rect) - .build(); - renderGlop(glop); -} - -void OpenGLRenderer::composeLayerRectSwapped(Layer* layer, const Rect& rect) { - Glop glop; - GlopBuilder(mRenderState, mCaches, &glop) - .setRoundRectClipState(currentSnapshot()->roundRectClipState) - .setMeshTexturedUvQuad(nullptr, layer->texCoords) - .setFillLayer(layer->getTexture(), layer->getColorFilter(), - getLayerAlpha(layer), layer->getMode(), Blend::ModeOrderSwap::Swap) - .setTransform(*currentSnapshot(), TransformFlags::MeshIgnoresCanvasTransform) - .setModelViewMapUnitToRect(rect) - .build(); - renderGlop(glop); -} - -void OpenGLRenderer::composeLayerRect(Layer* layer, const Rect& rect) { - if (layer->isTextureLayer()) { - EVENT_LOGD("composeTextureLayerRect"); - drawTextureLayer(layer, rect); - } else { - EVENT_LOGD("composeHardwareLayerRect"); - - const bool tryToSnap = layer->getWidth() == static_cast<uint32_t>(rect.getWidth()) - && layer->getHeight() == static_cast<uint32_t>(rect.getHeight()); - Glop glop; - GlopBuilder(mRenderState, mCaches, &glop) - .setRoundRectClipState(currentSnapshot()->roundRectClipState) - .setMeshTexturedUvQuad(nullptr, layer->texCoords) - .setFillLayer(layer->getTexture(), layer->getColorFilter(), getLayerAlpha(layer), layer->getMode(), Blend::ModeOrderSwap::NoSwap) - .setTransform(*currentSnapshot(), TransformFlags::None) - .setModelViewMapUnitToRectOptionalSnap(tryToSnap, rect) - .build(); - renderGlop(glop); - } -} - -/** - * Issues the command X, and if we're composing a save layer to the fbo or drawing a newly updated - * hardware layer with overdraw debug on, draws again to the stencil only, so that these draw - * operations are correctly counted twice for overdraw. NOTE: assumes composeLayerRegion only used - * by saveLayer's restore - */ -#define DRAW_DOUBLE_STENCIL_IF(COND, DRAW_COMMAND) { \ - DRAW_COMMAND; \ - if (CC_UNLIKELY(Properties::debugOverdraw && getTargetFbo() == 0 && (COND))) { \ - glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); \ - DRAW_COMMAND; \ - glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); \ - } \ - } - -#define DRAW_DOUBLE_STENCIL(DRAW_COMMAND) DRAW_DOUBLE_STENCIL_IF(true, DRAW_COMMAND) - -// This class is purely for inspection. It inherits from SkShader, but Skia does not know how to -// use it. The OpenGLRenderer will look at it to find its Layer and whether it is opaque. -class LayerShader : public SkShader { -public: - LayerShader(Layer* layer, const SkMatrix* localMatrix) - : INHERITED(localMatrix) - , mLayer(layer) { - } - - virtual bool asACustomShader(void** data) const override { - if (data) { - *data = static_cast<void*>(mLayer); - } - return true; - } - - virtual bool isOpaque() const override { - return !mLayer->isBlend(); - } - -protected: - virtual void shadeSpan(int x, int y, SkPMColor[], int count) { - LOG_ALWAYS_FATAL("LayerShader should never be drawn with raster backend."); - } - - virtual void flatten(SkWriteBuffer&) const override { - LOG_ALWAYS_FATAL("LayerShader should never be flattened."); - } - - virtual Factory getFactory() const override { - LOG_ALWAYS_FATAL("LayerShader should never be created from a stream."); - return nullptr; - } -private: - // Unowned. - Layer* mLayer; - typedef SkShader INHERITED; -}; - -void OpenGLRenderer::composeLayerRegion(Layer* layer, const Rect& rect) { - if (CC_UNLIKELY(layer->region.isEmpty())) return; // nothing to draw - - if (layer->getConvexMask()) { - save(SaveFlags::MatrixClip); - - // clip to the area of the layer the mask can be larger - clipRect(rect.left, rect.top, rect.right, rect.bottom, SkRegion::kIntersect_Op); - - SkPaint paint; - paint.setAntiAlias(true); - paint.setColor(SkColorSetARGB(int(getLayerAlpha(layer) * 255), 0, 0, 0)); - - // create LayerShader to map SaveLayer content into subsequent draw - SkMatrix shaderMatrix; - shaderMatrix.setTranslate(rect.left, rect.bottom); - shaderMatrix.preScale(1, -1); - LayerShader layerShader(layer, &shaderMatrix); - paint.setShader(&layerShader); - - // Since the drawing primitive is defined in local drawing space, - // we don't need to modify the draw matrix - const SkPath* maskPath = layer->getConvexMask(); - DRAW_DOUBLE_STENCIL(drawConvexPath(*maskPath, &paint)); - - paint.setShader(nullptr); - restore(); - - return; - } - - if (layer->region.isRect()) { - layer->setRegionAsRect(); - - DRAW_DOUBLE_STENCIL(composeLayerRect(layer, layer->regionRect)); - - layer->region.clear(); - return; - } - - EVENT_LOGD("composeLayerRegion"); - // standard Region based draw - size_t count; - const android::Rect* rects; - Region safeRegion; - if (CC_LIKELY(hasRectToRectTransform())) { - rects = layer->region.getArray(&count); - } else { - safeRegion = Region::createTJunctionFreeRegion(layer->region); - rects = safeRegion.getArray(&count); - } - - const float texX = 1.0f / float(layer->getWidth()); - const float texY = 1.0f / float(layer->getHeight()); - const float height = rect.getHeight(); - - TextureVertex quadVertices[count * 4]; - TextureVertex* mesh = &quadVertices[0]; - for (size_t i = 0; i < count; i++) { - const android::Rect* r = &rects[i]; - - const float u1 = r->left * texX; - const float v1 = (height - r->top) * texY; - const float u2 = r->right * texX; - const float v2 = (height - r->bottom) * texY; - - // TODO: Reject quads outside of the clip - TextureVertex::set(mesh++, r->left, r->top, u1, v1); - TextureVertex::set(mesh++, r->right, r->top, u2, v1); - TextureVertex::set(mesh++, r->left, r->bottom, u1, v2); - TextureVertex::set(mesh++, r->right, r->bottom, u2, v2); - } - Rect modelRect = Rect(rect.getWidth(), rect.getHeight()); - Glop glop; - GlopBuilder(mRenderState, mCaches, &glop) - .setRoundRectClipState(currentSnapshot()->roundRectClipState) - .setMeshTexturedIndexedQuads(&quadVertices[0], count * 6) - .setFillLayer(layer->getTexture(), layer->getColorFilter(), getLayerAlpha(layer), layer->getMode(), Blend::ModeOrderSwap::NoSwap) - .setTransform(*currentSnapshot(), TransformFlags::None) - .setModelViewOffsetRectSnap(rect.left, rect.top, modelRect) - .build(); - DRAW_DOUBLE_STENCIL_IF(!layer->hasDrawnSinceUpdate, renderGlop(glop)); - -#if DEBUG_LAYERS_AS_REGIONS - drawRegionRectsDebug(layer->region); -#endif - - layer->region.clear(); -} - -#if DEBUG_LAYERS_AS_REGIONS -void OpenGLRenderer::drawRegionRectsDebug(const Region& region) { - size_t count; - const android::Rect* rects = region.getArray(&count); - - uint32_t colors[] = { - 0x7fff0000, 0x7f00ff00, - 0x7f0000ff, 0x7fff00ff, - }; - - int offset = 0; - int32_t top = rects[0].top; - - for (size_t i = 0; i < count; i++) { - if (top != rects[i].top) { - offset ^= 0x2; - top = rects[i].top; - } - - SkPaint paint; - paint.setColor(colors[offset + (i & 0x1)]); - Rect r(rects[i].left, rects[i].top, rects[i].right, rects[i].bottom); - drawColorRect(r.left, r.top, r.right, r.bottom, paint); - } -} -#endif - -void OpenGLRenderer::drawRegionRects(const SkRegion& region, const SkPaint& paint, bool dirty) { - Vector<float> rects; - - SkRegion::Iterator it(region); - while (!it.done()) { - const SkIRect& r = it.rect(); - rects.push(r.fLeft); - rects.push(r.fTop); - rects.push(r.fRight); - rects.push(r.fBottom); - it.next(); - } - - drawColorRects(rects.array(), rects.size(), &paint, true, dirty, false); -} - -void OpenGLRenderer::dirtyLayer(const float left, const float top, - const float right, const float bottom, const Matrix4& transform) { - if (hasLayer()) { - Rect bounds(left, top, right, bottom); - transform.mapRect(bounds); - dirtyLayerUnchecked(bounds, getRegion()); - } -} - -void OpenGLRenderer::dirtyLayer(const float left, const float top, - const float right, const float bottom) { - if (hasLayer()) { - Rect bounds(left, top, right, bottom); - dirtyLayerUnchecked(bounds, getRegion()); - } -} - -void OpenGLRenderer::dirtyLayerUnchecked(Rect& bounds, Region* region) { - bounds.doIntersect(mState.currentRenderTargetClip()); - if (!bounds.isEmpty()) { - bounds.snapToPixelBoundaries(); - android::Rect dirty(bounds.left, bounds.top, bounds.right, bounds.bottom); - if (!dirty.isEmpty()) { - region->orSelf(dirty); - } - } -} - -void OpenGLRenderer::clearLayerRegions() { - const size_t quadCount = mLayers.size(); - if (quadCount == 0) return; - - if (!mState.currentlyIgnored()) { - EVENT_LOGD("clearLayerRegions"); - // Doing several glScissor/glClear here can negatively impact - // GPUs with a tiler architecture, instead we draw quads with - // the Clear blending mode - - // The list contains bounds that have already been clipped - // against their initial clip rect, and the current clip - // is likely different so we need to disable clipping here - bool scissorChanged = mRenderState.scissor().setEnabled(false); - - Vertex mesh[quadCount * 4]; - Vertex* vertex = mesh; - - for (uint32_t i = 0; i < quadCount; i++) { - const Rect& bounds = mLayers[i]; - - Vertex::set(vertex++, bounds.left, bounds.top); - Vertex::set(vertex++, bounds.right, bounds.top); - Vertex::set(vertex++, bounds.left, bounds.bottom); - Vertex::set(vertex++, bounds.right, bounds.bottom); - } - // We must clear the list of dirty rects before we - // call clearLayerRegions() in renderGlop to prevent - // stencil setup from doing the same thing again - mLayers.clear(); - - const int transformFlags = TransformFlags::MeshIgnoresCanvasTransform; - Glop glop; - GlopBuilder(mRenderState, mCaches, &glop) - .setRoundRectClipState(nullptr) // clear ignores clip state - .setMeshIndexedQuads(&mesh[0], quadCount) - .setFillClear() - .setTransform(*currentSnapshot(), transformFlags) - .setModelViewOffsetRect(0, 0, Rect(currentSnapshot()->getRenderTargetClip())) - .build(); - renderGlop(glop, GlopRenderType::LayerClear); - - if (scissorChanged) mRenderState.scissor().setEnabled(true); - } else { - mLayers.clear(); - } -} - -/////////////////////////////////////////////////////////////////////////////// -// State Deferral -/////////////////////////////////////////////////////////////////////////////// - -bool OpenGLRenderer::storeDisplayState(DeferredDisplayState& state, int stateDeferFlags) { - const Rect& currentClip = mState.currentRenderTargetClip(); - const mat4* currentMatrix = currentTransform(); - - if (stateDeferFlags & kStateDeferFlag_Draw) { - // state has bounds initialized in local coordinates - if (!state.mBounds.isEmpty()) { - currentMatrix->mapRect(state.mBounds); - Rect clippedBounds(state.mBounds); - // NOTE: if we ever want to use this clipping info to drive whether the scissor - // is used, it should more closely duplicate the quickReject logic (in how it uses - // snapToPixelBoundaries) - - clippedBounds.doIntersect(currentClip); - if (clippedBounds.isEmpty()) { - // quick rejected - return true; - } - - state.mClipSideFlags = kClipSide_None; - if (!currentClip.contains(state.mBounds)) { - int& flags = state.mClipSideFlags; - // op partially clipped, so record which sides are clipped for clip-aware merging - if (currentClip.left > state.mBounds.left) flags |= kClipSide_Left; - if (currentClip.top > state.mBounds.top) flags |= kClipSide_Top; - if (currentClip.right < state.mBounds.right) flags |= kClipSide_Right; - if (currentClip.bottom < state.mBounds.bottom) flags |= kClipSide_Bottom; - } - state.mBounds.set(clippedBounds); - } else { - // Empty bounds implies size unknown. Label op as conservatively clipped to disable - // overdraw avoidance (since we don't know what it overlaps) - state.mClipSideFlags = kClipSide_ConservativeFull; - state.mBounds.set(currentClip); - } - } - - state.mClipValid = (stateDeferFlags & kStateDeferFlag_Clip); - if (state.mClipValid) { - state.mClip.set(currentClip); - } - - // Transform and alpha always deferred, since they are used by state operations - // (Note: saveLayer/restore use colorFilter and alpha, so we just save restore everything) - state.mMatrix = *currentMatrix; - state.mAlpha = currentSnapshot()->alpha; - - // always store/restore, since these are just pointers - state.mRoundRectClipState = currentSnapshot()->roundRectClipState; -#if !HWUI_NEW_OPS - state.mProjectionPathMask = currentSnapshot()->projectionPathMask; -#endif - return false; -} - -void OpenGLRenderer::restoreDisplayState(const DeferredDisplayState& state, bool skipClipRestore) { - setGlobalMatrix(state.mMatrix); - writableSnapshot()->alpha = state.mAlpha; - writableSnapshot()->roundRectClipState = state.mRoundRectClipState; -#if !HWUI_NEW_OPS - writableSnapshot()->projectionPathMask = state.mProjectionPathMask; -#endif - - if (state.mClipValid && !skipClipRestore) { - writableSnapshot()->setClip(state.mClip.left, state.mClip.top, - state.mClip.right, state.mClip.bottom); - dirtyClip(); - } -} - -/** - * Merged multidraw (such as in drawText and drawBitmaps rely on the fact that no clipping is done - * in the draw path. Instead, clipping is done ahead of time - either as a single clip rect (when at - * least one op is clipped), or disabled entirely (because no merged op is clipped) - * - * This method should be called when restoreDisplayState() won't be restoring the clip - */ -void OpenGLRenderer::setupMergedMultiDraw(const Rect* clipRect) { - if (clipRect != nullptr) { - writableSnapshot()->setClip(clipRect->left, clipRect->top, clipRect->right, clipRect->bottom); - } else { - writableSnapshot()->setClip(0, 0, mState.getWidth(), mState.getHeight()); - } - dirtyClip(); - bool enableScissor = (clipRect != nullptr) || mScissorOptimizationDisabled; - mRenderState.scissor().setEnabled(enableScissor); -} - -/////////////////////////////////////////////////////////////////////////////// -// Clipping -/////////////////////////////////////////////////////////////////////////////// - -void OpenGLRenderer::setScissorFromClip() { - Rect clip(mState.currentRenderTargetClip()); - clip.snapToPixelBoundaries(); - - if (mRenderState.scissor().set(clip.left, getViewportHeight() - clip.bottom, - clip.getWidth(), clip.getHeight())) { - mState.setDirtyClip(false); - } -} - -void OpenGLRenderer::ensureStencilBuffer() { - // Thanks to the mismatch between EGL and OpenGL ES FBO we - // cannot attach a stencil buffer to fbo0 dynamically. Let's - // just hope we have one when hasLayer() returns false. - if (hasLayer()) { - attachStencilBufferToLayer(currentSnapshot()->layer); - } -} - -void OpenGLRenderer::attachStencilBufferToLayer(Layer* layer) { - // The layer's FBO is already bound when we reach this stage - if (!layer->getStencilRenderBuffer()) { - RenderBuffer* buffer = mCaches.renderBufferCache.get( - Stencil::getLayerStencilFormat(), - layer->getWidth(), layer->getHeight()); - layer->setStencilRenderBuffer(buffer); - } -} - -static void handlePoint(std::vector<Vertex>& rectangleVertices, const Matrix4& transform, - float x, float y) { - Vertex v; - v.x = x; - v.y = y; - transform.mapPoint(v.x, v.y); - rectangleVertices.push_back(v); -} - -static void handlePointNoTransform(std::vector<Vertex>& rectangleVertices, float x, float y) { - Vertex v; - v.x = x; - v.y = y; - rectangleVertices.push_back(v); -} - -void OpenGLRenderer::drawRectangleList(const RectangleList& rectangleList) { - int quadCount = rectangleList.getTransformedRectanglesCount(); - std::vector<Vertex> rectangleVertices(quadCount * 4); - Rect scissorBox = rectangleList.calculateBounds(); - scissorBox.snapToPixelBoundaries(); - for (int i = 0; i < quadCount; ++i) { - const TransformedRectangle& tr(rectangleList.getTransformedRectangle(i)); - const Matrix4& transform = tr.getTransform(); - Rect bounds = tr.getBounds(); - if (transform.rectToRect()) { - transform.mapRect(bounds); - bounds.doIntersect(scissorBox); - if (!bounds.isEmpty()) { - handlePointNoTransform(rectangleVertices, bounds.left, bounds.top); - handlePointNoTransform(rectangleVertices, bounds.right, bounds.top); - handlePointNoTransform(rectangleVertices, bounds.left, bounds.bottom); - handlePointNoTransform(rectangleVertices, bounds.right, bounds.bottom); - } - } else { - handlePoint(rectangleVertices, transform, bounds.left, bounds.top); - handlePoint(rectangleVertices, transform, bounds.right, bounds.top); - handlePoint(rectangleVertices, transform, bounds.left, bounds.bottom); - handlePoint(rectangleVertices, transform, bounds.right, bounds.bottom); - } - } - - mRenderState.scissor().set(scissorBox.left, getViewportHeight() - scissorBox.bottom, - scissorBox.getWidth(), scissorBox.getHeight()); - const int transformFlags = TransformFlags::MeshIgnoresCanvasTransform; - Glop glop; - Vertex* vertices = &rectangleVertices[0]; - GlopBuilder(mRenderState, mCaches, &glop) - .setRoundRectClipState(currentSnapshot()->roundRectClipState) - .setMeshIndexedQuads(vertices, rectangleVertices.size() / 4) - .setFillBlack() - .setTransform(*currentSnapshot(), transformFlags) - .setModelViewOffsetRect(0, 0, scissorBox) - .build(); - renderGlop(glop); -} - -void OpenGLRenderer::setStencilFromClip() { - if (!Properties::debugOverdraw) { - if (!currentSnapshot()->clipIsSimple()) { - int incrementThreshold; - EVENT_LOGD("setStencilFromClip - enabling"); - - // NOTE: The order here is important, we must set dirtyClip to false - // before any draw call to avoid calling back into this method - mState.setDirtyClip(false); - - ensureStencilBuffer(); - - const ClipArea& clipArea = currentSnapshot()->getClipArea(); - - bool isRectangleList = clipArea.isRectangleList(); - if (isRectangleList) { - incrementThreshold = clipArea.getRectangleList().getTransformedRectanglesCount(); - } else { - incrementThreshold = 0; - } - - mRenderState.stencil().enableWrite(incrementThreshold); - - // Clean and update the stencil, but first make sure we restrict drawing - // to the region's bounds - bool resetScissor = mRenderState.scissor().setEnabled(true); - if (resetScissor) { - // The scissor was not set so we now need to update it - setScissorFromClip(); - } - - mRenderState.stencil().clear(); - - // stash and disable the outline clip state, since stencil doesn't account for outline - bool storedSkipOutlineClip = mSkipOutlineClip; - mSkipOutlineClip = true; - - SkPaint paint; - paint.setColor(SK_ColorBLACK); - paint.setXfermodeMode(SkXfermode::kSrc_Mode); - - if (isRectangleList) { - drawRectangleList(clipArea.getRectangleList()); - } else { - // NOTE: We could use the region contour path to generate a smaller mesh - // Since we are using the stencil we could use the red book path - // drawing technique. It might increase bandwidth usage though. - - // The last parameter is important: we are not drawing in the color buffer - // so we don't want to dirty the current layer, if any - drawRegionRects(clipArea.getClipRegion(), paint, false); - } - if (resetScissor) mRenderState.scissor().setEnabled(false); - mSkipOutlineClip = storedSkipOutlineClip; - - mRenderState.stencil().enableTest(incrementThreshold); - - // Draw the region used to generate the stencil if the appropriate debug - // mode is enabled - // TODO: Implement for rectangle list clip areas - if (Properties::debugStencilClip == StencilClipDebug::ShowRegion - && !clipArea.isRectangleList()) { - paint.setColor(0x7f0000ff); - paint.setXfermodeMode(SkXfermode::kSrcOver_Mode); - drawRegionRects(currentSnapshot()->getClipRegion(), paint); - } - } else { - EVENT_LOGD("setStencilFromClip - disabling"); - mRenderState.stencil().disable(); - } - } -} - -/** - * Returns false and sets scissor enable based upon bounds if drawing won't be clipped out. - * - * @param paint if not null, the bounds will be expanded to account for stroke depending on paint - * style, and tessellated AA ramp - */ -bool OpenGLRenderer::quickRejectSetupScissor(float left, float top, float right, float bottom, - const SkPaint* paint) { - bool snapOut = paint && paint->isAntiAlias(); - - if (paint && paint->getStyle() != SkPaint::kFill_Style) { - float outset = paint->getStrokeWidth() * 0.5f; - left -= outset; - top -= outset; - right += outset; - bottom += outset; - } - - bool clipRequired = false; - bool roundRectClipRequired = false; - if (mState.calculateQuickRejectForScissor(left, top, right, bottom, - &clipRequired, &roundRectClipRequired, snapOut)) { - return true; - } - - // not quick rejected, so enable the scissor if clipRequired - mRenderState.scissor().setEnabled(mScissorOptimizationDisabled || clipRequired); - mSkipOutlineClip = !roundRectClipRequired; - return false; -} - -void OpenGLRenderer::debugClip() { -#if DEBUG_CLIP_REGIONS - if (!currentSnapshot()->clipRegion->isEmpty()) { - SkPaint paint; - paint.setColor(0x7f00ff00); - drawRegionRects(*(currentSnapshot()->clipRegion, paint); - - } -#endif -} - -void OpenGLRenderer::renderGlop(const Glop& glop, GlopRenderType type) { - // TODO: It would be best if we could do this before quickRejectSetupScissor() - // changes the scissor test state - if (type != GlopRenderType::LayerClear) { - // Regular draws need to clear the dirty area on the layer before they start drawing on top - // of it. If this draw *is* a layer clear, it skips the clear step (since it would - // infinitely recurse) - clearLayerRegions(); - } - - if (mState.getDirtyClip()) { - if (mRenderState.scissor().isEnabled()) { - setScissorFromClip(); - } - - setStencilFromClip(); - } - mRenderState.render(glop, currentSnapshot()->getOrthoMatrix()); - if (type == GlopRenderType::Standard && !mRenderState.stencil().isWriteEnabled()) { - // TODO: specify more clearly when a draw should dirty the layer. - // is writing to the stencil the only time we should ignore this? -#if !HWUI_NEW_OPS - dirtyLayer(glop.bounds.left, glop.bounds.top, glop.bounds.right, glop.bounds.bottom); -#endif - mDirty = true; - } -} - -/////////////////////////////////////////////////////////////////////////////// -// Drawing -/////////////////////////////////////////////////////////////////////////////// - -void OpenGLRenderer::drawRenderNode(RenderNode* renderNode, Rect& dirty, int32_t replayFlags) { - // All the usual checks and setup operations (quickReject, setupDraw, etc.) - // will be performed by the display list itself - if (renderNode && renderNode->isRenderable()) { - // compute 3d ordering - renderNode->computeOrdering(); - if (CC_UNLIKELY(Properties::drawDeferDisabled)) { - startFrame(); - ReplayStateStruct replayStruct(*this, dirty, replayFlags); - renderNode->replay(replayStruct, 0); - return; - } - - DeferredDisplayList deferredList(mState.currentRenderTargetClip()); - DeferStateStruct deferStruct(deferredList, *this, replayFlags); - renderNode->defer(deferStruct, 0); - - flushLayers(); - startFrame(); - - deferredList.flush(*this, dirty); - } else { - // Even if there is no drawing command(Ex: invisible), - // it still needs startFrame to clear buffer and start tiling. - startFrame(); - } -} - -/** - * Important note: this method is intended to draw batches of bitmaps and - * will not set the scissor enable or dirty the current layer, if any. - * The caller is responsible for properly dirtying the current layer. - */ -void OpenGLRenderer::drawBitmaps(const SkBitmap* bitmap, AssetAtlas::Entry* entry, - int bitmapCount, TextureVertex* vertices, bool pureTranslate, - const Rect& bounds, const SkPaint* paint) { - Texture* texture = entry ? entry->texture : mCaches.textureCache.get(bitmap); - if (!texture) return; - - const AutoTexture autoCleanup(texture); - - // TODO: remove layer dirty in multi-draw callers - // TODO: snap doesn't need to touch transform, only texture filter. - bool snap = pureTranslate; - const float x = floorf(bounds.left + 0.5f); - const float y = floorf(bounds.top + 0.5f); - - const int textureFillFlags = (bitmap->colorType() == kAlpha_8_SkColorType) - ? TextureFillFlags::IsAlphaMaskTexture : TextureFillFlags::None; - const int transformFlags = TransformFlags::MeshIgnoresCanvasTransform; - Glop glop; - GlopBuilder(mRenderState, mCaches, &glop) - .setRoundRectClipState(currentSnapshot()->roundRectClipState) - .setMeshTexturedMesh(vertices, bitmapCount * 6) - .setFillTexturePaint(*texture, textureFillFlags, paint, currentSnapshot()->alpha) - .setTransform(*currentSnapshot(), transformFlags) - .setModelViewOffsetRectOptionalSnap(snap, x, y, Rect(bounds.getWidth(), bounds.getHeight())) - .build(); - renderGlop(glop, GlopRenderType::Multi); -} - -void OpenGLRenderer::drawBitmap(const SkBitmap* bitmap, const SkPaint* paint) { - if (quickRejectSetupScissor(0, 0, bitmap->width(), bitmap->height())) { - return; - } - - mCaches.textureState().activateTexture(0); - Texture* texture = getTexture(bitmap); - if (!texture) return; - const AutoTexture autoCleanup(texture); - - const int textureFillFlags = (bitmap->colorType() == kAlpha_8_SkColorType) - ? TextureFillFlags::IsAlphaMaskTexture : TextureFillFlags::None; - Glop glop; - GlopBuilder(mRenderState, mCaches, &glop) - .setRoundRectClipState(currentSnapshot()->roundRectClipState) - .setMeshTexturedUnitQuad(texture->uvMapper) - .setFillTexturePaint(*texture, textureFillFlags, paint, currentSnapshot()->alpha) - .setTransform(*currentSnapshot(), TransformFlags::None) - .setModelViewMapUnitToRectSnap(Rect(texture->width(), texture->height())) - .build(); - renderGlop(glop); -} - -void OpenGLRenderer::drawBitmapMesh(const SkBitmap* bitmap, int meshWidth, int meshHeight, - const float* vertices, const int* colors, const SkPaint* paint) { - if (!vertices || mState.currentlyIgnored()) { - return; - } - - float left = FLT_MAX; - float top = FLT_MAX; - float right = FLT_MIN; - float bottom = FLT_MIN; - - const uint32_t elementCount = meshWidth * meshHeight * 6; - - std::unique_ptr<ColorTextureVertex[]> mesh(new ColorTextureVertex[elementCount]); - ColorTextureVertex* vertex = &mesh[0]; - - std::unique_ptr<int[]> tempColors; - if (!colors) { - uint32_t colorsCount = (meshWidth + 1) * (meshHeight + 1); - tempColors.reset(new int[colorsCount]); - memset(tempColors.get(), 0xff, colorsCount * sizeof(int)); - colors = tempColors.get(); - } - - Texture* texture = mRenderState.assetAtlas().getEntryTexture(bitmap->pixelRef()); - const UvMapper& mapper(getMapper(texture)); - - for (int32_t y = 0; y < meshHeight; y++) { - for (int32_t x = 0; x < meshWidth; x++) { - uint32_t i = (y * (meshWidth + 1) + x) * 2; - - float u1 = float(x) / meshWidth; - float u2 = float(x + 1) / meshWidth; - float v1 = float(y) / meshHeight; - float v2 = float(y + 1) / meshHeight; - - mapper.map(u1, v1, u2, v2); - - int ax = i + (meshWidth + 1) * 2; - int ay = ax + 1; - int bx = i; - int by = bx + 1; - int cx = i + 2; - int cy = cx + 1; - int dx = i + (meshWidth + 1) * 2 + 2; - int dy = dx + 1; - - ColorTextureVertex::set(vertex++, vertices[dx], vertices[dy], u2, v2, colors[dx / 2]); - ColorTextureVertex::set(vertex++, vertices[ax], vertices[ay], u1, v2, colors[ax / 2]); - ColorTextureVertex::set(vertex++, vertices[bx], vertices[by], u1, v1, colors[bx / 2]); - - ColorTextureVertex::set(vertex++, vertices[dx], vertices[dy], u2, v2, colors[dx / 2]); - ColorTextureVertex::set(vertex++, vertices[bx], vertices[by], u1, v1, colors[bx / 2]); - ColorTextureVertex::set(vertex++, vertices[cx], vertices[cy], u2, v1, colors[cx / 2]); - - left = std::min(left, std::min(vertices[ax], std::min(vertices[bx], vertices[cx]))); - top = std::min(top, std::min(vertices[ay], std::min(vertices[by], vertices[cy]))); - right = std::max(right, std::max(vertices[ax], std::max(vertices[bx], vertices[cx]))); - bottom = std::max(bottom, std::max(vertices[ay], std::max(vertices[by], vertices[cy]))); - } - } - - if (quickRejectSetupScissor(left, top, right, bottom)) { - return; - } - - if (!texture) { - texture = mCaches.textureCache.get(bitmap); - if (!texture) { - return; - } - } - const AutoTexture autoCleanup(texture); - - /* - * TODO: handle alpha_8 textures correctly by applying paint color, but *not* - * shader in that case to mimic the behavior in SkiaCanvas::drawBitmapMesh. - */ - const int textureFillFlags = TextureFillFlags::None; - Glop glop; - GlopBuilder(mRenderState, mCaches, &glop) - .setRoundRectClipState(currentSnapshot()->roundRectClipState) - .setMeshColoredTexturedMesh(mesh.get(), elementCount) - .setFillTexturePaint(*texture, textureFillFlags, paint, currentSnapshot()->alpha) - .setTransform(*currentSnapshot(), TransformFlags::None) - .setModelViewOffsetRect(0, 0, Rect(left, top, right, bottom)) - .build(); - renderGlop(glop); -} - -void OpenGLRenderer::drawBitmap(const SkBitmap* bitmap, Rect src, Rect dst, const SkPaint* paint) { - if (quickRejectSetupScissor(dst)) { - return; - } - - Texture* texture = getTexture(bitmap); - if (!texture) return; - const AutoTexture autoCleanup(texture); - - Rect uv(std::max(0.0f, src.left / texture->width()), - std::max(0.0f, src.top / texture->height()), - std::min(1.0f, src.right / texture->width()), - std::min(1.0f, src.bottom / texture->height())); - - const int textureFillFlags = (bitmap->colorType() == kAlpha_8_SkColorType) - ? TextureFillFlags::IsAlphaMaskTexture : TextureFillFlags::None; - const bool tryToSnap = MathUtils::areEqual(src.getWidth(), dst.getWidth()) - && MathUtils::areEqual(src.getHeight(), dst.getHeight()); - Glop glop; - GlopBuilder(mRenderState, mCaches, &glop) - .setRoundRectClipState(currentSnapshot()->roundRectClipState) - .setMeshTexturedUvQuad(texture->uvMapper, uv) - .setFillTexturePaint(*texture, textureFillFlags, paint, currentSnapshot()->alpha) - .setTransform(*currentSnapshot(), TransformFlags::None) - .setModelViewMapUnitToRectOptionalSnap(tryToSnap, dst) - .build(); - renderGlop(glop); -} - -void OpenGLRenderer::drawPatch(const SkBitmap* bitmap, const Patch* mesh, - AssetAtlas::Entry* entry, float left, float top, float right, float bottom, - const SkPaint* paint) { - if (!mesh || !mesh->verticesCount || quickRejectSetupScissor(left, top, right, bottom)) { - return; - } - - Texture* texture = entry ? entry->texture : mCaches.textureCache.get(bitmap); - if (!texture) return; - const AutoTexture autoCleanup(texture); - - // 9 patches are built for stretching - always filter - int textureFillFlags = TextureFillFlags::ForceFilter; - if (bitmap->colorType() == kAlpha_8_SkColorType) { - textureFillFlags |= TextureFillFlags::IsAlphaMaskTexture; - } - Glop glop; - GlopBuilder(mRenderState, mCaches, &glop) - .setRoundRectClipState(currentSnapshot()->roundRectClipState) - .setMeshPatchQuads(*mesh) - .setFillTexturePaint(*texture, textureFillFlags, paint, currentSnapshot()->alpha) - .setTransform(*currentSnapshot(), TransformFlags::None) - .setModelViewOffsetRectSnap(left, top, Rect(right - left, bottom - top)) // TODO: get minimal bounds from patch - .build(); - renderGlop(glop); -} - -/** - * Important note: this method is intended to draw batches of 9-patch objects and - * will not set the scissor enable or dirty the current layer, if any. - * The caller is responsible for properly dirtying the current layer. - */ -void OpenGLRenderer::drawPatches(const SkBitmap* bitmap, AssetAtlas::Entry* entry, - TextureVertex* vertices, uint32_t elementCount, const SkPaint* paint) { - mCaches.textureState().activateTexture(0); - Texture* texture = entry ? entry->texture : mCaches.textureCache.get(bitmap); - if (!texture) return; - const AutoTexture autoCleanup(texture); - - // TODO: get correct bounds from caller - const int transformFlags = TransformFlags::MeshIgnoresCanvasTransform; - // 9 patches are built for stretching - always filter - int textureFillFlags = TextureFillFlags::ForceFilter; - if (bitmap->colorType() == kAlpha_8_SkColorType) { - textureFillFlags |= TextureFillFlags::IsAlphaMaskTexture; - } - Glop glop; - GlopBuilder(mRenderState, mCaches, &glop) - .setRoundRectClipState(currentSnapshot()->roundRectClipState) - .setMeshTexturedIndexedQuads(vertices, elementCount) - .setFillTexturePaint(*texture, textureFillFlags, paint, currentSnapshot()->alpha) - .setTransform(*currentSnapshot(), transformFlags) - .setModelViewOffsetRect(0, 0, Rect()) - .build(); - renderGlop(glop, GlopRenderType::Multi); -} - -void OpenGLRenderer::drawVertexBuffer(float translateX, float translateY, - const VertexBuffer& vertexBuffer, const SkPaint* paint, int displayFlags) { - // not missing call to quickReject/dirtyLayer, always done at a higher level - if (!vertexBuffer.getVertexCount()) { - // no vertices to draw - return; - } - - bool shadowInterp = displayFlags & kVertexBuffer_ShadowInterp; - const int transformFlags = TransformFlags::OffsetByFudgeFactor; - Glop glop; - GlopBuilder(mRenderState, mCaches, &glop) - .setRoundRectClipState(currentSnapshot()->roundRectClipState) - .setMeshVertexBuffer(vertexBuffer) - .setFillPaint(*paint, currentSnapshot()->alpha, shadowInterp) - .setTransform(*currentSnapshot(), transformFlags) - .setModelViewOffsetRect(translateX, translateY, vertexBuffer.getBounds()) - .build(); - renderGlop(glop); -} - -/** - * Renders a convex path via tessellation. For AA paths, this function uses a similar approach to - * that of AA lines in the drawLines() function. We expand the convex path by a half pixel in - * screen space in all directions. However, instead of using a fragment shader to compute the - * translucency of the color from its position, we simply use a varying parameter to define how far - * a given pixel is from the edge. For non-AA paths, the expansion and alpha varying are not used. - * - * Doesn't yet support joins, caps, or path effects. - */ -void OpenGLRenderer::drawConvexPath(const SkPath& path, const SkPaint* paint) { - VertexBuffer vertexBuffer; - // TODO: try clipping large paths to viewport - - PathTessellator::tessellatePath(path, paint, *currentTransform(), vertexBuffer); - drawVertexBuffer(vertexBuffer, paint); -} - -/** - * We create tristrips for the lines much like shape stroke tessellation, using a per-vertex alpha - * and additional geometry for defining an alpha slope perimeter. - * - * Using GL_LINES can be difficult because the rasterization rules for those lines produces some - * unexpected results, and may vary between hardware devices. Previously we used a varying-base - * in-shader alpha region, but found it to be taxing on some GPUs. - * - * TODO: try using a fixed input buffer for non-capped lines as in text rendering. this may reduce - * memory transfer by removing need for degenerate vertices. - */ -void OpenGLRenderer::drawLines(const float* points, int count, const SkPaint* paint) { - if (mState.currentlyIgnored() || count < 4) return; - - count &= ~0x3; // round down to nearest four - - VertexBuffer buffer; - PathTessellator::tessellateLines(points, count, paint, *currentTransform(), buffer); - const Rect& bounds = buffer.getBounds(); - - if (quickRejectSetupScissor(bounds.left, bounds.top, bounds.right, bounds.bottom)) { - return; - } - - int displayFlags = paint->isAntiAlias() ? 0 : kVertexBuffer_Offset; - drawVertexBuffer(buffer, paint, displayFlags); -} - -void OpenGLRenderer::drawPoints(const float* points, int count, const SkPaint* paint) { - if (mState.currentlyIgnored() || count < 2) return; - - count &= ~0x1; // round down to nearest two - - VertexBuffer buffer; - PathTessellator::tessellatePoints(points, count, paint, *currentTransform(), buffer); - - const Rect& bounds = buffer.getBounds(); - if (quickRejectSetupScissor(bounds.left, bounds.top, bounds.right, bounds.bottom)) { - return; - } - - int displayFlags = paint->isAntiAlias() ? 0 : kVertexBuffer_Offset; - drawVertexBuffer(buffer, paint, displayFlags); - - mDirty = true; -} - -void OpenGLRenderer::drawColor(int color, SkXfermode::Mode mode) { - // No need to check against the clip, we fill the clip region - if (mState.currentlyIgnored()) return; - - Rect clip(mState.currentRenderTargetClip()); - clip.snapToPixelBoundaries(); - - SkPaint paint; - paint.setColor(color); - paint.setXfermodeMode(mode); - - drawColorRect(clip.left, clip.top, clip.right, clip.bottom, &paint, true); - - mDirty = true; -} - -void OpenGLRenderer::drawShape(float left, float top, PathTexture* texture, - const SkPaint* paint) { - if (!texture) return; - const AutoTexture autoCleanup(texture); - - const float x = left + texture->left - texture->offset; - const float y = top + texture->top - texture->offset; - - drawPathTexture(texture, x, y, paint); - - mDirty = true; -} - -void OpenGLRenderer::drawRoundRect(float left, float top, float right, float bottom, - float rx, float ry, const SkPaint* p) { - if (mState.currentlyIgnored() - || quickRejectSetupScissor(left, top, right, bottom, p) - || PaintUtils::paintWillNotDraw(*p)) { - return; - } - - if (p->getPathEffect() != nullptr) { - mCaches.textureState().activateTexture(0); - PathTexture* texture = mCaches.pathCache.getRoundRect( - right - left, bottom - top, rx, ry, p); - drawShape(left, top, texture, p); - } else { - const VertexBuffer* vertexBuffer = mCaches.tessellationCache.getRoundRect( - *currentTransform(), *p, right - left, bottom - top, rx, ry); - drawVertexBuffer(left, top, *vertexBuffer, p); - } -} - -void OpenGLRenderer::drawCircle(float x, float y, float radius, const SkPaint* p) { - if (mState.currentlyIgnored() - || quickRejectSetupScissor(x - radius, y - radius, x + radius, y + radius, p) - || PaintUtils::paintWillNotDraw(*p)) { - return; - } - - if (p->getPathEffect() != nullptr) { - mCaches.textureState().activateTexture(0); - PathTexture* texture = mCaches.pathCache.getCircle(radius, p); - drawShape(x - radius, y - radius, texture, p); - return; - } - - SkPath path; - if (p->getStyle() == SkPaint::kStrokeAndFill_Style) { - path.addCircle(x, y, radius + p->getStrokeWidth() / 2); - } else { - path.addCircle(x, y, radius); - } - -#if !HWUI_NEW_OPS - if (CC_UNLIKELY(currentSnapshot()->projectionPathMask != nullptr)) { - // mask ripples with projection mask - SkPath maskPath = *(currentSnapshot()->projectionPathMask->projectionMask); - - Matrix4 screenSpaceTransform; - currentSnapshot()->buildScreenSpaceTransform(&screenSpaceTransform); - - Matrix4 totalTransform; - totalTransform.loadInverse(screenSpaceTransform); - totalTransform.multiply(currentSnapshot()->projectionPathMask->projectionMaskTransform); - - SkMatrix skTotalTransform; - totalTransform.copyTo(skTotalTransform); - maskPath.transform(skTotalTransform); - - // Mask the ripple path by the projection mask, now that it's - // in local space. Note that this can create CCW paths. - Op(path, maskPath, kIntersect_SkPathOp, &path); - } -#endif - drawConvexPath(path, p); -} - -void OpenGLRenderer::drawOval(float left, float top, float right, float bottom, - const SkPaint* p) { - if (mState.currentlyIgnored() - || quickRejectSetupScissor(left, top, right, bottom, p) - || PaintUtils::paintWillNotDraw(*p)) { - return; - } - - if (p->getPathEffect() != nullptr) { - mCaches.textureState().activateTexture(0); - PathTexture* texture = mCaches.pathCache.getOval(right - left, bottom - top, p); - drawShape(left, top, texture, p); - } else { - SkPath path; - SkRect rect = SkRect::MakeLTRB(left, top, right, bottom); - if (p->getStyle() == SkPaint::kStrokeAndFill_Style) { - rect.outset(p->getStrokeWidth() / 2, p->getStrokeWidth() / 2); - } - path.addOval(rect); - drawConvexPath(path, p); - } -} - -void OpenGLRenderer::drawArc(float left, float top, float right, float bottom, - float startAngle, float sweepAngle, bool useCenter, const SkPaint* p) { - if (mState.currentlyIgnored() - || quickRejectSetupScissor(left, top, right, bottom, p) - || PaintUtils::paintWillNotDraw(*p)) { - return; - } - - // TODO: support fills (accounting for concavity if useCenter && sweepAngle > 180) - if (p->getStyle() != SkPaint::kStroke_Style || p->getPathEffect() != nullptr || useCenter) { - mCaches.textureState().activateTexture(0); - PathTexture* texture = mCaches.pathCache.getArc(right - left, bottom - top, - startAngle, sweepAngle, useCenter, p); - drawShape(left, top, texture, p); - return; - } - SkRect rect = SkRect::MakeLTRB(left, top, right, bottom); - if (p->getStyle() == SkPaint::kStrokeAndFill_Style) { - rect.outset(p->getStrokeWidth() / 2, p->getStrokeWidth() / 2); - } - - SkPath path; - if (useCenter) { - path.moveTo(rect.centerX(), rect.centerY()); - } - path.arcTo(rect, startAngle, sweepAngle, !useCenter); - if (useCenter) { - path.close(); - } - drawConvexPath(path, p); -} - -void OpenGLRenderer::drawRect(float left, float top, float right, float bottom, - const SkPaint* p) { - if (mState.currentlyIgnored() - || quickRejectSetupScissor(left, top, right, bottom, p) - || PaintUtils::paintWillNotDraw(*p)) { - return; - } - - if (p->getStyle() != SkPaint::kFill_Style) { - // only fill style is supported by drawConvexPath, since others have to handle joins - static_assert(SkPaintDefaults_MiterLimit == 4.0f, "Miter limit has changed"); - if (p->getPathEffect() != nullptr || p->getStrokeJoin() != SkPaint::kMiter_Join || - p->getStrokeMiter() != SkPaintDefaults_MiterLimit) { - mCaches.textureState().activateTexture(0); - PathTexture* texture = - mCaches.pathCache.getRect(right - left, bottom - top, p); - drawShape(left, top, texture, p); - } else { - SkPath path; - SkRect rect = SkRect::MakeLTRB(left, top, right, bottom); - if (p->getStyle() == SkPaint::kStrokeAndFill_Style) { - rect.outset(p->getStrokeWidth() / 2, p->getStrokeWidth() / 2); - } - path.addRect(rect); - drawConvexPath(path, p); - } - } else { - if (p->isAntiAlias() && !currentTransform()->isSimple()) { - SkPath path; - path.addRect(left, top, right, bottom); - drawConvexPath(path, p); - } else { - drawColorRect(left, top, right, bottom, p); - - mDirty = true; - } - } -} - -void OpenGLRenderer::drawTextShadow(const SkPaint* paint, const glyph_t* glyphs, - int count, const float* positions, - FontRenderer& fontRenderer, int alpha, float x, float y) { - mCaches.textureState().activateTexture(0); - - PaintUtils::TextShadow textShadow; - if (!PaintUtils::getTextShadow(paint, &textShadow)) { - LOG_ALWAYS_FATAL("failed to query shadow attributes"); - } - - // NOTE: The drop shadow will not perform gamma correction - // if shader-based correction is enabled - mCaches.dropShadowCache.setFontRenderer(fontRenderer); - ShadowTexture* texture = mCaches.dropShadowCache.get( - paint, glyphs, count, textShadow.radius, positions); - // If the drop shadow exceeds the max texture size or couldn't be - // allocated, skip drawing - if (!texture) return; - const AutoTexture autoCleanup(texture); - - const float sx = x - texture->left + textShadow.dx; - const float sy = y - texture->top + textShadow.dy; - - Glop glop; - GlopBuilder(mRenderState, mCaches, &glop) - .setRoundRectClipState(currentSnapshot()->roundRectClipState) - .setMeshTexturedUnitQuad(nullptr) - .setFillShadowTexturePaint(*texture, textShadow.color, *paint, currentSnapshot()->alpha) - .setTransform(*currentSnapshot(), TransformFlags::None) - .setModelViewMapUnitToRect(Rect(sx, sy, sx + texture->width(), sy + texture->height())) - .build(); - renderGlop(glop); -} - -// TODO: remove this, once mState.currentlyIgnored captures snapshot alpha -bool OpenGLRenderer::canSkipText(const SkPaint* paint) const { - float alpha = (PaintUtils::hasTextShadow(paint) - ? 1.0f : paint->getAlpha()) * currentSnapshot()->alpha; - return MathUtils::isZero(alpha) - && PaintUtils::getXfermode(paint->getXfermode()) == SkXfermode::kSrcOver_Mode; -} - -bool OpenGLRenderer::findBestFontTransform(const mat4& transform, SkMatrix* outMatrix) const { - if (CC_LIKELY(transform.isPureTranslate())) { - outMatrix->setIdentity(); - return false; - } else if (CC_UNLIKELY(transform.isPerspective())) { - outMatrix->setIdentity(); - return true; - } - - /** - * Input is a non-perspective, scaling transform. Generate a scale-only transform, - * with values rounded to the nearest int. - */ - float sx, sy; - transform.decomposeScale(sx, sy); - outMatrix->setScale( - roundf(std::max(1.0f, sx)), - roundf(std::max(1.0f, sy))); - return true; -} - -int OpenGLRenderer::getSaveCount() const { - return mState.getSaveCount(); -} - -int OpenGLRenderer::save(int flags) { - return mState.save(flags); -} - -void OpenGLRenderer::restore() { - mState.restore(); -} - -void OpenGLRenderer::restoreToCount(int saveCount) { - mState.restoreToCount(saveCount); -} - - -void OpenGLRenderer::translate(float dx, float dy, float dz) { - mState.translate(dx, dy, dz); -} - -void OpenGLRenderer::rotate(float degrees) { - mState.rotate(degrees); -} - -void OpenGLRenderer::scale(float sx, float sy) { - mState.scale(sx, sy); -} - -void OpenGLRenderer::skew(float sx, float sy) { - mState.skew(sx, sy); -} - -void OpenGLRenderer::setLocalMatrix(const Matrix4& matrix) { - mState.setMatrix(mBaseTransform); - mState.concatMatrix(matrix); -} - -void OpenGLRenderer::setLocalMatrix(const SkMatrix& matrix) { - mState.setMatrix(mBaseTransform); - mState.concatMatrix(matrix); -} - -void OpenGLRenderer::concatMatrix(const Matrix4& matrix) { - mState.concatMatrix(matrix); -} - -bool OpenGLRenderer::clipRect(float left, float top, float right, float bottom, SkRegion::Op op) { - return mState.clipRect(left, top, right, bottom, op); -} - -bool OpenGLRenderer::clipPath(const SkPath* path, SkRegion::Op op) { - return mState.clipPath(path, op); -} - -bool OpenGLRenderer::clipRegion(const SkRegion* region, SkRegion::Op op) { - return mState.clipRegion(region, op); -} - -void OpenGLRenderer::setClippingOutline(LinearAllocator& allocator, const Outline* outline) { - mState.setClippingOutline(allocator, outline); -} - -void OpenGLRenderer::setClippingRoundRect(LinearAllocator& allocator, - const Rect& rect, float radius, bool highPriority) { - mState.setClippingRoundRect(allocator, rect, radius, highPriority); -} - -void OpenGLRenderer::setProjectionPathMask(LinearAllocator& allocator, const SkPath* path) { - mState.setProjectionPathMask(allocator, path); -} - -void OpenGLRenderer::drawText(const glyph_t* glyphs, int bytesCount, int count, float x, float y, - const float* positions, const SkPaint* paint, float totalAdvance, const Rect& bounds, - DrawOpMode drawOpMode) { - - if (drawOpMode == DrawOpMode::kImmediate) { - // The checks for corner-case ignorable text and quick rejection is only done for immediate - // drawing as ops from DeferredDisplayList are already filtered for these - if (glyphs == nullptr || count == 0 || mState.currentlyIgnored() || canSkipText(paint) || - quickRejectSetupScissor(bounds)) { - return; - } - } - - const float oldX = x; - const float oldY = y; - - const mat4& transform = *currentTransform(); - const bool pureTranslate = transform.isPureTranslate(); - - if (CC_LIKELY(pureTranslate)) { - x = floorf(x + transform.getTranslateX() + 0.5f); - y = floorf(y + transform.getTranslateY() + 0.5f); - } - - int alpha = PaintUtils::getAlphaDirect(paint) * currentSnapshot()->alpha; - SkXfermode::Mode mode = PaintUtils::getXfermodeDirect(paint); - - FontRenderer& fontRenderer = mCaches.fontRenderer.getFontRenderer(); - - if (CC_UNLIKELY(PaintUtils::hasTextShadow(paint))) { - fontRenderer.setFont(paint, SkMatrix::I()); - drawTextShadow(paint, glyphs, count, positions, fontRenderer, - alpha, oldX, oldY); - } - - const bool hasActiveLayer = hasLayer(); - - // We only pass a partial transform to the font renderer. That partial - // matrix defines how glyphs are rasterized. Typically we want glyphs - // to be rasterized at their final size on screen, which means the partial - // matrix needs to take the scale factor into account. - // When a partial matrix is used to transform glyphs during rasterization, - // the mesh is generated with the inverse transform (in the case of scale, - // the mesh is generated at 1.0 / scale for instance.) This allows us to - // apply the full transform matrix at draw time in the vertex shader. - // Applying the full matrix in the shader is the easiest way to handle - // rotation and perspective and allows us to always generated quads in the - // font renderer which greatly simplifies the code, clipping in particular. - SkMatrix fontTransform; - bool linearFilter = findBestFontTransform(transform, &fontTransform) - || fabs(y - (int) y) > 0.0f - || fabs(x - (int) x) > 0.0f; - fontRenderer.setFont(paint, fontTransform); - fontRenderer.setTextureFiltering(linearFilter); - - // TODO: Implement better clipping for scaled/rotated text - const Rect* clip = !pureTranslate ? nullptr : &mState.currentRenderTargetClip(); - Rect layerBounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f); - - bool status; -#if HWUI_NEW_OPS - LOG_ALWAYS_FATAL("unsupported"); - TextDrawFunctor functor(nullptr, nullptr, nullptr, x, y, pureTranslate, alpha, mode, paint); -#else - TextDrawFunctor functor(this, x, y, pureTranslate, alpha, mode, paint); -#endif - - // don't call issuedrawcommand, do it at end of batch - bool forceFinish = (drawOpMode != DrawOpMode::kDefer); - if (CC_UNLIKELY(paint->getTextAlign() != SkPaint::kLeft_Align)) { - SkPaint paintCopy(*paint); - paintCopy.setTextAlign(SkPaint::kLeft_Align); - status = fontRenderer.renderPosText(&paintCopy, clip, glyphs, count, x, y, - positions, hasActiveLayer ? &layerBounds : nullptr, &functor, forceFinish); - } else { - status = fontRenderer.renderPosText(paint, clip, glyphs, count, x, y, - positions, hasActiveLayer ? &layerBounds : nullptr, &functor, forceFinish); - } - - if ((status || drawOpMode != DrawOpMode::kImmediate) && hasActiveLayer) { - if (!pureTranslate) { - transform.mapRect(layerBounds); - } - dirtyLayerUnchecked(layerBounds, getRegion()); - } - - mDirty = true; -} - -void OpenGLRenderer::drawTextOnPath(const glyph_t* glyphs, int bytesCount, int count, - const SkPath* path, float hOffset, float vOffset, const SkPaint* paint) { - if (glyphs == nullptr || count == 0 || mState.currentlyIgnored() || canSkipText(paint)) { - return; - } - - // TODO: avoid scissor by calculating maximum bounds using path bounds + font metrics - mRenderState.scissor().setEnabled(true); - - FontRenderer& fontRenderer = mCaches.fontRenderer.getFontRenderer(); - fontRenderer.setFont(paint, SkMatrix::I()); - fontRenderer.setTextureFiltering(true); - - int alpha = PaintUtils::getAlphaDirect(paint) * currentSnapshot()->alpha; - SkXfermode::Mode mode = PaintUtils::getXfermodeDirect(paint); -#if HWUI_NEW_OPS - LOG_ALWAYS_FATAL("unsupported"); - TextDrawFunctor functor(nullptr, nullptr, nullptr, 0.0f, 0.0f, false, alpha, mode, paint); -#else - TextDrawFunctor functor(this, 0.0f, 0.0f, false, alpha, mode, paint); -#endif - - const Rect* clip = &writableSnapshot()->getLocalClip(); - Rect bounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f); - - if (fontRenderer.renderTextOnPath(paint, clip, glyphs, count, path, - hOffset, vOffset, hasLayer() ? &bounds : nullptr, &functor)) { - dirtyLayer(bounds.left, bounds.top, bounds.right, bounds.bottom, *currentTransform()); - mDirty = true; - } -} - -void OpenGLRenderer::drawPath(const SkPath* path, const SkPaint* paint) { - if (mState.currentlyIgnored()) return; - - mCaches.textureState().activateTexture(0); - - PathTexture* texture = mCaches.pathCache.get(path, paint); - if (!texture) return; - - const float x = texture->left - texture->offset; - const float y = texture->top - texture->offset; - - drawPathTexture(texture, x, y, paint); - - if (texture->cleanup) { - mCaches.pathCache.remove(path, paint); - } - mDirty = true; -} - -void OpenGLRenderer::drawLayer(Layer* layer) { - if (!layer) { - return; - } - - mat4* transform = nullptr; - if (layer->isTextureLayer()) { - transform = &layer->getTransform(); - if (!transform->isIdentity()) { - save(SaveFlags::Matrix); - concatMatrix(*transform); - } - } - - bool clipRequired = false; - const bool rejected = mState.calculateQuickRejectForScissor( - 0, 0, layer->layer.getWidth(), layer->layer.getHeight(), - &clipRequired, nullptr, false); - - if (rejected) { - if (transform && !transform->isIdentity()) { - restore(); - } - return; - } - - EVENT_LOGD("drawLayer," RECT_STRING ", clipRequired %d", x, y, - x + layer->layer.getWidth(), y + layer->layer.getHeight(), clipRequired); - - updateLayer(layer, true); - - mRenderState.scissor().setEnabled(mScissorOptimizationDisabled || clipRequired); - mCaches.textureState().activateTexture(0); - - if (CC_LIKELY(!layer->region.isEmpty())) { - if (layer->region.isRect()) { - DRAW_DOUBLE_STENCIL_IF(!layer->hasDrawnSinceUpdate, - composeLayerRect(layer, layer->regionRect)); - } else if (layer->mesh) { - Glop glop; - GlopBuilder(mRenderState, mCaches, &glop) - .setRoundRectClipState(currentSnapshot()->roundRectClipState) - .setMeshTexturedIndexedQuads(layer->mesh, layer->meshElementCount) - .setFillLayer(layer->getTexture(), layer->getColorFilter(), getLayerAlpha(layer), layer->getMode(), Blend::ModeOrderSwap::NoSwap) - .setTransform(*currentSnapshot(), TransformFlags::None) - .setModelViewOffsetRectSnap(0, 0, Rect(layer->layer.getWidth(), layer->layer.getHeight())) - .build(); - DRAW_DOUBLE_STENCIL_IF(!layer->hasDrawnSinceUpdate, renderGlop(glop)); -#if DEBUG_LAYERS_AS_REGIONS - drawRegionRectsDebug(layer->region); -#endif - } - - if (layer->debugDrawUpdate) { - layer->debugDrawUpdate = false; - - SkPaint paint; - paint.setColor(0x7f00ff00); - drawColorRect(0, 0, layer->layer.getWidth(), layer->layer.getHeight(), &paint); - } - } - layer->hasDrawnSinceUpdate = true; - - if (transform && !transform->isIdentity()) { - restore(); - } - - mDirty = true; -} - -/////////////////////////////////////////////////////////////////////////////// -// Draw filters -/////////////////////////////////////////////////////////////////////////////// -void OpenGLRenderer::setDrawFilter(SkDrawFilter* filter) { - // We should never get here since we apply the draw filter when stashing - // the paints in the DisplayList. - LOG_ALWAYS_FATAL("OpenGLRenderer does not directly support DrawFilters"); -} - -/////////////////////////////////////////////////////////////////////////////// -// Drawing implementation -/////////////////////////////////////////////////////////////////////////////// - -Texture* OpenGLRenderer::getTexture(const SkBitmap* bitmap) { - Texture* texture = mRenderState.assetAtlas().getEntryTexture(bitmap->pixelRef()); - if (!texture) { - return mCaches.textureCache.get(bitmap); - } - return texture; -} - -void OpenGLRenderer::drawPathTexture(PathTexture* texture, float x, float y, - const SkPaint* paint) { - if (quickRejectSetupScissor(x, y, x + texture->width(), y + texture->height())) { - return; - } - - Glop glop; - GlopBuilder(mRenderState, mCaches, &glop) - .setRoundRectClipState(currentSnapshot()->roundRectClipState) - .setMeshTexturedUnitQuad(nullptr) - .setFillPathTexturePaint(*texture, *paint, currentSnapshot()->alpha) - .setTransform(*currentSnapshot(), TransformFlags::None) - .setModelViewMapUnitToRect(Rect(x, y, x + texture->width(), y + texture->height())) - .build(); - renderGlop(glop); -} - -void OpenGLRenderer::drawRects(const float* rects, int count, const SkPaint* paint) { - if (mState.currentlyIgnored()) { - return; - } - - drawColorRects(rects, count, paint, false, true, true); -} - -void OpenGLRenderer::drawShadow(float casterAlpha, - const VertexBuffer* ambientShadowVertexBuffer, const VertexBuffer* spotShadowVertexBuffer) { - if (mState.currentlyIgnored()) return; - - // TODO: use quickRejectWithScissor. For now, always force enable scissor. - mRenderState.scissor().setEnabled(true); - - SkPaint paint; - paint.setAntiAlias(true); // want to use AlphaVertex - - // The caller has made sure casterAlpha > 0. - float ambientShadowAlpha = mAmbientShadowAlpha; - if (CC_UNLIKELY(Properties::overrideAmbientShadowStrength >= 0)) { - ambientShadowAlpha = Properties::overrideAmbientShadowStrength; - } - if (ambientShadowVertexBuffer && ambientShadowAlpha > 0) { - paint.setARGB(casterAlpha * ambientShadowAlpha, 0, 0, 0); - drawVertexBuffer(*ambientShadowVertexBuffer, &paint, kVertexBuffer_ShadowInterp); - } - - float spotShadowAlpha = mSpotShadowAlpha; - if (CC_UNLIKELY(Properties::overrideSpotShadowStrength >= 0)) { - spotShadowAlpha = Properties::overrideSpotShadowStrength; - } - if (spotShadowVertexBuffer && spotShadowAlpha > 0) { - paint.setARGB(casterAlpha * spotShadowAlpha, 0, 0, 0); - drawVertexBuffer(*spotShadowVertexBuffer, &paint, kVertexBuffer_ShadowInterp); - } - - mDirty=true; -} - -void OpenGLRenderer::drawColorRects(const float* rects, int count, const SkPaint* paint, - bool ignoreTransform, bool dirty, bool clip) { - if (count == 0) { - return; - } - - float left = FLT_MAX; - float top = FLT_MAX; - float right = FLT_MIN; - float bottom = FLT_MIN; - - Vertex mesh[count]; - Vertex* vertex = mesh; - - for (int index = 0; index < count; index += 4) { - float l = rects[index + 0]; - float t = rects[index + 1]; - float r = rects[index + 2]; - float b = rects[index + 3]; - - Vertex::set(vertex++, l, t); - Vertex::set(vertex++, r, t); - Vertex::set(vertex++, l, b); - Vertex::set(vertex++, r, b); - - left = std::min(left, l); - top = std::min(top, t); - right = std::max(right, r); - bottom = std::max(bottom, b); - } - - if (clip && quickRejectSetupScissor(left, top, right, bottom)) { - return; - } - - const int transformFlags = ignoreTransform - ? TransformFlags::MeshIgnoresCanvasTransform : TransformFlags::None; - Glop glop; - GlopBuilder(mRenderState, mCaches, &glop) - .setRoundRectClipState(currentSnapshot()->roundRectClipState) - .setMeshIndexedQuads(&mesh[0], count / 4) - .setFillPaint(*paint, currentSnapshot()->alpha) - .setTransform(*currentSnapshot(), transformFlags) - .setModelViewOffsetRect(0, 0, Rect(left, top, right, bottom)) - .build(); - renderGlop(glop); -} - -void OpenGLRenderer::drawColorRect(float left, float top, float right, float bottom, - const SkPaint* paint, bool ignoreTransform) { - const int transformFlags = ignoreTransform - ? TransformFlags::MeshIgnoresCanvasTransform : TransformFlags::None; - Glop glop; - GlopBuilder(mRenderState, mCaches, &glop) - .setRoundRectClipState(currentSnapshot()->roundRectClipState) - .setMeshUnitQuad() - .setFillPaint(*paint, currentSnapshot()->alpha) - .setTransform(*currentSnapshot(), transformFlags) - .setModelViewMapUnitToRect(Rect(left, top, right, bottom)) - .build(); - renderGlop(glop); -} - -float OpenGLRenderer::getLayerAlpha(const Layer* layer) const { - return (layer->getAlpha() / 255.0f) * currentSnapshot()->alpha; -} - -}; // namespace uirenderer -}; // namespace android diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h deleted file mode 100644 index ec450bd63296..000000000000 --- a/libs/hwui/OpenGLRenderer.h +++ /dev/null @@ -1,785 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ANDROID_HWUI_OPENGL_RENDERER_H -#define ANDROID_HWUI_OPENGL_RENDERER_H - -#include "CanvasState.h" -#include "Debug.h" -#include "Extensions.h" -#include "Matrix.h" -#include "Program.h" -#include "Rect.h" -#include "Snapshot.h" -#include "UvMapper.h" -#include "Vertex.h" -#include "Caches.h" -#include "utils/PaintUtils.h" - -#include <GLES2/gl2.h> -#include <GLES2/gl2ext.h> - -#include <SkBitmap.h> -#include <SkCanvas.h> -#include <SkColorFilter.h> -#include <SkDrawLooper.h> -#include <SkMatrix.h> -#include <SkPaint.h> -#include <SkRegion.h> -#include <SkXfermode.h> - -#include <utils/Blur.h> -#include <utils/Functor.h> -#include <utils/RefBase.h> -#include <utils/SortedVector.h> - -#include <cutils/compiler.h> - -#include <androidfw/ResourceTypes.h> - -#include <vector> - -class SkShader; - -namespace android { -namespace uirenderer { - -enum class DrawOpMode { - kImmediate, - kDefer, - kFlush -}; - -class DeferredDisplayState; -struct Glop; -class RenderState; -class RenderNode; -class TextDrawFunctor; -class VertexBuffer; - -enum StateDeferFlags { - kStateDeferFlag_Draw = 0x1, - kStateDeferFlag_Clip = 0x2 -}; - -enum ClipSideFlags { - kClipSide_None = 0x0, - kClipSide_Left = 0x1, - kClipSide_Top = 0x2, - kClipSide_Right = 0x4, - kClipSide_Bottom = 0x8, - kClipSide_Full = 0xF, - kClipSide_ConservativeFull = 0x1F -}; - -enum VertexBufferDisplayFlags { - kVertexBuffer_Offset = 0x1, - kVertexBuffer_ShadowInterp = 0x2, -}; - -/** - * Defines additional transformation that should be applied by the model view matrix, beyond that of - * the currentTransform() - */ -enum ModelViewMode { - /** - * Used when the model view should simply translate geometry passed to the shader. The resulting - * matrix will be a simple translation. - */ - kModelViewMode_Translate = 0, - - /** - * Used when the model view should translate and scale geometry. The resulting matrix will be a - * translation + scale. This is frequently used together with VBO 0, the (0,0,1,1) rect. - */ - kModelViewMode_TranslateAndScale = 1, -}; - -/////////////////////////////////////////////////////////////////////////////// -// Renderer -/////////////////////////////////////////////////////////////////////////////// -/** - * OpenGL Renderer implementation. - */ -class OpenGLRenderer : public CanvasStateClient { -public: - explicit OpenGLRenderer(RenderState& renderState); - virtual ~OpenGLRenderer(); - - void initProperties(); - void initLight(float lightRadius, uint8_t ambientShadowAlpha, - uint8_t spotShadowAlpha); - void setLightCenter(const Vector3& lightCenter); - - /* - * Prepares the renderer to draw a frame. This method must be invoked - * at the beginning of each frame. Only the specified rectangle of the - * frame is assumed to be dirty. A clip will automatically be set to - * the specified rectangle. - * - * @param opaque If true, the target surface is considered opaque - * and will not be cleared. If false, the target surface - * will be cleared - */ - virtual void prepareDirty(int viewportWidth, int viewportHeight, - float left, float top, float right, float bottom, bool opaque); - - /** - * Indicates the end of a frame. This method must be invoked whenever - * the caller is done rendering a frame. - * Returns true if any drawing was done during the frame (the output - * has changed / is "dirty" and should be displayed to the user). - */ - virtual bool finish(); - - void callDrawGLFunction(Functor* functor, Rect& dirty); - - void pushLayerUpdate(Layer* layer); - void cancelLayerUpdate(Layer* layer); - void flushLayerUpdates(); - void markLayersAsBuildLayers(); - - virtual int saveLayer(float left, float top, float right, float bottom, - const SkPaint* paint, int flags) { - return saveLayer(left, top, right, bottom, paint, flags, nullptr); - } - - // Specialized saveLayer implementation, which will pass the convexMask to an FBO layer, if - // created, which will in turn clip to that mask when drawn back/restored. - int saveLayer(float left, float top, float right, float bottom, - const SkPaint* paint, int flags, const SkPath* convexMask); - - int saveLayerDeferred(float left, float top, float right, float bottom, - const SkPaint* paint, int flags); - - void drawRenderNode(RenderNode* displayList, Rect& dirty, int32_t replayFlags = 1); - void drawLayer(Layer* layer); - void drawBitmap(const SkBitmap* bitmap, const SkPaint* paint); - void drawBitmaps(const SkBitmap* bitmap, AssetAtlas::Entry* entry, int bitmapCount, - TextureVertex* vertices, bool pureTranslate, const Rect& bounds, const SkPaint* paint); - void drawBitmap(const SkBitmap* bitmap, Rect src, Rect dst, - const SkPaint* paint); - void drawBitmapMesh(const SkBitmap* bitmap, int meshWidth, int meshHeight, - const float* vertices, const int* colors, const SkPaint* paint); - void drawPatches(const SkBitmap* bitmap, AssetAtlas::Entry* entry, - TextureVertex* vertices, uint32_t indexCount, const SkPaint* paint); - void drawPatch(const SkBitmap* bitmap, const Patch* mesh, AssetAtlas::Entry* entry, - float left, float top, float right, float bottom, const SkPaint* paint); - void drawColor(int color, SkXfermode::Mode mode); - void drawRect(float left, float top, float right, float bottom, - const SkPaint* paint); - void drawRoundRect(float left, float top, float right, float bottom, - float rx, float ry, const SkPaint* paint); - void drawCircle(float x, float y, float radius, const SkPaint* paint); - void drawOval(float left, float top, float right, float bottom, - const SkPaint* paint); - void drawArc(float left, float top, float right, float bottom, - float startAngle, float sweepAngle, bool useCenter, const SkPaint* paint); - void drawPath(const SkPath* path, const SkPaint* paint); - void drawLines(const float* points, int count, const SkPaint* paint); - void drawPoints(const float* points, int count, const SkPaint* paint); - void drawTextOnPath(const glyph_t* glyphs, int bytesCount, int count, const SkPath* path, - float hOffset, float vOffset, const SkPaint* paint); - void drawText(const glyph_t* glyphs, int bytesCount, int count, float x, float y, - const float* positions, const SkPaint* paint, float totalAdvance, const Rect& bounds, - DrawOpMode drawOpMode = DrawOpMode::kImmediate); - void drawRects(const float* rects, int count, const SkPaint* paint); - - void drawShadow(float casterAlpha, - const VertexBuffer* ambientShadowVertexBuffer, - const VertexBuffer* spotShadowVertexBuffer); - - void setDrawFilter(SkDrawFilter* filter); - - /** - * Store the current display state (most importantly, the current clip and transform), and - * additionally map the state's bounds from local to window coordinates. - * - * Returns true if quick-rejected - */ - bool storeDisplayState(DeferredDisplayState& state, int stateDeferFlags); - void restoreDisplayState(const DeferredDisplayState& state, bool skipClipRestore = false); - void setupMergedMultiDraw(const Rect* clipRect); - - bool isCurrentTransformSimple() { - return currentTransform()->isSimple(); - } - - Caches& getCaches() { - return mCaches; - } - - RenderState& renderState() { - return mRenderState; - } - - int getViewportWidth() { return mState.getViewportWidth(); } - int getViewportHeight() { return mState.getViewportHeight(); } - - /** - * Scales the alpha on the current snapshot. This alpha value will be modulated - * with other alpha values when drawing primitives. - */ - void scaleAlpha(float alpha) { mState.scaleAlpha(alpha); } - - /** - * Inserts a named event marker in the stream of GL commands. - */ - void eventMark(const char* name) const; - - /** - * Inserts a formatted event marker in the stream of GL commands. - */ - void eventMarkDEBUG(const char *fmt, ...) const; - - /** - * Inserts a named group marker in the stream of GL commands. This marker - * can be used by tools to group commands into logical groups. A call to - * this method must always be followed later on by a call to endMark(). - */ - void startMark(const char* name) const; - - /** - * Closes the last group marker opened by startMark(). - */ - void endMark() const; - - /** - * Build the best transform to use to rasterize text given a full - * transform matrix, and whether filteration is needed. - * - * Returns whether filtration is needed - */ - bool findBestFontTransform(const mat4& transform, SkMatrix* outMatrix) const; - -#if DEBUG_MERGE_BEHAVIOR - void drawScreenSpaceColorRect(float left, float top, float right, float bottom, int color) { - mCaches.setScissorEnabled(false); - - // should only be called outside of other draw ops, so stencil can only be in test state - bool stencilWasEnabled = mCaches.stencil.isTestEnabled(); - mCaches.stencil.disable(); - - drawColorRect(left, top, right, bottom, color, SkXfermode::kSrcOver_Mode, true); - - if (stencilWasEnabled) mCaches.stencil.enableTest(); - mDirty = true; - } -#endif - - const Vector3& getLightCenter() const { return mState.currentLightCenter(); } - float getLightRadius() const { return mLightRadius; } - uint8_t getAmbientShadowAlpha() const { return mAmbientShadowAlpha; } - uint8_t getSpotShadowAlpha() const { return mSpotShadowAlpha; } - - /////////////////////////////////////////////////////////////////// - /// State manipulation - - int getSaveCount() const; - int save(int flags); - void restore(); - void restoreToCount(int saveCount); - - void setGlobalMatrix(const Matrix4& matrix) { - mState.setMatrix(matrix); - } - void setLocalMatrix(const Matrix4& matrix); - void setLocalMatrix(const SkMatrix& matrix); - void concatMatrix(const SkMatrix& matrix) { mState.concatMatrix(matrix); } - - void translate(float dx, float dy, float dz = 0.0f); - void rotate(float degrees); - void scale(float sx, float sy); - void skew(float sx, float sy); - - void setMatrix(const Matrix4& matrix); // internal only convenience method - void concatMatrix(const Matrix4& matrix); // internal only convenience method - - const Rect& getLocalClipBounds() const { return mState.getLocalClipBounds(); } - const Rect& getRenderTargetClipBounds() const { return mState.getRenderTargetClipBounds(); } - bool quickRejectConservative(float left, float top, - float right, float bottom) const { - return mState.quickRejectConservative(left, top, right, bottom); - } - - bool clipRect(float left, float top, - float right, float bottom, SkRegion::Op op); - bool clipPath(const SkPath* path, SkRegion::Op op); - bool clipRegion(const SkRegion* region, SkRegion::Op op); - - /** - * Does not support different clipping Ops (that is, every call to setClippingOutline is - * effectively using SkRegion::kReplaceOp) - * - * The clipping outline is independent from the regular clip. - */ - void setClippingOutline(LinearAllocator& allocator, const Outline* outline); - void setClippingRoundRect(LinearAllocator& allocator, - const Rect& rect, float radius, bool highPriority = true); - void setProjectionPathMask(LinearAllocator& allocator, const SkPath* path); - - inline bool hasRectToRectTransform() const { return mState.hasRectToRectTransform(); } - inline const mat4* currentTransform() const { return mState.currentTransform(); } - - /////////////////////////////////////////////////////////////////// - /// CanvasStateClient interface - - virtual void onViewportInitialized() override; - virtual void onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) override; - virtual GLuint getTargetFbo() const override { return 0; } - - SkPath* allocPathForFrame() { - std::unique_ptr<SkPath> path(new SkPath()); - SkPath* returnPath = path.get(); - mTempPaths.push_back(std::move(path)); - return returnPath; - } - - void setBaseTransform(const Matrix4& matrix) { mBaseTransform = matrix; } - -protected: - /** - * Perform the setup specific to a frame. This method does not - * issue any OpenGL commands. - */ - void setupFrameState(int viewportWidth, int viewportHeight, - float left, float top, float right, float bottom, bool opaque); - - /** - * Indicates the start of rendering. This method will setup the - * initial OpenGL state (viewport, clearing the buffer, etc.) - */ - void startFrame(); - - /** - * Clears the underlying surface if needed. - */ - virtual void clear(float left, float top, float right, float bottom, bool opaque); - - /** - * Call this method after updating a layer during a drawing pass. - */ - void resumeAfterLayer(); - - /** - * This method is called whenever a stencil buffer is required. Subclasses - * should override this method and call attachStencilBufferToLayer() on the - * appropriate layer(s). - */ - virtual void ensureStencilBuffer(); - - /** - * Obtains a stencil render buffer (allocating it if necessary) and - * attaches it to the specified layer. - */ - void attachStencilBufferToLayer(Layer* layer); - - /** - * Draw a rectangle list. Currently only used for the the stencil buffer so that the stencil - * will have a value of 'n' in every unclipped pixel, where 'n' is the number of rectangles - * in the list. - */ - void drawRectangleList(const RectangleList& rectangleList); - - bool quickRejectSetupScissor(float left, float top, float right, float bottom, - const SkPaint* paint = nullptr); - bool quickRejectSetupScissor(const Rect& bounds, const SkPaint* paint = nullptr) { - return quickRejectSetupScissor(bounds.left, bounds.top, - bounds.right, bounds.bottom, paint); - } - - /** - * Compose the layer defined in the current snapshot with the layer - * defined by the previous snapshot. - * - * The current snapshot *must* be a layer (flag kFlagIsLayer set.) - * - * @param curent The current snapshot containing the layer to compose - * @param previous The previous snapshot to compose the current layer with - */ - virtual void composeLayer(const Snapshot& current, const Snapshot& previous); - - /** - * Marks the specified region as dirty at the specified bounds. - */ - void dirtyLayerUnchecked(Rect& bounds, Region* region); - - /** - * Returns the region of the current layer. - */ - virtual Region* getRegion() const { - return mState.currentRegion(); - } - - /** - * Indicates whether rendering is currently targeted at a layer. - */ - virtual bool hasLayer() const { - return (mState.currentFlags() & Snapshot::kFlagFboTarget) && mState.currentRegion(); - } - - /** - * Renders the specified layer as a textured quad. - * - * @param layer The layer to render - * @param rect The bounds of the layer - */ - void drawTextureLayer(Layer* layer, const Rect& rect); - - /** - * Gets the alpha from a layer, accounting for snapshot alpha - * - * @param layer The layer from which the alpha is extracted - */ - inline float getLayerAlpha(const Layer* layer) const; - - /** - * Set to true to suppress error checks at the end of a frame. - */ - virtual bool suppressErrorChecks() const { - return false; - } - - CanvasState mState; - Caches& mCaches; - RenderState& mRenderState; - -private: - enum class GlopRenderType { - Standard, - Multi, - LayerClear - }; - - void renderGlop(const Glop& glop, GlopRenderType type = GlopRenderType::Standard); - - /** - * Discards the content of the framebuffer if supported by the driver. - * This method should be called at the beginning of a frame to optimize - * rendering on some tiler architectures. - */ - void discardFramebuffer(float left, float top, float right, float bottom); - - /** - * Sets the clipping rectangle using glScissor. The clip is defined by - * the current snapshot's clipRect member. - */ - void setScissorFromClip(); - - /** - * Sets the clipping region using the stencil buffer. The clip region - * is defined by the current snapshot's clipRegion member. - */ - void setStencilFromClip(); - - /** - * Given the local bounds of the layer, calculates ... - */ - void calculateLayerBoundsAndClip(Rect& bounds, Rect& clip, bool fboLayer); - - /** - * Given the local bounds + clip of the layer, updates current snapshot's empty/invisible - */ - void updateSnapshotIgnoreForLayer(const Rect& bounds, const Rect& clip, - bool fboLayer, int alpha); - - /** - * Creates a new layer stored in the specified snapshot. - * - * @param snapshot The snapshot associated with the new layer - * @param left The left coordinate of the layer - * @param top The top coordinate of the layer - * @param right The right coordinate of the layer - * @param bottom The bottom coordinate of the layer - * @param alpha The translucency of the layer - * @param mode The blending mode of the layer - * @param flags The layer save flags - * @param mask A mask to use when drawing the layer back, may be empty - * - * @return True if the layer was successfully created, false otherwise - */ - bool createLayer(float left, float top, float right, float bottom, - const SkPaint* paint, int flags, const SkPath* convexMask); - - /** - * Creates a new layer stored in the specified snapshot as an FBO. - * - * @param layer The layer to store as an FBO - * @param snapshot The snapshot associated with the new layer - * @param bounds The bounds of the layer - */ - bool createFboLayer(Layer* layer, Rect& bounds, Rect& clip); - - /** - * Compose the specified layer as a region. - * - * @param layer The layer to compose - * @param rect The layer's bounds - */ - void composeLayerRegion(Layer* layer, const Rect& rect); - - /** - * Restores the content in layer to the screen, swapping the blend mode, - * specifically used in the restore() of a saveLayerAlpha(). - * - * This allows e.g. a layer that would have been drawn on top of existing content (with SrcOver) - * to be drawn underneath. - * - * This will always ignore the canvas transform. - */ - void composeLayerRectSwapped(Layer* layer, const Rect& rect); - - /** - * Draws the content in layer to the screen. - */ - void composeLayerRect(Layer* layer, const Rect& rect); - - /** - * Clears all the regions corresponding to the current list of layers. - * This method MUST be invoked before any drawing operation. - */ - void clearLayerRegions(); - - /** - * Mark the layer as dirty at the specified coordinates. The coordinates - * are transformed with the supplied matrix. - */ - void dirtyLayer(const float left, const float top, - const float right, const float bottom, const Matrix4& transform); - - /** - * Mark the layer as dirty at the specified coordinates. - */ - void dirtyLayer(const float left, const float top, - const float right, const float bottom); - - /** - * Draws a colored rectangle with the specified color. The specified coordinates - * are transformed by the current snapshot's transform matrix unless specified - * otherwise. - * - * @param left The left coordinate of the rectangle - * @param top The top coordinate of the rectangle - * @param right The right coordinate of the rectangle - * @param bottom The bottom coordinate of the rectangle - * @param paint The paint containing the color, blending mode, etc. - * @param ignoreTransform True if the current transform should be ignored - */ - void drawColorRect(float left, float top, float right, float bottom, - const SkPaint* paint, bool ignoreTransform = false); - - /** - * Draws a series of colored rectangles with the specified color. The specified - * coordinates are transformed by the current snapshot's transform matrix unless - * specified otherwise. - * - * @param rects A list of rectangles, 4 floats (left, top, right, bottom) - * per rectangle - * @param paint The paint containing the color, blending mode, etc. - * @param ignoreTransform True if the current transform should be ignored - * @param dirty True if calling this method should dirty the current layer - * @param clip True if the rects should be clipped, false otherwise - */ - void drawColorRects(const float* rects, int count, const SkPaint* paint, - bool ignoreTransform = false, bool dirty = true, bool clip = true); - - /** - * Draws the shape represented by the specified path texture. - * This method invokes drawPathTexture() but takes into account - * the extra left/top offset and the texture offset to correctly - * position the final shape. - * - * @param left The left coordinate of the shape to render - * @param top The top coordinate of the shape to render - * @param texture The texture reprsenting the shape - * @param paint The paint to draw the shape with - */ - void drawShape(float left, float top, PathTexture* texture, const SkPaint* paint); - - /** - * Renders a strip of polygons with the specified paint, used for tessellated geometry. - * - * @param vertexBuffer The VertexBuffer to be drawn - * @param paint The paint to render with - * @param flags flags with which to draw - */ - void drawVertexBuffer(float translateX, float translateY, const VertexBuffer& vertexBuffer, - const SkPaint* paint, int flags = 0); - - /** - * Convenience for translating method - */ - void drawVertexBuffer(const VertexBuffer& vertexBuffer, - const SkPaint* paint, int flags = 0) { - drawVertexBuffer(0.0f, 0.0f, vertexBuffer, paint, flags); - } - - /** - * Renders the convex hull defined by the specified path as a strip of polygons. - * - * @param path The hull of the path to draw - * @param paint The paint to render with - */ - void drawConvexPath(const SkPath& path, const SkPaint* paint); - - /** - * Draws shadow layer on text (with optional positions). - * - * @param paint The paint to draw the shadow with - * @param text The text to draw - * @param count The number of glyphs in the text - * @param positions The x, y positions of individual glyphs (or NULL) - * @param fontRenderer The font renderer object - * @param alpha The alpha value for drawing the shadow - * @param x The x coordinate where the shadow will be drawn - * @param y The y coordinate where the shadow will be drawn - */ - void drawTextShadow(const SkPaint* paint, const glyph_t* glyphs, int count, - const float* positions, FontRenderer& fontRenderer, int alpha, - float x, float y); - - /** - * Draws a path texture. Path textures are alpha8 bitmaps that need special - * compositing to apply colors/filters/etc. - * - * @param texture The texture to render - * @param x The x coordinate where the texture will be drawn - * @param y The y coordinate where the texture will be drawn - * @param paint The paint to draw the texture with - */ - void drawPathTexture(PathTexture* texture, float x, float y, const SkPaint* paint); - - /** - * Resets the texture coordinates stored in mMeshVertices. Setting the values - * back to default is achieved by calling: - * - * resetDrawTextureTexCoords(0.0f, 0.0f, 1.0f, 1.0f); - * - * @param u1 The left coordinate of the texture - * @param v1 The bottom coordinate of the texture - * @param u2 The right coordinate of the texture - * @param v2 The top coordinate of the texture - */ - void resetDrawTextureTexCoords(float u1, float v1, float u2, float v2); - - /** - * Returns true if the specified paint will draw invisible text. - */ - bool canSkipText(const SkPaint* paint) const; - - bool updateLayer(Layer* layer, bool inFrame); - void updateLayers(); - void flushLayers(); - -#if DEBUG_LAYERS_AS_REGIONS - /** - * Renders the specified region as a series of rectangles. This method - * is used for debugging only. - */ - void drawRegionRectsDebug(const Region& region); -#endif - - /** - * Renders the specified region as a series of rectangles. The region - * must be in screen-space coordinates. - */ - void drawRegionRects(const SkRegion& region, const SkPaint& paint, bool dirty = false); - - /** - * Draws the current clip region if any. Only when DEBUG_CLIP_REGIONS - * is turned on. - */ - void debugClip(); - - void debugOverdraw(bool enable, bool clear); - void renderOverdraw(); - void countOverdraw(); - - /** - * Should be invoked every time the glScissor is modified. - */ - inline void dirtyClip() { mState.setDirtyClip(true); } - - inline const UvMapper& getMapper(const Texture* texture) { - return texture && texture->uvMapper ? *texture->uvMapper : mUvMapper; - } - - /** - * Returns a texture object for the specified bitmap. The texture can - * come from the texture cache or an atlas. If this method returns - * NULL, the texture could not be found and/or allocated. - */ - Texture* getTexture(const SkBitmap* bitmap); - - bool reportAndClearDirty() { bool ret = mDirty; mDirty = false; return ret; } - inline Snapshot* writableSnapshot() { return mState.writableSnapshot(); } - inline const Snapshot* currentSnapshot() const { return mState.currentSnapshot(); } - - // State used to define the clipping region - Rect mTilingClip; - // Is the target render surface opaque - bool mOpaque; - // Is a frame currently being rendered - bool mFrameStarted; - - // Default UV mapper - const UvMapper mUvMapper; - - // List of rectangles to clear after saveLayer() is invoked - std::vector<Rect> mLayers; - // List of layers to update at the beginning of a frame - std::vector< sp<Layer> > mLayerUpdates; - - // See PROPERTY_DISABLE_SCISSOR_OPTIMIZATION in - // Properties.h - bool mScissorOptimizationDisabled; - - bool mSkipOutlineClip; - - // True if anything has been drawn since the last call to - // reportAndClearDirty() - bool mDirty; - - // Lighting + shadows - Vector3 mLightCenter; - float mLightRadius; - uint8_t mAmbientShadowAlpha; - uint8_t mSpotShadowAlpha; - - // Paths kept alive for the duration of the frame - std::vector<std::unique_ptr<SkPath>> mTempPaths; - - /** - * Initial transform for a rendering pass; transform from global device - * coordinates to the current RenderNode's drawing content coordinates, - * with the RenderNode's RenderProperty transforms already applied. - * Calling setMatrix(mBaseTransform) will result in drawing at the origin - * of the DisplayList's recorded surface prior to any Canvas - * transformation. - */ - Matrix4 mBaseTransform; - - friend class Layer; - friend class TextDrawFunctor; - friend class DrawBitmapOp; - friend class DrawPatchOp; - -}; // class OpenGLRenderer - -}; // namespace uirenderer -}; // namespace android - -#endif // ANDROID_HWUI_OPENGL_RENDERER_H diff --git a/libs/hwui/PatchCache.cpp b/libs/hwui/PatchCache.cpp index bd6feb9fc762..983c17e92266 100644 --- a/libs/hwui/PatchCache.cpp +++ b/libs/hwui/PatchCache.cpp @@ -36,28 +36,12 @@ PatchCache::PatchCache(RenderState& renderState) , mSize(0) , mCache(LruCache<PatchDescription, Patch*>::kUnlimitedCapacity) , mMeshBuffer(0) - , mFreeBlocks(nullptr) - , mGenerationId(0) {} + , mFreeBlocks(nullptr) {} PatchCache::~PatchCache() { clear(); } -void PatchCache::init() { - bool created = false; - if (!mMeshBuffer) { - glGenBuffers(1, &mMeshBuffer); - created = true; - } - - mRenderState.meshState().bindMeshBuffer(mMeshBuffer); - mRenderState.meshState().resetVertexPointers(); - - if (created) { - createVertexBuffer(); - } -} - /////////////////////////////////////////////////////////////////////////////// // Caching /////////////////////////////////////////////////////////////////////////////// @@ -80,8 +64,7 @@ void PatchCache::clear() { clearCache(); if (mMeshBuffer) { - mRenderState.meshState().unbindMeshBuffer(); - glDeleteBuffers(1, &mMeshBuffer); + mRenderState.meshState().deleteMeshBuffer(mMeshBuffer); mMeshBuffer = 0; mSize = 0; } @@ -170,10 +153,10 @@ void PatchCache::clearGarbage() { } void PatchCache::createVertexBuffer() { - glBufferData(GL_ARRAY_BUFFER, mMaxSize, nullptr, GL_DYNAMIC_DRAW); + mRenderState.meshState().genOrUpdateMeshBuffer(&mMeshBuffer, + mMaxSize, nullptr, GL_DYNAMIC_DRAW); mSize = 0; mFreeBlocks = new BufferBlock(0, mMaxSize); - mGenerationId++; } /** @@ -182,7 +165,9 @@ void PatchCache::createVertexBuffer() { */ void PatchCache::setupMesh(Patch* newMesh) { // This call ensures the VBO exists and that it is bound - init(); + if (!mMeshBuffer) { + createVertexBuffer(); + } // If we're running out of space, let's clear the entire cache uint32_t size = newMesh->getSize(); @@ -215,7 +200,9 @@ void PatchCache::setupMesh(Patch* newMesh) { // Copy the 9patch mesh in the VBO newMesh->positionOffset = (GLintptr) (block->offset); newMesh->textureOffset = newMesh->positionOffset + kMeshTextureOffset; - glBufferSubData(GL_ARRAY_BUFFER, newMesh->positionOffset, size, newMesh->vertices.get()); + + mRenderState.meshState().updateMeshBufferSubData(mMeshBuffer, newMesh->positionOffset, size, + newMesh->vertices.get()); // Remove the block since we've used it entirely if (block->size == size) { @@ -236,17 +223,15 @@ void PatchCache::setupMesh(Patch* newMesh) { static const UvMapper sIdentity; -const Patch* PatchCache::get(const AssetAtlas::Entry* entry, - const uint32_t bitmapWidth, const uint32_t bitmapHeight, +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) { - const UvMapper& mapper = entry ? entry->uvMapper : sIdentity; Patch* newMesh = new Patch(bitmapWidth, bitmapHeight, - pixelWidth, pixelHeight, mapper, patch); + pixelWidth, pixelHeight, sIdentity, patch); if (newMesh->vertices) { setupMesh(newMesh); diff --git a/libs/hwui/PatchCache.h b/libs/hwui/PatchCache.h index bc5981d53457..aa746c7dfa15 100644 --- a/libs/hwui/PatchCache.h +++ b/libs/hwui/PatchCache.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef ANDROID_HWUI_PATCH_CACHE_H -#define ANDROID_HWUI_PATCH_CACHE_H +#pragma once #include <GLES2/gl2.h> @@ -23,7 +22,6 @@ #include <androidfw/ResourceTypes.h> -#include "AssetAtlas.h" #include "Debug.h" #include "utils/Pair.h" @@ -48,15 +46,14 @@ class Patch; /////////////////////////////////////////////////////////////////////////////// class Caches; +class RenderState; class PatchCache { public: explicit PatchCache(RenderState& renderState); ~PatchCache(); - void init(); - const Patch* get(const AssetAtlas::Entry* entry, - const uint32_t bitmapWidth, const uint32_t bitmapHeight, + const Patch* get(const uint32_t bitmapWidth, const uint32_t bitmapHeight, const float pixelWidth, const float pixelHeight, const Res_png_9patch* patch); void clear(); @@ -72,10 +69,6 @@ public: return mMeshBuffer; } - uint32_t getGenerationId() const { - return mGenerationId; - } - /** * Removes the entries associated with the specified 9-patch. This is meant * to be called from threads that are not the EGL context thread (GC thread @@ -178,8 +171,6 @@ private: // First available free block inside the mesh buffer BufferBlock* mFreeBlocks; - uint32_t mGenerationId; - // Garbage tracking, required to handle GC events on the VM side Vector<Res_png_9patch*> mGarbage; mutable Mutex mLock; @@ -187,5 +178,3 @@ private: }; // namespace uirenderer }; // namespace android - -#endif // ANDROID_HWUI_PATCH_CACHE_H diff --git a/libs/hwui/PathCache.cpp b/libs/hwui/PathCache.cpp index 972ff81ad31f..cc96de71df82 100644 --- a/libs/hwui/PathCache.cpp +++ b/libs/hwui/PathCache.cpp @@ -17,6 +17,8 @@ #include <SkBitmap.h> #include <SkCanvas.h> #include <SkColor.h> +#include <SkColorFilter.h> +#include <SkMaskFilter.h> #include <SkPaint.h> #include <SkPath.h> #include <SkPathEffect.h> @@ -121,34 +123,19 @@ bool PathDescription::operator==(const PathDescription& rhs) const { // Utilities /////////////////////////////////////////////////////////////////////////////// -bool PathCache::canDrawAsConvexPath(SkPath* path, const SkPaint* paint) { - // NOTE: This should only be used after PathTessellator handles joins properly - return paint->getPathEffect() == nullptr && path->getConvexity() == SkPath::kConvex_Convexity; -} - -void PathCache::computePathBounds(const SkPath* path, const SkPaint* paint, - float& left, float& top, float& offset, uint32_t& width, uint32_t& height) { +static void computePathBounds(const SkPath* path, const SkPaint* paint, PathTexture* texture, + uint32_t& width, uint32_t& height) { const SkRect& bounds = path->getBounds(); - PathCache::computeBounds(bounds, paint, left, top, offset, width, height); -} - -void PathCache::computeBounds(const SkRect& bounds, const SkPaint* paint, - float& left, float& top, float& offset, uint32_t& width, uint32_t& height) { const float pathWidth = std::max(bounds.width(), 1.0f); const float pathHeight = std::max(bounds.height(), 1.0f); - left = bounds.fLeft; - top = bounds.fTop; - - offset = (int) floorf(std::max(paint->getStrokeWidth(), 1.0f) * 1.5f + 0.5f); + texture->left = floorf(bounds.fLeft); + texture->top = floorf(bounds.fTop); - width = uint32_t(pathWidth + offset * 2.0 + 0.5); - height = uint32_t(pathHeight + offset * 2.0 + 0.5); -} + texture->offset = (int) floorf(std::max(paint->getStrokeWidth(), 1.0f) * 1.5f + 0.5f); -static void initBitmap(SkBitmap& bitmap, uint32_t width, uint32_t height) { - bitmap.allocPixels(SkImageInfo::MakeA8(width, height)); - bitmap.eraseColor(0); + width = uint32_t(pathWidth + texture->offset * 2.0 + 0.5); + height = uint32_t(pathHeight + texture->offset * 2.0 + 0.5); } static void initPaint(SkPaint& paint) { @@ -159,20 +146,30 @@ static void initPaint(SkPaint& paint) { paint.setColorFilter(nullptr); paint.setMaskFilter(nullptr); paint.setShader(nullptr); - SkXfermode* mode = SkXfermode::Create(SkXfermode::kSrc_Mode); - SkSafeUnref(paint.setXfermode(mode)); + paint.setBlendMode(SkBlendMode::kSrc); } -static void drawPath(const SkPath *path, const SkPaint* paint, SkBitmap& bitmap, - float left, float top, float offset, uint32_t width, uint32_t height) { - initBitmap(bitmap, width, height); +static sk_sp<Bitmap> drawPath(const SkPath* path, const SkPaint* paint, PathTexture* texture, + 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); + return nullptr; + } + sk_sp<Bitmap> bitmap = Bitmap::allocateHeapBitmap(SkImageInfo::MakeA8(width, height)); SkPaint pathPaint(*paint); initPaint(pathPaint); - SkCanvas canvas(bitmap); - canvas.translate(-left + offset, -top + offset); + SkBitmap skBitmap; + bitmap->getSkBitmap(&skBitmap); + skBitmap.eraseColor(0); + SkCanvas canvas(skBitmap); + canvas.translate(-texture->left + texture->offset, -texture->top + texture->offset); canvas.drawPath(*path, pathPaint); + return bitmap; } /////////////////////////////////////////////////////////////////////////////// @@ -182,8 +179,7 @@ static void drawPath(const SkPath *path, const SkPaint* paint, SkBitmap& bitmap, PathCache::PathCache() : mCache(LruCache<PathDescription, PathTexture*>::kUnlimitedCapacity) , mSize(0) - , mMaxSize(Properties::pathCacheSize) - , mTexNum(0) { + , mMaxSize(Properties::pathCacheSize) { mCache.setOnEntryRemovedListener(this); GLint maxTextureSize; @@ -227,7 +223,7 @@ void PathCache::removeTexture(PathTexture* texture) { // If there is a pending task we must wait for it to return // before attempting our cleanup - const sp<Task<SkBitmap*> >& task = texture->task(); + const sp<PathTask>& task = texture->task(); if (task != nullptr) { task->getResult(); texture->clearTask(); @@ -239,7 +235,6 @@ void PathCache::removeTexture(PathTexture* texture) { "the cache in an inconsistent state", size); } mSize -= size; - mTexNum--; } PATH_LOGD("PathCache::delete name, size, mSize = %d, %d, %d", @@ -264,7 +259,14 @@ void PathCache::purgeCache(uint32_t width, uint32_t height) { } void PathCache::trim() { - while (mSize > mMaxSize || mTexNum > DEFAULT_PATH_TEXTURE_CAP) { + // 25 is just an arbitrary lower bound to ensure we aren't in weird edge cases + // of things like a cap of 0 or 1 as that's going to break things. + // It does not represent a reasonable minimum value + static_assert(DEFAULT_PATH_TEXTURE_CAP > 25, "Path cache texture cap is too small"); + + while (mSize > mMaxSize || mCache.size() > DEFAULT_PATH_TEXTURE_CAP) { + LOG_ALWAYS_FATAL_IF(!mCache.size(), "Inconsistent mSize! Ran out of items to remove!" + " mSize = %u, mMaxSize = %u", mSize, mMaxSize); mCache.removeOldest(); } } @@ -273,27 +275,21 @@ PathTexture* PathCache::addTexture(const PathDescription& entry, const SkPath *p const SkPaint* paint) { ATRACE_NAME("Generate Path Texture"); - float left, top, offset; - uint32_t width, height; - computePathBounds(path, paint, left, top, offset, width, height); - - if (!checkTextureSize(width, height)) return nullptr; - - purgeCache(width, height); - - SkBitmap bitmap; - drawPath(path, paint, bitmap, left, top, offset, width, height); - - PathTexture* texture = new PathTexture(Caches::getInstance(), - left, top, offset, path->getGenerationID()); - generateTexture(entry, &bitmap, texture); + PathTexture* texture = new PathTexture(Caches::getInstance(), path->getGenerationID()); + sk_sp<Bitmap> bitmap(drawPath(path, paint, texture, mMaxTextureSize)); + if (!bitmap) { + delete texture; + return nullptr; + } + purgeCache(bitmap->width(), bitmap->height()); + generateTexture(entry, *bitmap, texture); return texture; } -void PathCache::generateTexture(const PathDescription& entry, SkBitmap* bitmap, +void PathCache::generateTexture(const PathDescription& entry, Bitmap& bitmap, PathTexture* texture, bool addToCache) { - generateTexture(*bitmap, texture); + generateTexture(bitmap, texture); // Note here that we upload to a texture even if it's bigger than mMaxSize. // Such an entry in mCache will only be temporary, since it will be evicted @@ -314,11 +310,10 @@ void PathCache::clear() { mCache.clear(); } -void PathCache::generateTexture(SkBitmap& bitmap, Texture* texture) { +void PathCache::generateTexture(Bitmap& bitmap, Texture* texture) { ATRACE_NAME("Upload Path Texture"); texture->upload(bitmap); texture->setFilter(GL_LINEAR); - mTexNum++; } /////////////////////////////////////////////////////////////////////////////// @@ -326,29 +321,14 @@ void PathCache::generateTexture(SkBitmap& bitmap, Texture* texture) { /////////////////////////////////////////////////////////////////////////////// PathCache::PathProcessor::PathProcessor(Caches& caches): - TaskProcessor<SkBitmap*>(&caches.tasks), mMaxTextureSize(caches.maxTextureSize) { + TaskProcessor<sk_sp<Bitmap> >(&caches.tasks), mMaxTextureSize(caches.maxTextureSize) { } -void PathCache::PathProcessor::onProcess(const sp<Task<SkBitmap*> >& task) { +void PathCache::PathProcessor::onProcess(const sp<Task<sk_sp<Bitmap> > >& task) { PathTask* t = static_cast<PathTask*>(task.get()); ATRACE_NAME("pathPrecache"); - float left, top, offset; - uint32_t width, height; - PathCache::computePathBounds(&t->path, &t->paint, left, top, offset, width, height); - - PathTexture* texture = t->texture; - texture->left = left; - texture->top = top; - texture->offset = offset; - - if (width <= mMaxTextureSize && height <= mMaxTextureSize) { - SkBitmap* bitmap = new SkBitmap(); - drawPath(&t->path, &t->paint, *bitmap, left, top, offset, width, height); - t->setResult(bitmap); - } else { - t->setResult(nullptr); - } + t->setResult(drawPath(&t->path, &t->paint, t->texture, mMaxTextureSize)); } /////////////////////////////////////////////////////////////////////////////// @@ -393,16 +373,15 @@ PathTexture* PathCache::get(const SkPath* path, const SkPaint* paint) { } else { // A bitmap is attached to the texture, this means we need to // upload it as a GL texture - const sp<Task<SkBitmap*> >& task = texture->task(); + const sp<PathTask>& task = texture->task(); if (task != nullptr) { // But we must first wait for the worker thread to be done // producing the bitmap, so let's wait - SkBitmap* bitmap = task->getResult(); + sk_sp<Bitmap> bitmap = task->getResult(); if (bitmap) { - generateTexture(entry, bitmap, texture, false); + generateTexture(entry, *bitmap, texture, false); texture->clearTask(); } else { - ALOGW("Path too large to be rendered into a texture"); texture->clearTask(); texture = nullptr; mCache.remove(entry); diff --git a/libs/hwui/PathCache.h b/libs/hwui/PathCache.h index e925848a25b8..7bd190df951b 100644 --- a/libs/hwui/PathCache.h +++ b/libs/hwui/PathCache.h @@ -19,6 +19,7 @@ #include "Debug.h" #include "Texture.h" +#include "hwui/Bitmap.h" #include "thread/Task.h" #include "thread/TaskProcessor.h" #include "utils/Macros.h" @@ -32,7 +33,6 @@ #include <vector> -class SkBitmap; class SkCanvas; class SkPaint; struct SkRect; @@ -41,7 +41,6 @@ namespace android { namespace uirenderer { class Caches; - /////////////////////////////////////////////////////////////////////////////// // Defines /////////////////////////////////////////////////////////////////////////////// @@ -57,18 +56,25 @@ class Caches; // Classes /////////////////////////////////////////////////////////////////////////////// +struct PathTexture; +class PathTask: public Task<sk_sp<Bitmap>> { +public: + 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; + + // copied, since input paint may not be immutable + const SkPaint paint; + PathTexture* texture; +}; + /** * Alpha texture used to represent a path. */ struct PathTexture: public Texture { - PathTexture(Caches& caches, float left, float top, - float offset, int generation) - : Texture(caches) - , left(left) - , top(top) - , offset(offset) { - this->generation = generation; - } PathTexture(Caches& caches, int generation) : Texture(caches) { this->generation = generation; @@ -91,11 +97,11 @@ struct PathTexture: public Texture { */ float offset = 0; - sp<Task<SkBitmap*> > task() const { + sp<PathTask> task() const { return mTask; } - void setTask(const sp<Task<SkBitmap*> >& task) { + void setTask(const sp<PathTask>& task) { mTask = task; } @@ -106,7 +112,7 @@ struct PathTexture: public Texture { } private: - sp<Task<SkBitmap*> > mTask; + sp<PathTask> mTask; }; // struct PathTexture enum class ShapeType { @@ -227,22 +233,15 @@ public: */ void precache(const SkPath* path, const SkPaint* paint); - static bool canDrawAsConvexPath(SkPath* path, const SkPaint* paint); - static void computePathBounds(const SkPath* path, const SkPaint* paint, - float& left, float& top, float& offset, uint32_t& width, uint32_t& height); - static void computeBounds(const SkRect& bounds, const SkPaint* paint, - float& left, float& top, float& offset, uint32_t& width, uint32_t& height); - private: PathTexture* addTexture(const PathDescription& entry, const SkPath *path, const SkPaint* paint); - PathTexture* addTexture(const PathDescription& entry, SkBitmap* bitmap); /** * Generates the texture from a bitmap into the specified texture structure. */ - void generateTexture(SkBitmap& bitmap, Texture* texture); - void generateTexture(const PathDescription& entry, SkBitmap* bitmap, PathTexture* texture, + void generateTexture(Bitmap& bitmap, Texture* texture); + void generateTexture(const PathDescription& entry, Bitmap& bitmap, PathTexture* texture, bool addToCache = true); PathTexture* get(const PathDescription& entry) { @@ -257,41 +256,15 @@ private: void removeTexture(PathTexture* texture); - bool checkTextureSize(uint32_t width, uint32_t height) { - if (width > mMaxTextureSize || height > mMaxTextureSize) { - ALOGW("Shape too large to be rendered into a texture (%dx%d, max=%dx%d)", - width, height, mMaxTextureSize, mMaxTextureSize); - return false; - } - return true; - } - void init(); - class PathTask: public Task<SkBitmap*> { - public: - PathTask(const SkPath* path, const SkPaint* paint, PathTexture* texture): - path(*path), paint(*paint), texture(texture) { - } - - ~PathTask() { - delete future()->get(); - } - - // copied, since input path not guaranteed to survive for duration of task - const SkPath path; - // copied, since input paint may not be immutable - const SkPaint paint; - PathTexture* texture; - }; - - class PathProcessor: public TaskProcessor<SkBitmap*> { + class PathProcessor: public TaskProcessor<sk_sp<Bitmap> > { public: explicit PathProcessor(Caches& caches); ~PathProcessor() { } - virtual void onProcess(const sp<Task<SkBitmap*> >& task) override; + virtual void onProcess(const sp<Task<sk_sp<Bitmap> > >& task) override; private: uint32_t mMaxTextureSize; @@ -304,12 +277,6 @@ private: bool mDebugEnabled; - /** - * Driver allocated 4k/8k/16k memory for small path cache, - * limit the number of PathTexture in case occupy too much memory in hardware. - */ - uint32_t mTexNum; - sp<PathProcessor> mProcessor; std::vector<uint32_t> mGarbage; diff --git a/libs/hwui/PixelBuffer.cpp b/libs/hwui/PixelBuffer.cpp index 165c7db4c85f..2a96b6914afc 100644 --- a/libs/hwui/PixelBuffer.cpp +++ b/libs/hwui/PixelBuffer.cpp @@ -37,8 +37,6 @@ public: uint8_t* map(AccessMode mode = kAccessMode_ReadWrite) override; - uint8_t* getMappedPointer() const override; - void upload(uint32_t x, uint32_t y, uint32_t width, uint32_t height, int offset) override; protected: @@ -64,10 +62,6 @@ void CpuPixelBuffer::unmap() { mAccessMode = kAccessMode_None; } -uint8_t* CpuPixelBuffer::getMappedPointer() const { - return mAccessMode == kAccessMode_None ? nullptr : mBuffer.get(); -} - 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]); @@ -84,8 +78,6 @@ public: uint8_t* map(AccessMode mode = kAccessMode_ReadWrite) override; - uint8_t* getMappedPointer() const override; - void upload(uint32_t x, uint32_t y, uint32_t width, uint32_t height, int offset) override; protected: @@ -142,10 +134,6 @@ void GpuPixelBuffer::unmap() { } } -uint8_t* GpuPixelBuffer::getMappedPointer() const { - return mMappedPointer; -} - void GpuPixelBuffer::upload(uint32_t x, uint32_t y, uint32_t width, uint32_t height, int offset) { // If the buffer is not mapped, unmap() will not bind it mCaches.pixelBufferState().bind(mBuffer); diff --git a/libs/hwui/PixelBuffer.h b/libs/hwui/PixelBuffer.h index 280af877b93d..77d5e413cb36 100644 --- a/libs/hwui/PixelBuffer.h +++ b/libs/hwui/PixelBuffer.h @@ -100,12 +100,6 @@ public: } /** - * Returns the currently mapped pointer. Returns NULL if the buffer - * is not mapped. - */ - virtual uint8_t* getMappedPointer() const = 0; - - /** * Upload the specified rectangle of this pixel buffer as a * GL_TEXTURE_2D texture. Calling this method will trigger * an unmap() if necessary. @@ -200,8 +194,7 @@ protected: /** * Unmaps this buffer, if needed. After the buffer is unmapped, * the pointer previously returned by map() becomes invalid and - * should not be used. After calling this method, getMappedPointer() - * will always return NULL. + * should not be used. */ virtual void unmap() = 0; diff --git a/libs/hwui/ProfileRenderer.cpp b/libs/hwui/ProfileRenderer.cpp new file mode 100644 index 000000000000..0ad484ce0687 --- /dev/null +++ b/libs/hwui/ProfileRenderer.cpp @@ -0,0 +1,40 @@ +/* + * 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 "ProfileRenderer.h" + +namespace android { +namespace uirenderer { + +void ProfileRenderer::drawRect(float left, float top, float right, float bottom, + const SkPaint& paint) { + mRenderer.drawRect(left, top, right, bottom, &paint); +} + +void ProfileRenderer::drawRects(const float* rects, int count, const SkPaint& paint) { + mRenderer.drawRects(rects, count, &paint); +} + +uint32_t ProfileRenderer::getViewportWidth() { + return mRenderer.getViewportWidth(); +} + +uint32_t ProfileRenderer::getViewportHeight() { + return mRenderer.getViewportHeight(); +} + +} /* namespace uirenderer */ +} /* namespace android */ diff --git a/libs/hwui/ProfileRenderer.h b/libs/hwui/ProfileRenderer.h new file mode 100644 index 000000000000..b9e586f592e8 --- /dev/null +++ b/libs/hwui/ProfileRenderer.h @@ -0,0 +1,42 @@ +/* + * 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 "IProfileRenderer.h" + +#include "BakedOpRenderer.h" + +namespace android { +namespace uirenderer { + +class ProfileRenderer : public IProfileRenderer { +public: + 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; + uint32_t getViewportWidth() override; + uint32_t getViewportHeight() override; + + virtual ~ProfileRenderer() {} + +private: + BakedOpRenderer& mRenderer; +}; + +} /* namespace uirenderer */ +} /* namespace android */ diff --git a/libs/hwui/Program.h b/libs/hwui/Program.h index e5200a516777..e70982f5444a 100644 --- a/libs/hwui/Program.h +++ b/libs/hwui/Program.h @@ -22,7 +22,7 @@ #include <GLES2/gl2.h> #include <GLES2/gl2ext.h> -#include <SkXfermode.h> +#include <SkBlendMode.h> #include "Debug.h" #include "FloatColor.h" @@ -54,6 +54,7 @@ namespace uirenderer { #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_SWAP_SRC_DST 0x2000 @@ -85,6 +86,8 @@ namespace uirenderer { #define PROGRAM_HAS_DEBUG_HIGHLIGHT 42 #define PROGRAM_HAS_ROUND_RECT_CLIP 43 +#define PROGRAM_HAS_GAMMA_CORRECTION 44 + /////////////////////////////////////////////////////////////////////////////// // Types /////////////////////////////////////////////////////////////////////////////// @@ -131,7 +134,8 @@ struct ProgramDescription { // Shaders bool hasBitmap; - bool isBitmapNpot; + bool isShaderBitmapExternal; + bool useShaderBasedWrap; bool hasVertexAlpha; bool useShadowAlphaInterp; @@ -140,7 +144,7 @@ struct ProgramDescription { Gradient gradientType; bool isSimpleGradient; - SkXfermode::Mode shadersMode; + SkBlendMode shadersMode; bool isBitmapFirst; GLenum bitmapWrapS; @@ -148,16 +152,18 @@ struct ProgramDescription { // Color operations ColorFilterMode colorOp; - SkXfermode::Mode colorMode; + SkBlendMode colorMode; // Framebuffer blending (requires Extensions.hasFramebufferFetch()) - // Ignored for all values < SkXfermode::kPlus_Mode - SkXfermode::Mode framebufferMode; + // Ignored for all values < SkBlendMode::kPlus + SkBlendMode framebufferMode; bool swapSrcDst; bool hasDebugHighlight; bool hasRoundRectClip; + bool hasGammaCorrection; + /** * Resets this description. All fields are reset back to the default * values they hold after building a new instance. @@ -176,26 +182,29 @@ struct ProgramDescription { modulate = false; hasBitmap = false; - isBitmapNpot = false; + isShaderBitmapExternal = false; + useShaderBasedWrap = false; hasGradient = false; gradientType = kGradientLinear; isSimpleGradient = false; - shadersMode = SkXfermode::kClear_Mode; + shadersMode = SkBlendMode::kClear; isBitmapFirst = false; bitmapWrapS = GL_CLAMP_TO_EDGE; bitmapWrapT = GL_CLAMP_TO_EDGE; colorOp = ColorFilterMode::None; - colorMode = SkXfermode::kClear_Mode; + colorMode = SkBlendMode::kClear; - framebufferMode = SkXfermode::kClear_Mode; + framebufferMode = SkBlendMode::kClear; swapSrcDst = false; hasDebugHighlight = false; hasRoundRectClip = false; + + hasGammaCorrection = false; } /** @@ -228,17 +237,20 @@ struct ProgramDescription { if (hasAlpha8Texture) key |= PROGRAM_KEY_A8_TEXTURE; if (hasBitmap) { key |= PROGRAM_KEY_BITMAP; - if (isBitmapNpot) { + if (useShaderBasedWrap) { key |= PROGRAM_KEY_BITMAP_NPOT; key |= getEnumForWrap(bitmapWrapS) << PROGRAM_BITMAP_WRAPS_SHIFT; key |= getEnumForWrap(bitmapWrapT) << PROGRAM_BITMAP_WRAPT_SHIFT; } + if (isShaderBitmapExternal) { + key |= PROGRAM_KEY_BITMAP_EXTERNAL; + } } if (hasGradient) key |= PROGRAM_KEY_GRADIENT; key |= programid(gradientType) << PROGRAM_GRADIENT_TYPE_SHIFT; if (isBitmapFirst) key |= PROGRAM_KEY_BITMAP_FIRST; if (hasBitmap && hasGradient) { - key |= (shadersMode & PROGRAM_MAX_XFERMODE) << PROGRAM_XFERMODE_SHADER_SHIFT; + key |= ((int)shadersMode & PROGRAM_MAX_XFERMODE) << PROGRAM_XFERMODE_SHADER_SHIFT; } switch (colorOp) { case ColorFilterMode::Matrix: @@ -246,12 +258,12 @@ struct ProgramDescription { break; case ColorFilterMode::Blend: key |= PROGRAM_KEY_COLOR_BLEND; - key |= (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 |= (framebufferMode & PROGRAM_MAX_XFERMODE) << PROGRAM_XFERMODE_FRAMEBUFFER_SHIFT; + key |= ((int)framebufferMode & PROGRAM_MAX_XFERMODE) << PROGRAM_XFERMODE_FRAMEBUFFER_SHIFT; if (swapSrcDst) key |= PROGRAM_KEY_SWAP_SRC_DST; if (modulate) key |= programid(0x1) << PROGRAM_MODULATE_SHIFT; if (hasVertexAlpha) key |= programid(0x1) << PROGRAM_HAS_VERTEX_ALPHA_SHIFT; @@ -262,6 +274,7 @@ struct ProgramDescription { if (hasColors) key |= programid(0x1) << PROGRAM_HAS_COLORS; if (hasDebugHighlight) key |= programid(0x1) << PROGRAM_HAS_DEBUG_HIGHLIGHT; if (hasRoundRectClip) key |= programid(0x1) << PROGRAM_HAS_ROUND_RECT_CLIP; + if (hasGammaCorrection) key |= programid(0x1) << PROGRAM_HAS_GAMMA_CORRECTION; return key; } diff --git a/libs/hwui/ProgramCache.cpp b/libs/hwui/ProgramCache.cpp index 59225e108ac7..71076791cf7f 100644 --- a/libs/hwui/ProgramCache.cpp +++ b/libs/hwui/ProgramCache.cpp @@ -17,8 +17,8 @@ #include <utils/String8.h> #include "Caches.h" -#include "Dither.h" #include "ProgramCache.h" +#include "Properties.h" namespace android { namespace uirenderer { @@ -69,22 +69,16 @@ 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 vec2 ditherTexCoords;\n", - "varying float linear;\n" - "varying vec2 ditherTexCoords;\n", + "varying highp vec2 linear;\n", + "varying float linear;\n", // Circular - "varying highp vec2 circular;\n" - "varying vec2 ditherTexCoords;\n", - "varying highp vec2 circular;\n" - "varying vec2 ditherTexCoords;\n", + "varying highp vec2 circular;\n", + "varying highp vec2 circular;\n", // Sweep - "varying highp vec2 sweep;\n" - "varying vec2 ditherTexCoords;\n", - "varying highp vec2 sweep;\n" - "varying vec2 ditherTexCoords;\n", + "varying highp vec2 sweep;\n", + "varying highp vec2 sweep;\n", }; const char* gVS_Header_Varyings_HasRoundRectClip = "varying highp vec2 roundRectPos;\n"; @@ -98,22 +92,16 @@ const char* gVS_Main_OutTransformedTexCoords = " outTexCoords = (mainTextureTransform * vec4(texCoords, 0.0, 1.0)).xy;\n"; const char* gVS_Main_OutGradient[6] = { // Linear - " linear = vec2((screenSpace * position).x, 0.5);\n" - " ditherTexCoords = (transform * position).xy * " STR(DITHER_KERNEL_SIZE_INV) ";\n", - " linear = (screenSpace * position).x;\n" - " ditherTexCoords = (transform * position).xy * " STR(DITHER_KERNEL_SIZE_INV) ";\n", + " linear = vec2((screenSpace * position).x, 0.5);\n", + " linear = (screenSpace * position).x;\n", // Circular - " circular = (screenSpace * position).xy;\n" - " ditherTexCoords = (transform * position).xy * " STR(DITHER_KERNEL_SIZE_INV) ";\n", - " circular = (screenSpace * position).xy;\n" - " ditherTexCoords = (transform * position).xy * " STR(DITHER_KERNEL_SIZE_INV) ";\n", + " circular = (screenSpace * position).xy;\n", + " circular = (screenSpace * position).xy;\n", // Sweep + " sweep = (screenSpace * position).xy;\n", " sweep = (screenSpace * position).xy;\n" - " ditherTexCoords = (transform * position).xy * " STR(DITHER_KERNEL_SIZE_INV) ";\n", - " sweep = (screenSpace * position).xy;\n" - " ditherTexCoords = (transform * position).xy * " STR(DITHER_KERNEL_SIZE_INV) ";\n", }; const char* gVS_Main_OutBitmapTexCoords = " outBitmapTexCoords = (textureTransform * position).xy * textureDimension;\n"; @@ -147,17 +135,18 @@ const char* gFS_Uniforms_TextureSampler = "uniform sampler2D baseSampler;\n"; const char* gFS_Uniforms_ExternalTextureSampler = "uniform samplerExternalOES baseSampler;\n"; -const char* gFS_Uniforms_Dither = - "uniform sampler2D ditherSampler;"; const char* gFS_Uniforms_GradientSampler[2] = { - "%s\n" + "uniform vec2 screenSize;\n" "uniform sampler2D gradientSampler;\n", - "%s\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"; const char* gFS_Uniforms_ColorOp[3] = { // None "", @@ -172,18 +161,59 @@ const char* gFS_Uniforms_HasRoundRectClip = "uniform vec4 roundRectInnerRectLTRB;\n" "uniform float roundRectRadius;\n"; +// Dithering must be done in the quantization space +// When we are writing to an sRGB framebuffer, we must do the following: +// EOCF(OECF(color) + dither) +// We approximate the transfer functions with gamma 2.0 to avoid branches and pow() +// The dithering pattern is generated with a triangle noise generator in the range [-0.0,1.0] +// TODO: Handle linear fp16 render targets +const char* gFS_Gradient_Functions = + "\nfloat triangleNoise(const highp vec2 n) {\n" + " highp vec2 p = fract(n * vec2(5.3987, 5.4421));\n" + " p += dot(p.yx, p.xy + vec2(21.5351, 14.3137));\n" + " highp float xy = p.x * p.y;\n" + " return fract(xy * 95.4307) + fract(xy * 75.04961) - 1.0;\n" + "}\n"; +const char* gFS_Gradient_Preamble[2] = { + // Linear framebuffer + "\nvec4 dither(const vec4 color) {\n" + " return vec4(color.rgb + (triangleNoise(gl_FragCoord.xy * screenSize.xy) / 255.0), color.a);\n" + "}\n" + "\nvec4 gammaMix(const vec4 a, const vec4 b, float v) {\n" + " vec4 c = pow(mix(a, b, v), vec4(vec3(1.0 / 2.2), 1.0));\n" + " return vec4(c.rgb * c.a, c.a);\n" + "}\n", + // sRGB framebuffer + "\nvec4 dither(const vec4 color) {\n" + " vec3 dithered = sqrt(color.rgb) + (triangleNoise(gl_FragCoord.xy * screenSize.xy) / 255.0);\n" + " return vec4(dithered * dithered, color.a);\n" + "}\n" + "\nvec4 gammaMix(const vec4 a, const vec4 b, float v) {\n" + " vec4 c = mix(a, b, v);\n" + " return vec4(c.rgb * c.a, c.a);\n" + "}\n" +}; + +// Uses luminance coefficients from Rec.709 to choose the appropriate gamma +// The gamma() function assumes that bright text will be displayed on a dark +// background and that dark text will be displayed on bright background +// The gamma coefficient is chosen to thicken or thin the text accordingly +// The dot product used to compute the luminance could be approximated with +// a simple max(color.r, color.g, color.b) +const char* gFS_Gamma_Preamble = + "\n#define GAMMA (%.2f)\n" + "#define GAMMA_INV (%.2f)\n" + "\nfloat gamma(float a, const vec3 color) {\n" + " float luminance = dot(color, vec3(0.2126, 0.7152, 0.0722));\n" + " return pow(a, luminance < 0.5 ? GAMMA_INV : GAMMA);\n" + "}\n"; + const char* gFS_Main = "\nvoid main(void) {\n" - " lowp vec4 fragColor;\n"; + " vec4 fragColor;\n"; -const char* gFS_Main_Dither[2] = { - // ES 2.0 - "texture2D(ditherSampler, ditherTexCoords).a * " STR(DITHER_KERNEL_SIZE_INV_SQUARE), - // ES 3.0 - "texture2D(ditherSampler, ditherTexCoords).a" -}; -const char* gFS_Main_AddDitherToGradient = - " gradientColor += %s;\n"; +const char* gFS_Main_AddDither = + " fragColor = dither(fragColor);\n"; // Fast cases const char* gFS_Fast_SingleColor = @@ -202,24 +232,32 @@ const char* gFS_Fast_SingleA8Texture = "\nvoid main(void) {\n" " gl_FragColor = texture2D(baseSampler, outTexCoords);\n" "}\n\n"; +const char* gFS_Fast_SingleA8Texture_ApplyGamma = + "\nvoid main(void) {\n" + " gl_FragColor = vec4(0.0, 0.0, 0.0, pow(texture2D(baseSampler, outTexCoords).a, GAMMA));\n" + "}\n\n"; const char* gFS_Fast_SingleModulateA8Texture = "\nvoid main(void) {\n" " gl_FragColor = color * texture2D(baseSampler, outTexCoords).a;\n" "}\n\n"; +const char* gFS_Fast_SingleModulateA8Texture_ApplyGamma = + "\nvoid main(void) {\n" + " gl_FragColor = color * gamma(texture2D(baseSampler, outTexCoords).a, color.rgb);\n" + "}\n\n"; const char* gFS_Fast_SingleGradient[2] = { "\nvoid main(void) {\n" - " gl_FragColor = %s + texture2D(gradientSampler, linear);\n" + " gl_FragColor = dither(texture2D(gradientSampler, linear));\n" "}\n\n", "\nvoid main(void) {\n" - " gl_FragColor = %s + mix(startColor, endColor, clamp(linear, 0.0, 1.0));\n" + " gl_FragColor = dither(gammaMix(startColor, endColor, clamp(linear, 0.0, 1.0)));\n" "}\n\n", }; const char* gFS_Fast_SingleModulateGradient[2] = { "\nvoid main(void) {\n" - " gl_FragColor = %s + color.a * texture2D(gradientSampler, linear);\n" + " gl_FragColor = dither(color.a * texture2D(gradientSampler, linear));\n" "}\n\n", "\nvoid main(void) {\n" - " gl_FragColor = %s + color.a * mix(startColor, endColor, clamp(linear, 0.0, 1.0));\n" + " gl_FragColor = dither(color.a * gammaMix(startColor, endColor, clamp(linear, 0.0, 1.0)));\n" "}\n\n" }; @@ -239,29 +277,31 @@ const char* gFS_Main_FetchTexture[2] = { // Modulate " fragColor = color * texture2D(baseSampler, outTexCoords);\n" }; -const char* gFS_Main_FetchA8Texture[2] = { +const char* gFS_Main_FetchA8Texture[4] = { // Don't modulate " fragColor = texture2D(baseSampler, outTexCoords);\n", + " fragColor = texture2D(baseSampler, outTexCoords);\n", // Modulate " fragColor = color * texture2D(baseSampler, outTexCoords).a;\n", + " fragColor = color * gamma(texture2D(baseSampler, outTexCoords).a, color.rgb);\n", }; const char* gFS_Main_FetchGradient[6] = { // Linear " vec4 gradientColor = texture2D(gradientSampler, linear);\n", - " vec4 gradientColor = mix(startColor, endColor, clamp(linear, 0.0, 1.0));\n", + " vec4 gradientColor = gammaMix(startColor, endColor, clamp(linear, 0.0, 1.0));\n", // Circular " vec4 gradientColor = texture2D(gradientSampler, vec2(length(circular), 0.5));\n", - " vec4 gradientColor = mix(startColor, endColor, clamp(length(circular), 0.0, 1.0));\n", + " vec4 gradientColor = gammaMix(startColor, endColor, clamp(length(circular), 0.0, 1.0));\n", // Sweep " highp float index = atan(sweep.y, sweep.x) * 0.15915494309; // inv(2 * PI)\n" " 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 = gammaMix(startColor, endColor, clamp(index - floor(index), 0.0, 1.0));\n" }; const char* gFS_Main_FetchBitmap = " vec4 bitmapColor = texture2D(bitmapSampler, outBitmapTexCoords);\n"; @@ -271,29 +311,38 @@ const char* gFS_Main_BlendShadersBG = " fragColor = blendShaders(gradientColor, bitmapColor)"; const char* gFS_Main_BlendShadersGB = " fragColor = blendShaders(bitmapColor, gradientColor)"; -const char* gFS_Main_BlendShaders_Modulate[3] = { +const char* gFS_Main_BlendShaders_Modulate[6] = { // Don't modulate ";\n", + ";\n", // Modulate " * 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[3] = { +const char* gFS_Main_GradientShader_Modulate[6] = { // Don't modulate " fragColor = gradientColor;\n", + " fragColor = gradientColor;\n", // Modulate " 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", }; -const char* gFS_Main_BitmapShader_Modulate[3] = { +const char* gFS_Main_BitmapShader_Modulate[6] = { // Don't modulate " fragColor = bitmapColor;\n", + " fragColor = bitmapColor;\n", // Modulate " 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"; @@ -385,7 +434,8 @@ const char* gBlendOps[18] = { /////////////////////////////////////////////////////////////////////////////// ProgramCache::ProgramCache(Extensions& extensions) - : mHasES3(extensions.getMajorGlVersion() >= 3) { + : mHasES3(extensions.getMajorGlVersion() >= 3) + , mHasSRGB(extensions.hasSRGB()) { } ProgramCache::~ProgramCache() { @@ -518,6 +568,7 @@ String8 ProgramCache::generateVertexShader(const ProgramDescription& description 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]); return description.hasAlpha8Texture; } @@ -525,11 +576,12 @@ static bool shaderOp(const ProgramDescription& description, String8& shader, String8 ProgramCache::generateFragmentShader(const ProgramDescription& description) { String8 shader(gFS_Header_Start); - const bool blendFramebuffer = description.framebufferMode >= SkXfermode::kPlus_Mode; + const bool blendFramebuffer = description.framebufferMode >= SkBlendMode::kPlus; if (blendFramebuffer) { shader.append(gFS_Header_Extension_FramebufferFetch); } - if (description.hasExternalTexture) { + if (description.hasExternalTexture + || (description.hasBitmap && description.isShaderBitmapExternal)) { shader.append(gFS_Header_Extension_ExternalTexture); } @@ -570,13 +622,16 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti shader.append(gFS_Uniforms_ExternalTextureSampler); } if (description.hasGradient) { - shader.appendFormat(gFS_Uniforms_GradientSampler[description.isSimpleGradient], - gFS_Uniforms_Dither); + shader.append(gFS_Uniforms_GradientSampler[description.isSimpleGradient]); } if (description.hasRoundRectClip) { shader.append(gFS_Uniforms_HasRoundRectClip); } + if (description.hasGammaCorrection) { + shader.appendFormat(gFS_Gamma_Preamble, Properties::textGamma, 1.0f / Properties::textGamma); + } + // Optimization for common cases if (!description.hasVertexAlpha && !blendFramebuffer @@ -607,18 +662,26 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti fast = true; } else if (singleA8Texture) { if (!description.modulate) { - shader.append(gFS_Fast_SingleA8Texture); + if (description.hasGammaCorrection) { + shader.append(gFS_Fast_SingleA8Texture_ApplyGamma); + } else { + shader.append(gFS_Fast_SingleA8Texture); + } } else { - shader.append(gFS_Fast_SingleModulateA8Texture); + if (description.hasGammaCorrection) { + shader.append(gFS_Fast_SingleModulateA8Texture_ApplyGamma); + } else { + shader.append(gFS_Fast_SingleModulateA8Texture); + } } fast = true; } else if (singleGradient) { + shader.append(gFS_Gradient_Functions); + shader.append(gFS_Gradient_Preamble[mHasSRGB]); if (!description.modulate) { - shader.appendFormat(gFS_Fast_SingleGradient[description.isSimpleGradient], - gFS_Main_Dither[mHasES3]); + shader.append(gFS_Fast_SingleGradient[description.isSimpleGradient]); } else { - shader.appendFormat(gFS_Fast_SingleModulateGradient[description.isSimpleGradient], - gFS_Main_Dither[mHasES3]); + shader.append(gFS_Fast_SingleModulateGradient[description.isSimpleGradient]); } fast = true; } @@ -635,7 +698,11 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti } if (description.hasBitmap) { - shader.append(gFS_Uniforms_BitmapSampler); + if (description.isShaderBitmapExternal) { + shader.append(gFS_Uniforms_BitmapExternalSampler); + } else { + shader.append(gFS_Uniforms_BitmapSampler); + } } shader.append(gFS_Uniforms_ColorOp[static_cast<int>(description.colorOp)]); @@ -649,9 +716,13 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti if (blendFramebuffer) { generateBlend(shader, "blendFramebuffer", description.framebufferMode); } - if (description.isBitmapNpot) { + if (description.useShaderBasedWrap) { generateTextureWrap(shader, description.bitmapWrapS, description.bitmapWrapT); } + if (description.hasGradient) { + shader.append(gFS_Gradient_Functions); + shader.append(gFS_Gradient_Preamble[mHasSRGB]); + } // Begin the shader shader.append(gFS_Main); { @@ -659,7 +730,8 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti if (description.hasTexture || description.hasExternalTexture) { if (description.hasAlpha8Texture) { if (!description.hasGradient && !description.hasBitmap) { - shader.append(gFS_Main_FetchA8Texture[modulateOp]); + shader.append( + gFS_Main_FetchA8Texture[modulateOp * 2 + description.hasGammaCorrection]); } } else { shader.append(gFS_Main_FetchTexture[modulateOp]); @@ -671,10 +743,9 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti } if (description.hasGradient) { shader.append(gFS_Main_FetchGradient[gradientIndex(description)]); - shader.appendFormat(gFS_Main_AddDitherToGradient, gFS_Main_Dither[mHasES3]); } if (description.hasBitmap) { - if (!description.isBitmapNpot) { + if (!description.useShaderBasedWrap) { shader.append(gFS_Main_FetchBitmap); } else { shader.append(gFS_Main_FetchBitmapNpot); @@ -715,6 +786,10 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti } } + if (description.hasGradient) { + shader.append(gFS_Main_AddDither); + } + // Output the fragment if (!blendFramebuffer) { shader.append(gFS_Main_FragColor); @@ -743,12 +818,12 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti return shader; } -void ProgramCache::generateBlend(String8& shader, const char* name, SkXfermode::Mode mode) { +void ProgramCache::generateBlend(String8& shader, const char* name, SkBlendMode mode) { shader.append("\nvec4 "); shader.append(name); shader.append("(vec4 src, vec4 dst) {\n"); shader.append(" "); - shader.append(gBlendOps[mode]); + shader.append(gBlendOps[(int)mode]); shader.append("}\n"); } diff --git a/libs/hwui/ProgramCache.h b/libs/hwui/ProgramCache.h index 9ac885b665e7..c2f715de70c3 100644 --- a/libs/hwui/ProgramCache.h +++ b/libs/hwui/ProgramCache.h @@ -51,7 +51,7 @@ private: Program* generateProgram(const ProgramDescription& description, programid key); String8 generateVertexShader(const ProgramDescription& description); String8 generateFragmentShader(const ProgramDescription& description); - void generateBlend(String8& shader, const char* name, SkXfermode::Mode mode); + void generateBlend(String8& shader, const char* name, SkBlendMode mode); void generateTextureWrap(String8& shader, GLenum wrapS, GLenum wrapT); void printLongString(const String8& shader) const; @@ -59,6 +59,7 @@ private: std::map<programid, std::unique_ptr<Program>> mCache; const bool mHasES3; + const bool mHasSRGB; }; // class ProgramCache }; // namespace uirenderer diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp index c1e2e5e3058a..09e34bf5ca15 100644 --- a/libs/hwui/Properties.cpp +++ b/libs/hwui/Properties.cpp @@ -63,8 +63,10 @@ int Properties::overrideSpotShadowStrength = -1; ProfileType Properties::sProfileType = ProfileType::None; bool Properties::sDisableProfileBars = false; +RenderPipelineType Properties::sRenderPipelineType = RenderPipelineType::NotInitialized; bool Properties::waitForGpuCompletion = false; +bool Properties::forceDrawFrame = false; bool Properties::filterOutTestOverhead = false; @@ -204,5 +206,33 @@ ProfileType Properties::getProfileType() { return sProfileType; } +RenderPipelineType Properties::getRenderPipelineType() { + if (RenderPipelineType::NotInitialized != sRenderPipelineType) { + return sRenderPipelineType; + } + char prop[PROPERTY_VALUE_MAX]; + property_get(PROPERTY_DEFAULT_RENDERER, prop, "opengl"); + if (!strcmp(prop, "skiagl") ) { + sRenderPipelineType = RenderPipelineType::SkiaGL; + } else if (!strcmp(prop, "skiavk") ) { + sRenderPipelineType = RenderPipelineType::SkiaVulkan; + } else { //"opengl" + sRenderPipelineType = RenderPipelineType::OpenGL; + } + return sRenderPipelineType; +} + +#ifdef HWUI_GLES_WRAP_ENABLED +void Properties::overrideRenderPipelineType(RenderPipelineType type) { + sRenderPipelineType = type; +} +#endif + +bool Properties::isSkiaEnabled() { + auto renderType = getRenderPipelineType(); + return RenderPipelineType::SkiaGL == renderType + || RenderPipelineType::SkiaVulkan == renderType; +} + }; // namespace uirenderer }; // namespace android diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h index 881bd5f57714..6dc0cb30a25a 100644 --- a/libs/hwui/Properties.h +++ b/libs/hwui/Properties.h @@ -20,8 +20,7 @@ #include <cutils/properties.h> /** - * This file contains the list of system properties used to configure - * the OpenGLRenderer. + * This file contains the list of system properties used to configure libhwui. */ namespace android { @@ -153,6 +152,12 @@ enum DebugLevel { #define PROPERTY_FILTER_TEST_OVERHEAD "debug.hwui.filter_test_overhead" +/** + * Allows to set rendering pipeline mode to OpenGL (default), Skia OpenGL + * or Vulkan. + */ +#define PROPERTY_DEFAULT_RENDERER "debug.hwui.default_renderer" + /////////////////////////////////////////////////////////////////////////////// // Runtime configuration properties /////////////////////////////////////////////////////////////////////////////// @@ -161,7 +166,7 @@ enum DebugLevel { * Used to enable/disable scissor optimization. The accepted values are * "true" and "false". The default value is "false". * - * When scissor optimization is enabled, OpenGLRenderer will attempt to + * When scissor optimization is enabled, libhwui will attempt to * minimize the use of scissor by selectively enabling and disabling the * GL scissor test. * When the optimization is disabled, OpenGLRenderer will keep the GL @@ -198,7 +203,7 @@ enum DebugLevel { #define PROPERTY_TEXT_LARGE_CACHE_WIDTH "ro.hwui.text_large_cache_width" #define PROPERTY_TEXT_LARGE_CACHE_HEIGHT "ro.hwui.text_large_cache_height" -// Gamma (>= 1.0, <= 10.0) +// Gamma (>= 1.0, <= 3.0) #define PROPERTY_TEXT_GAMMA "hwui.text_gamma" /////////////////////////////////////////////////////////////////////////////// @@ -217,7 +222,7 @@ enum DebugLevel { #define DEFAULT_TEXTURE_CACHE_FLUSH_RATE 0.6f -#define DEFAULT_TEXT_GAMMA 1.4f +#define DEFAULT_TEXT_GAMMA 1.45f // Match design tools // cap to 256 to limite paths in the path cache #define DEFAULT_PATH_TEXTURE_CAP 256 @@ -248,6 +253,13 @@ enum class StencilClipDebug { ShowRegion }; +enum class RenderPipelineType { + OpenGL = 0, + SkiaGL, + SkiaVulkan, + NotInitialized = 128 +}; + /** * Renderthread-only singleton which manages several static rendering properties. Most of these * are driven by system properties which are queried once at initialization, and again if init() @@ -295,18 +307,26 @@ public: static int overrideSpotShadowStrength; static ProfileType getProfileType(); + static RenderPipelineType getRenderPipelineType(); + static bool isSkiaEnabled(); // Should be used only by test apps static bool waitForGpuCompletion; + static bool forceDrawFrame; // Should only be set by automated tests to try and filter out // any overhead they add static bool filterOutTestOverhead; + // Used for testing only to change the render pipeline. +#ifdef HWUI_GLES_WRAP_ENABLED + static void overrideRenderPipelineType(RenderPipelineType); +#endif + private: static ProfileType sProfileType; static bool sDisableProfileBars; - + static RenderPipelineType sRenderPipelineType; }; // class Caches }; // namespace uirenderer diff --git a/libs/hwui/PropertyValuesHolder.cpp b/libs/hwui/PropertyValuesHolder.cpp index 6ba0ab59a88c..2a03e6a3ebc5 100644 --- a/libs/hwui/PropertyValuesHolder.cpp +++ b/libs/hwui/PropertyValuesHolder.cpp @@ -16,6 +16,7 @@ #include "PropertyValuesHolder.h" +#include "utils/Color.h" #include "utils/VectorDrawableUtils.h" #include <utils/Log.h> @@ -25,18 +26,26 @@ namespace uirenderer { using namespace VectorDrawable; -inline U8CPU lerp(U8CPU fromValue, U8CPU toValue, float fraction) { - return (U8CPU) (fromValue * (1 - fraction) + toValue * fraction); +inline constexpr float lerp(float fromValue, float toValue, float fraction) { + return float (fromValue * (1 - fraction) + toValue * fraction); +} + +inline constexpr float linearize(U8CPU component) { + return EOCF_sRGB(component / 255.0f); } // TODO: Add a test for this void ColorEvaluator::evaluate(SkColor* outColor, const SkColor& fromColor, const SkColor& toColor, float fraction) const { - U8CPU alpha = lerp(SkColorGetA(fromColor), SkColorGetA(toColor), fraction); - U8CPU red = lerp(SkColorGetR(fromColor), SkColorGetR(toColor), fraction); - U8CPU green = lerp(SkColorGetG(fromColor), SkColorGetG(toColor), fraction); - U8CPU blue = lerp(SkColorGetB(fromColor), SkColorGetB(toColor), fraction); - *outColor = SkColorSetARGB(alpha, red, green, blue); + 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)); } void PathEvaluator::evaluate(PathData* out, diff --git a/libs/hwui/Readback.h b/libs/hwui/Readback.h index a112c42988c0..b76395301a21 100644 --- a/libs/hwui/Readback.h +++ b/libs/hwui/Readback.h @@ -17,11 +17,13 @@ #pragma once #include "renderthread/RenderThread.h" +#include "Rect.h" #include <SkBitmap.h> #include <gui/Surface.h> namespace android { +class GraphicBuffer; namespace uirenderer { // Keep in sync with PixelCopy.java codes @@ -36,8 +38,18 @@ enum class CopyResult { class Readback { public: - static CopyResult copySurfaceInto(renderthread::RenderThread& renderThread, - Surface& surface, SkBitmap* bitmap); + /** + * 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 copyGraphicBufferInto(GraphicBuffer* graphicBuffer, SkBitmap* bitmap) = 0; + +protected: + explicit Readback(renderthread::RenderThread& thread) : mRenderThread(thread) {} + virtual ~Readback() {} + + renderthread::RenderThread& mRenderThread; }; } // namespace uirenderer diff --git a/libs/hwui/RecordedOp.h b/libs/hwui/RecordedOp.h index 5497f863b357..dea2be68c8db 100644 --- a/libs/hwui/RecordedOp.h +++ b/libs/hwui/RecordedOp.h @@ -14,20 +14,19 @@ * limitations under the License. */ -#ifndef ANDROID_HWUI_RECORDED_OP_H -#define ANDROID_HWUI_RECORDED_OP_H +#pragma once -#include "RecordedOp.h" #include "font/FontUtil.h" +#include "GlLayer.h" #include "Matrix.h" #include "Rect.h" #include "RenderNode.h" #include "TessellationCache.h" #include "utils/LinearAllocator.h" +#include "utils/PaintUtils.h" #include "Vector.h" #include <androidfw/ResourceTypes.h> -#include <SkXfermode.h> class SkBitmap; class SkPaint; @@ -213,15 +212,14 @@ struct ArcOp : RecordedOp { }; struct BitmapOp : RecordedOp { - BitmapOp(BASE_PARAMS, const SkBitmap* bitmap) + BitmapOp(BASE_PARAMS, Bitmap* bitmap) : SUPER(BitmapOp) , bitmap(bitmap) {} - const SkBitmap* bitmap; - // TODO: asset atlas/texture id lookup? + Bitmap* bitmap; }; struct BitmapMeshOp : RecordedOp { - BitmapMeshOp(BASE_PARAMS, const SkBitmap* bitmap, int meshWidth, int meshHeight, + BitmapMeshOp(BASE_PARAMS, Bitmap* bitmap, int meshWidth, int meshHeight, const float* vertices, const int* colors) : SUPER(BitmapMeshOp) , bitmap(bitmap) @@ -229,7 +227,7 @@ struct BitmapMeshOp : RecordedOp { , meshHeight(meshHeight) , vertices(vertices) , colors(colors) {} - const SkBitmap* bitmap; + Bitmap* bitmap; const int meshWidth; const int meshHeight; const float* vertices; @@ -237,11 +235,11 @@ struct BitmapMeshOp : RecordedOp { }; struct BitmapRectOp : RecordedOp { - BitmapRectOp(BASE_PARAMS, const SkBitmap* bitmap, const Rect& src) + BitmapRectOp(BASE_PARAMS, Bitmap* bitmap, const Rect& src) : SUPER(BitmapRectOp) , bitmap(bitmap) , src(src) {} - const SkBitmap* bitmap; + Bitmap* bitmap; const Rect src; }; @@ -259,12 +257,12 @@ struct CirclePropsOp : RecordedOp { struct ColorOp : RecordedOp { // Note: unbounded op that will fillclip, so no bounds/matrix needed - ColorOp(const ClipBase* localClip, int color, SkXfermode::Mode mode) + ColorOp(const ClipBase* localClip, int color, SkBlendMode mode) : RecordedOp(RecordedOpId::ColorOp, Rect(), Matrix4::identity(), localClip, nullptr) , color(color) , mode(mode) {} const int color; - const SkXfermode::Mode mode; + const SkBlendMode mode; }; struct FunctorOp : RecordedOp { @@ -291,11 +289,11 @@ struct OvalOp : RecordedOp { }; struct PatchOp : RecordedOp { - PatchOp(BASE_PARAMS, const SkBitmap* bitmap, const Res_png_9patch* patch) + PatchOp(BASE_PARAMS, Bitmap* bitmap, const Res_png_9patch* patch) : SUPER(PatchOp) , bitmap(bitmap) , patch(patch) {} - const SkBitmap* bitmap; + Bitmap* bitmap; const Res_png_9patch* patch; }; @@ -416,7 +414,7 @@ struct TextOnPathOp : RecordedOp { }; struct TextureLayerOp : RecordedOp { - TextureLayerOp(BASE_PARAMS_PAINTLESS, Layer* layer) + TextureLayerOp(BASE_PARAMS_PAINTLESS, GlLayer* layer) : SUPER_PAINTLESS(TextureLayerOp) , layer(layer) {} @@ -427,7 +425,7 @@ struct TextureLayerOp : RecordedOp { , layer(op.layer) { } - Layer* layer; + GlLayer* layer; }; //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -506,7 +504,7 @@ struct LayerOp : RecordedOp { : SUPER_PAINTLESS(LayerOp) , layerHandle(layerHandle) , alpha(paint ? paint->getAlpha() / 255.0f : 1.0f) - , mode(PaintUtils::getXfermodeDirect(paint)) + , mode(PaintUtils::getBlendModeDirect(paint)) , colorFilter(paint ? paint->getColorFilter() : nullptr) {} explicit LayerOp(RenderNode& node) @@ -520,7 +518,7 @@ struct LayerOp : RecordedOp { // constructed until after this operation is constructed. OffscreenBuffer** layerHandle; const float alpha; - const SkXfermode::Mode mode; + const SkBlendMode mode; // pointer to object owned by either LayerProperties, or a recorded Paint object in a // BeginLayerOp. Lives longer than LayerOp in either case, so no skia ref counting is used. @@ -529,5 +527,3 @@ struct LayerOp : RecordedOp { }; // namespace uirenderer }; // namespace android - -#endif // ANDROID_HWUI_RECORDED_OP_H diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp index 0c552bac1d7f..b5e5d6801f99 100644 --- a/libs/hwui/RecordingCanvas.cpp +++ b/libs/hwui/RecordingCanvas.cpp @@ -20,6 +20,7 @@ #include "RecordedOp.h" #include "RenderNode.h" #include "VectorDrawable.h" +#include "hwui/MinikinUtils.h" namespace android { namespace uirenderer { @@ -35,7 +36,7 @@ RecordingCanvas::~RecordingCanvas() { "Destroyed a RecordingCanvas during a record!"); } -void RecordingCanvas::resetRecording(int width, int height) { +void RecordingCanvas::resetRecording(int width, int height, RenderNode* node) { LOG_ALWAYS_FATAL_IF(mDisplayList, "prepareDirty called a second time during a recording!"); mDisplayList = new DisplayList(); @@ -43,7 +44,6 @@ void RecordingCanvas::resetRecording(int width, int height) { mState.initializeRecordingSaveStack(width, height); mDeferredBarrierType = DeferredBarrierType::InOrder; - mState.setDirtyClip(false); } DisplayList* RecordingCanvas::finishRecording() { @@ -234,20 +234,17 @@ bool RecordingCanvas::quickRejectPath(const SkPath& path) const { SkRect bounds = path.getBounds(); return mState.quickRejectConservative(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom); } -bool RecordingCanvas::clipRect(float left, float top, float right, float bottom, SkRegion::Op op) { +bool RecordingCanvas::clipRect(float left, float top, float right, float bottom, SkClipOp op) { return mState.clipRect(left, top, right, bottom, op); } -bool RecordingCanvas::clipPath(const SkPath* path, SkRegion::Op op) { +bool RecordingCanvas::clipPath(const SkPath* path, SkClipOp op) { return mState.clipPath(path, op); } -bool RecordingCanvas::clipRegion(const SkRegion* region, SkRegion::Op op) { - return mState.clipRegion(region, op); -} // ---------------------------------------------------------------------------- // android/graphics/Canvas draw operations // ---------------------------------------------------------------------------- -void RecordingCanvas::drawColor(int color, SkXfermode::Mode mode) { +void RecordingCanvas::drawColor(int color, SkBlendMode mode) { addOp(alloc().create_trivial<ColorOp>( getRecordedClip(), color, @@ -468,17 +465,17 @@ void RecordingCanvas::drawVectorDrawable(VectorDrawableRoot* tree) { } // Bitmap-based -void RecordingCanvas::drawBitmap(const SkBitmap& bitmap, float left, float top, const SkPaint* paint) { +void RecordingCanvas::drawBitmap(Bitmap& bitmap, float left, float top, const SkPaint* paint) { save(SaveFlags::Matrix); translate(left, top); - drawBitmap(&bitmap, paint); + drawBitmap(bitmap, paint); restore(); } -void RecordingCanvas::drawBitmap(const SkBitmap& bitmap, const SkMatrix& matrix, +void RecordingCanvas::drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, const SkPaint* paint) { if (matrix.isIdentity()) { - drawBitmap(&bitmap, paint); + drawBitmap(bitmap, paint); } else if (!(matrix.getType() & ~(SkMatrix::kScale_Mask | SkMatrix::kTranslate_Mask)) && MathUtils::isPositive(matrix.getScaleX()) && MathUtils::isPositive(matrix.getScaleY())) { @@ -492,12 +489,12 @@ void RecordingCanvas::drawBitmap(const SkBitmap& bitmap, const SkMatrix& matrix, } else { save(SaveFlags::Matrix); concat(matrix); - drawBitmap(&bitmap, paint); + drawBitmap(bitmap, paint); restore(); } } -void RecordingCanvas::drawBitmap(const SkBitmap& bitmap, float srcLeft, float srcTop, +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 @@ -508,7 +505,7 @@ void RecordingCanvas::drawBitmap(const SkBitmap& bitmap, float srcLeft, float sr // transform simple rect to rect drawing case into position bitmap ops, since they merge save(SaveFlags::Matrix); translate(dstLeft, dstTop); - drawBitmap(&bitmap, paint); + drawBitmap(bitmap, paint); restore(); } else { addOp(alloc().create_trivial<BitmapRectOp>( @@ -520,7 +517,7 @@ void RecordingCanvas::drawBitmap(const SkBitmap& bitmap, float srcLeft, float sr } } -void RecordingCanvas::drawBitmapMesh(const SkBitmap& bitmap, int meshWidth, int meshHeight, +void RecordingCanvas::drawBitmapMesh(Bitmap& bitmap, int meshWidth, int meshHeight, const float* vertices, const int* colors, const SkPaint* paint) { int vertexCount = (meshWidth + 1) * (meshHeight + 1); addOp(alloc().create_trivial<BitmapMeshOp>( @@ -532,7 +529,7 @@ void RecordingCanvas::drawBitmapMesh(const SkBitmap& bitmap, int meshWidth, int refBuffer<int>(colors, vertexCount))); // 1 color per vertex } -void RecordingCanvas::drawNinePatch(const SkBitmap& bitmap, const android::Res_png_9patch& patch, +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>( @@ -559,22 +556,28 @@ void RecordingCanvas::drawGlyphs(const uint16_t* glyphs, const float* positions, drawTextDecorations(x, y, totalAdvance, paint); } -void RecordingCanvas::drawGlyphsOnPath(const uint16_t* glyphs, int glyphCount, const SkPath& path, - float hOffset, float vOffset, const SkPaint& paint) { - if (!glyphs || glyphCount <= 0 || PaintUtils::paintWillNotDrawText(paint)) return; - glyphs = refBuffer<glyph_t>(glyphs, glyphCount); - addOp(alloc().create_trivial<TextOnPathOp>( - *(mState.currentSnapshot()->transform), - getRecordedClip(), - refPaint(&paint), glyphs, glyphCount, refPath(&path), hOffset, vOffset)); +void RecordingCanvas::drawLayoutOnPath(const minikin::Layout& layout, float hOffset, float vOffset, + 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); + float x = hOffset + layout.getX(i); + float y = vOffset + layout.getY(i); + if (PaintUtils::paintWillNotDrawText(paint)) 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)); + } } -void RecordingCanvas::drawBitmap(const SkBitmap* bitmap, const SkPaint* paint) { +void RecordingCanvas::drawBitmap(Bitmap& bitmap, const SkPaint* paint) { addOp(alloc().create_trivial<BitmapOp>( - Rect(bitmap->width(), bitmap->height()), + Rect(bitmap.width(), bitmap.height()), *(mState.currentSnapshot()->transform), getRecordedClip(), - refPaint(paint), refBitmap(*bitmap))); + refPaint(paint), refBitmap(bitmap))); } void RecordingCanvas::drawRenderNode(RenderNode* renderNode) { @@ -603,13 +606,14 @@ void RecordingCanvas::drawLayer(DeferredLayerUpdater* layerHandle) { // We ref the DeferredLayerUpdater due to its thread-safe ref-counting semantics. mDisplayList->ref(layerHandle); + LOG_ALWAYS_FATAL_IF(layerHandle->backingLayer()->getApi() != Layer::Api::OpenGL); // Note that the backing layer has *not* yet been updated, so don't trust // its width, height, transform, etc...! addOp(alloc().create_trivial<TextureLayerOp>( Rect(layerHandle->getWidth(), layerHandle->getHeight()), *(mState.currentSnapshot()->transform), getRecordedClip(), - layerHandle->backingLayer())); + static_cast<GlLayer*>(layerHandle->backingLayer()))); } void RecordingCanvas::callDrawGLFunction(Functor* functor, @@ -660,7 +664,8 @@ void RecordingCanvas::refBitmapsInShader(const SkShader* shader) { SkBitmap bitmap; SkShader::TileMode xy[2]; if (shader->isABitmap(&bitmap, nullptr, xy)) { - refBitmap(bitmap); + Bitmap* hwuiBitmap = static_cast<Bitmap*>(bitmap.pixelRef()); + refBitmap(*hwuiBitmap); return; } SkShader::ComposeRec rec; diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h index 337e97bf450b..44181bd22397 100644 --- a/libs/hwui/RecordingCanvas.h +++ b/libs/hwui/RecordingCanvas.h @@ -22,10 +22,10 @@ #include "ResourceCache.h" #include "SkiaCanvasProxy.h" #include "Snapshot.h" +#include "hwui/Bitmap.h" #include "hwui/Canvas.h" #include "utils/LinearAllocator.h" #include "utils/Macros.h" -#include "utils/NinePatch.h" #include <SkDrawFilter.h> #include <SkPaint.h> @@ -50,7 +50,7 @@ public: RecordingCanvas(size_t width, size_t height); virtual ~RecordingCanvas(); - virtual void resetRecording(int width, int height) override; + virtual void resetRecording(int width, int height, RenderNode* node = nullptr) override; virtual WARN_UNUSED_RESULT DisplayList* finishRecording() override; // ---------------------------------------------------------------------------- // MISC HWUI OPERATIONS - TODO: CATEGORIZE @@ -131,9 +131,9 @@ 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, SkRegion::Op op) override; - virtual bool clipPath(const SkPath* path, SkRegion::Op op) override; - virtual bool clipRegion(const SkRegion* region, SkRegion::Op 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 virtual SkDrawFilter* getDrawFilter() override { return mDrawFilter.get(); } @@ -144,7 +144,7 @@ public: // ---------------------------------------------------------------------------- // android/graphics/Canvas draw operations // ---------------------------------------------------------------------------- - virtual void drawColor(int color, SkXfermode::Mode mode) override; + virtual void drawColor(int color, SkBlendMode mode) override; virtual void drawPaint(const SkPaint& paint) override; // Geometry @@ -176,15 +176,14 @@ public: virtual void drawVectorDrawable(VectorDrawableRoot* tree) override; // Bitmap-based - virtual void drawBitmap(const SkBitmap& bitmap, float left, float top, const SkPaint* paint) override; - virtual void drawBitmap(const SkBitmap& bitmap, const SkMatrix& matrix, - const SkPaint* paint) override; - virtual void drawBitmap(const SkBitmap& bitmap, float srcLeft, float srcTop, + 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 drawBitmapMesh(const SkBitmap& bitmap, int meshWidth, int meshHeight, + virtual void drawBitmapMesh(Bitmap& bitmap, int meshWidth, int meshHeight, const float* vertices, const int* colors, const SkPaint* paint) override; - virtual void drawNinePatch(const SkBitmap& bitmap, const android::Res_png_9patch& chunk, + virtual void drawNinePatch(Bitmap& bitmap, const android::Res_png_9patch& chunk, float dstLeft, float dstTop, float dstRight, float dstBottom, const SkPaint* paint) override; @@ -196,15 +195,15 @@ protected: const SkPaint& paint, float x, float y, float boundsLeft, float boundsTop, float boundsRight, float boundsBottom, float totalAdvance) override; - virtual void drawGlyphsOnPath(const uint16_t* glyphs, int count, const SkPath& path, - float hOffset, float vOffset, const SkPaint& paint) 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; private: const ClipBase* getRecordedClip() { return mState.writableSnapshot()->mutateClipArea().serializeClip(alloc()); } - void drawBitmap(const SkBitmap* bitmap, const SkPaint* paint); + void drawBitmap(Bitmap& bitmap, const SkPaint* paint); void drawSimpleRects(const float* rects, int vertexCount, const SkPaint* paint); @@ -286,14 +285,17 @@ private: return cachedRegion; } - inline const SkBitmap* refBitmap(const SkBitmap& bitmap) { + inline Bitmap* refBitmap(Bitmap& bitmap) { // Note that this assumes the bitmap is immutable. There are cases this won't handle // correctly, such as creating the bitmap from scratch, drawing with it, changing its // contents, and drawing again. The only fix would be to always copy it the first time, // which doesn't seem worth the extra cycles for this unlikely case. - SkBitmap* localBitmap = alloc().create<SkBitmap>(bitmap); - mDisplayList->bitmapResources.push_back(localBitmap); - return localBitmap; + + // this is required because sk_sp's ctor adopts the pointer, + // but does not increment the refcount, + bitmap.ref(); + mDisplayList->bitmapResources.emplace_back(&bitmap); + return &bitmap; } inline const Res_png_9patch* refPatch(const Res_png_9patch* patch) { @@ -313,7 +315,7 @@ private: const ClipBase* mDeferredBarrierClip = nullptr; DisplayList* mDisplayList = nullptr; bool mHighContrastText = false; - SkAutoTUnref<SkDrawFilter> mDrawFilter; + sk_sp<SkDrawFilter> mDrawFilter; }; // class RecordingCanvas }; // namespace uirenderer diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp index bdcad798f05e..a5443d9ff6f1 100644 --- a/libs/hwui/RenderNode.cpp +++ b/libs/hwui/RenderNode.cpp @@ -16,19 +16,17 @@ #include "RenderNode.h" +#include "BakedOpRenderer.h" #include "DamageAccumulator.h" #include "Debug.h" -#if HWUI_NEW_OPS -#include "BakedOpRenderer.h" -#include "RecordedOp.h" #include "OpDumper.h" -#endif -#include "DisplayListOp.h" -#include "LayerRenderer.h" -#include "OpenGLRenderer.h" +#include "RecordedOp.h" #include "TreeInfo.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" @@ -41,23 +39,6 @@ namespace android { namespace uirenderer { -void RenderNode::debugDumpLayers(const char* prefix) { -#if HWUI_NEW_OPS - LOG_ALWAYS_FATAL("TODO: dump layer"); -#else - if (mLayer) { - ALOGD("%sNode %p (%s) has layer %p (fbo = %u, wasBuildLayered = %s)", - prefix, this, getName(), mLayer, mLayer->getFbo(), - mLayer->wasBuildLayered ? "true" : "false"); - } -#endif - if (mDisplayList) { - for (auto&& child : mDisplayList->getChildren()) { - child->renderNode->debugDumpLayers(prefix); - } - } -} - RenderNode::RenderNode() : mDirtyPropertyFields(0) , mNeedsDisplayListSync(false) @@ -70,15 +51,7 @@ RenderNode::RenderNode() RenderNode::~RenderNode() { deleteDisplayList(nullptr); delete mStagingDisplayList; -#if HWUI_NEW_OPS - LOG_ALWAYS_FATAL_IF(mLayer, "layer missed detachment!"); -#else - if (mLayer) { - ALOGW("Memory Warning: Layer %p missed its detachment, held on to for far too long!", mLayer); - mLayer->postDecStrong(); - mLayer = nullptr; - } -#endif + LOG_ALWAYS_FATAL_IF(hasLayer(), "layer missed detachment!"); } void RenderNode::setStagingDisplayList(DisplayList* displayList, TreeObserver* observer) { @@ -96,53 +69,37 @@ void RenderNode::setStagingDisplayList(DisplayList* displayList, TreeObserver* o * This function is a simplified version of replay(), where we simply retrieve and log the * display list. This function should remain in sync with the replay() function. */ -#if HWUI_NEW_OPS -void RenderNode::output(uint32_t level, const char* label) { - ALOGD("%s (%s %p%s%s%s%s%s)", - label, - getName(), - this, - (MathUtils::isZero(properties().getAlpha()) ? ", zero alpha" : ""), - (properties().hasShadow() ? ", casting shadow" : ""), - (isRenderable() ? "" : ", empty"), - (properties().getProjectBackwards() ? ", projected" : ""), - (mLayer != nullptr ? ", on HW Layer" : "")); - properties().debugOutputProperties(level + 1); +void RenderNode::output() { + LogcatStream strout; + strout << "Root"; + output(strout, 0); +} + +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; + + properties().debugOutputProperties(output, level + 1); if (mDisplayList) { for (auto&& op : mDisplayList->getOps()) { - std::stringstream strout; - OpDumper::dump(*op, strout, level + 1); + OpDumper::dump(*op, output, level + 1); if (op->opId == RecordedOpId::RenderNodeOp) { auto rnOp = reinterpret_cast<const RenderNodeOp*>(op); - rnOp->renderNode->output(level + 1, strout.str().c_str()); + rnOp->renderNode->output(output, level + 1); } else { - ALOGD("%s", strout.str().c_str()); + output << std::endl; } } } - ALOGD("%*s/RenderNode(%s %p)", level * 2, "", getName(), this); + output << std::string(level * 2, ' ') << "/RenderNode(" << getName() << " " << this << ")"; + output << std::endl; } -#else -void RenderNode::output(uint32_t level) { - ALOGD("%*sStart display list (%p, %s%s%s%s%s%s)", (level - 1) * 2, "", this, - getName(), - (MathUtils::isZero(properties().getAlpha()) ? ", zero alpha" : ""), - (properties().hasShadow() ? ", casting shadow" : ""), - (isRenderable() ? "" : ", empty"), - (properties().getProjectBackwards() ? ", projected" : ""), - (mLayer != nullptr ? ", on HW Layer" : "")); - ALOGD("%*s%s %d", level * 2, "", "Save", SaveFlags::MatrixClip); - properties().debugOutputProperties(level); - if (mDisplayList) { - // TODO: consider printing the chunk boundaries here - for (auto&& op : mDisplayList->getOps()) { - op->output(level, DisplayListOp::kOpLogFlag_Recurse); - } - } - ALOGD("%*sDone (%p, %s)", (level - 1) * 2, "", this, getName()); - } -#endif void RenderNode::copyTo(proto::RenderNode *pnode) { pnode->set_id(static_cast<uint64_t>( @@ -231,8 +188,9 @@ void RenderNode::prepareTree(TreeInfo& info) { ATRACE_CALL(); LOG_ALWAYS_FATAL_IF(!info.damageAccumulator, "DamageAccumulator missing"); - // Functors don't correctly handle stencil usage of overdraw debugging - shove 'em in a layer. - bool functorsNeedLayer = Properties::debugOverdraw; + // The OpenGL renderer reserves the stencil buffer for overdraw debugging. Functors + // will need to be drawn in a layer. + bool functorsNeedLayer = Properties::debugOverdraw && !Properties::isSkiaEnabled(); prepareTreeImpl(info, functorsNeedLayer); } @@ -272,31 +230,6 @@ void RenderNode::prepareLayer(TreeInfo& info, uint32_t dirtyMask) { } } -static layer_t* createLayer(RenderState& renderState, uint32_t width, uint32_t height) { -#if HWUI_NEW_OPS - return renderState.layerPool().get(renderState, width, height); -#else - return LayerRenderer::createRenderLayer(renderState, width, height); -#endif -} - -static void destroyLayer(layer_t* layer) { -#if HWUI_NEW_OPS - RenderState& renderState = layer->renderState; - renderState.layerPool().putOrDelete(layer); -#else - LayerRenderer::destroyLayer(layer); -#endif -} - -static bool layerMatchesWidthAndHeight(layer_t* layer, int width, int height) { -#if HWUI_NEW_OPS - return layer->viewportWidth == (uint32_t) width && layer->viewportHeight == (uint32_t)height; -#else - return layer->layer.getWidth() == width && layer->layer.getHeight() == height; -#endif -} - void RenderNode::pushLayerUpdate(TreeInfo& info) { LayerType layerType = properties().effectiveLayerType(); // If we are not a layer OR we cannot be rendered (eg, view was detached) @@ -305,43 +238,17 @@ void RenderNode::pushLayerUpdate(TreeInfo& info) { || CC_UNLIKELY(!isRenderable()) || CC_UNLIKELY(properties().getWidth() == 0) || CC_UNLIKELY(properties().getHeight() == 0)) { - if (CC_UNLIKELY(mLayer)) { - destroyLayer(mLayer); - mLayer = nullptr; + if (CC_UNLIKELY(hasLayer())) { + renderthread::CanvasContext::destroyLayer(this); } return; } - bool transformUpdateNeeded = false; - if (!mLayer) { - mLayer = createLayer(info.canvasContext.getRenderState(), getWidth(), getHeight()); -#if !HWUI_NEW_OPS - applyLayerPropertiesToLayer(info); -#endif + if(info.canvasContext.createOrUpdateLayer(this, *info.damageAccumulator)) { damageSelf(info); - transformUpdateNeeded = true; - } else if (!layerMatchesWidthAndHeight(mLayer, getWidth(), getHeight())) { -#if HWUI_NEW_OPS - // TODO: remove now irrelevant, currently enqueued damage (respecting damage ordering) - // Or, ideally, maintain damage between frames on node/layer so ordering is always correct - RenderState& renderState = mLayer->renderState; - if (properties().fitsOnLayer()) { - mLayer = renderState.layerPool().resize(mLayer, getWidth(), getHeight()); - } else { -#else - if (!LayerRenderer::resizeLayer(mLayer, getWidth(), getHeight())) { -#endif - destroyLayer(mLayer); - mLayer = nullptr; - } - damageSelf(info); - transformUpdateNeeded = true; } - SkRect dirty; - info.damageAccumulator->peekAtDirty(&dirty); - - if (!mLayer) { + if (!hasLayer()) { Caches::getInstance().dumpMemoryUsage(); if (info.errorHandler) { std::ostringstream err; @@ -358,26 +265,9 @@ void RenderNode::pushLayerUpdate(TreeInfo& info) { return; } - if (transformUpdateNeeded && mLayer) { - // update the transform in window of the layer to reset its origin wrt light source position - Matrix4 windowTransform; - info.damageAccumulator->computeCurrentTransform(&windowTransform); - mLayer->setWindowTransform(windowTransform); - } - -#if HWUI_NEW_OPS + SkRect dirty; + info.damageAccumulator->peekAtDirty(&dirty); info.layerUpdateQueue->enqueueLayerWithDamage(this, dirty); -#else - if (dirty.intersect(0, 0, getWidth(), getHeight())) { - dirty.roundOut(&dirty); - mLayer->updateDeferred(this, dirty.fLeft, dirty.fTop, dirty.fRight, dirty.fBottom); - } - // This is not inside the above if because we may have called - // updateDeferred on a previous prepare pass that didn't have a renderer - if (info.renderer && mLayer->deferredUpdateScheduled) { - info.renderer->pushLayerUpdate(mLayer); - } -#endif // There might be prefetched layers that need to be accounted for. // That might be us, so tell CanvasContext that this layer is in the @@ -406,9 +296,9 @@ void RenderNode::prepareTreeImpl(TreeInfo& info, bool functorsNeedLayer) { bool willHaveFunctor = false; if (info.mode == TreeInfo::MODE_FULL && mStagingDisplayList) { - willHaveFunctor = !mStagingDisplayList->getFunctors().empty(); + willHaveFunctor = mStagingDisplayList->hasFunctor(); } else if (mDisplayList) { - willHaveFunctor = !mDisplayList->getFunctors().empty(); + willHaveFunctor = mDisplayList->hasFunctor(); } bool childFunctorsNeedLayer = mProperties.prepareForFunctorPresence( willHaveFunctor, functorsNeedLayer); @@ -421,15 +311,15 @@ void RenderNode::prepareTreeImpl(TreeInfo& info, bool functorsNeedLayer) { if (info.mode == TreeInfo::MODE_FULL) { pushStagingDisplayListChanges(info); } - prepareSubTree(info, childFunctorsNeedLayer, mDisplayList); if (mDisplayList) { - for (auto& vectorDrawable : mDisplayList->getVectorDrawables()) { - // If any vector drawable in the display list needs update, damage the node. - if (vectorDrawable->isDirty()) { - damageSelf(info); - } - vectorDrawable->setPropertyChangeWillBeConsumed(true); + info.out.hasFunctors |= mDisplayList->hasFunctor(); + bool isDirty = mDisplayList->prepareListAndChildren(info, childFunctorsNeedLayer, + [](RenderNode* child, TreeInfo& info, bool functorsNeedLayer) { + child->prepareTreeImpl(info, functorsNeedLayer); + }); + if (isDirty) { + damageSelf(info); } } pushLayerUpdate(info); @@ -453,9 +343,6 @@ void RenderNode::pushStagingPropertiesChanges(TreeInfo& info) { damageSelf(info); info.damageAccumulator->popTransform(); syncProperties(); -#if !HWUI_NEW_OPS - applyLayerPropertiesToLayer(info); -#endif // We could try to be clever and only re-damage if the matrix changed. // However, we don't need to worry about that. The cost of over-damaging // here is only going to be a single additional map rect of this node @@ -466,35 +353,19 @@ void RenderNode::pushStagingPropertiesChanges(TreeInfo& info) { } } -#if !HWUI_NEW_OPS -void RenderNode::applyLayerPropertiesToLayer(TreeInfo& info) { - if (CC_LIKELY(!mLayer)) return; - - const LayerProperties& props = properties().layerProperties(); - mLayer->setAlpha(props.alpha(), props.xferMode()); - mLayer->setColorFilter(props.colorFilter()); - mLayer->setBlend(props.needsBlending()); -} -#endif - void RenderNode::syncDisplayList(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) { - for (auto&& child : mStagingDisplayList->getChildren()) { - child->renderNode->incParentRefCount(); - } + mStagingDisplayList->updateChildren([](RenderNode* child) { + child->incParentRefCount(); + }); } deleteDisplayList(info ? info->observer : nullptr, info); mDisplayList = mStagingDisplayList; mStagingDisplayList = nullptr; if (mDisplayList) { - for (auto& iter : mDisplayList->getFunctors()) { - (*iter.functor)(DrawGlInfo::kModeSync, nullptr); - } - for (auto& vectorDrawable : mDisplayList->getVectorDrawables()) { - vectorDrawable->syncProperties(); - } + mDisplayList->syncContents(); } } @@ -511,48 +382,24 @@ void RenderNode::pushStagingDisplayListChanges(TreeInfo& info) { void RenderNode::deleteDisplayList(TreeObserver* observer, TreeInfo* info) { if (mDisplayList) { - for (auto&& child : mDisplayList->getChildren()) { - child->renderNode->decParentRefCount(observer, info); + mDisplayList->updateChildren([observer, info](RenderNode* child) { + child->decParentRefCount(observer, info); + }); + if (!mDisplayList->reuseDisplayList(this, info ? &info->canvasContext : nullptr)) { + delete mDisplayList; } } - delete mDisplayList; mDisplayList = nullptr; } -void RenderNode::prepareSubTree(TreeInfo& info, bool functorsNeedLayer, DisplayList* subtree) { - if (subtree) { - TextureCache& cache = Caches::getInstance().textureCache; - info.out.hasFunctors |= subtree->getFunctors().size(); - for (auto&& bitmapResource : subtree->getBitmapResources()) { - void* ownerToken = &info.canvasContext; - info.prepareTextures = cache.prefetchAndMarkInUse(ownerToken, bitmapResource); - } - for (auto&& op : subtree->getChildren()) { - RenderNode* childNode = op->renderNode; -#if HWUI_NEW_OPS - info.damageAccumulator->pushTransform(&op->localMatrix); - bool childFunctorsNeedLayer = functorsNeedLayer; // TODO! || op->mRecordedWithPotentialStencilClip; -#else - info.damageAccumulator->pushTransform(&op->localMatrix); - bool childFunctorsNeedLayer = functorsNeedLayer - // Recorded with non-rect clip, or canvas-rotated by parent - || op->mRecordedWithPotentialStencilClip; -#endif - childNode->prepareTreeImpl(info, childFunctorsNeedLayer); - info.damageAccumulator->popTransform(); - } - } -} - void RenderNode::destroyHardwareResources(TreeObserver* observer, TreeInfo* info) { - if (mLayer) { - destroyLayer(mLayer); - mLayer = nullptr; + if (hasLayer()) { + renderthread::CanvasContext::destroyLayer(this); } if (mDisplayList) { - for (auto&& child : mDisplayList->getChildren()) { - child->renderNode->destroyHardwareResources(observer, info); - } + mDisplayList->updateChildren([observer, info](RenderNode* child) { + child->destroyHardwareResources(observer, info); + }); if (mNeedsDisplayListSync) { // Next prepare tree we are going to push a new display list, so we can // drop our current one now @@ -579,84 +426,6 @@ void RenderNode::decParentRefCount(TreeObserver* observer, TreeInfo* info) { } } -/* - * For property operations, we pass a savecount of 0, since the operations aren't part of the - * displaylist, and thus don't have to compensate for the record-time/playback-time discrepancy in - * base saveCount (i.e., how RestoreToCount uses saveCount + properties().getCount()) - */ -#define PROPERTY_SAVECOUNT 0 - -template <class T> -void RenderNode::setViewProperties(OpenGLRenderer& renderer, T& handler) { -#if DEBUG_DISPLAY_LIST - properties().debugOutputProperties(handler.level() + 1); -#endif - if (properties().getLeft() != 0 || properties().getTop() != 0) { - renderer.translate(properties().getLeft(), properties().getTop()); - } - if (properties().getStaticMatrix()) { - renderer.concatMatrix(*properties().getStaticMatrix()); - } else if (properties().getAnimationMatrix()) { - renderer.concatMatrix(*properties().getAnimationMatrix()); - } - if (properties().hasTransformMatrix()) { - if (properties().isTransformTranslateOnly()) { - renderer.translate(properties().getTranslationX(), properties().getTranslationY()); - } else { - renderer.concatMatrix(*properties().getTransformMatrix()); - } - } - 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 - } - if (CC_LIKELY(isLayer || !properties().getHasOverlappingRendering())) { - // simply scale rendering content's alpha - renderer.scaleAlpha(properties().getAlpha()); - } else { - // savelayer needed to create an offscreen buffer - Rect layerBounds(0, 0, getWidth(), getHeight()); - if (clipFlags) { - properties().getClippingRectForFlags(clipFlags, &layerBounds); - clipFlags = 0; // all clipping done by savelayer - } - SaveLayerOp* op = new (handler.allocator()) SaveLayerOp( - layerBounds.left, layerBounds.top, - layerBounds.right, layerBounds.bottom, - (int) (properties().getAlpha() * 255), - SaveFlags::HasAlphaLayer | SaveFlags::ClipToLayer); - handler(op, PROPERTY_SAVECOUNT, properties().getClipToBounds()); - } - - if (CC_UNLIKELY(ATRACE_ENABLED() && properties().promotedToLayer())) { - // pretend alpha always causes savelayer to warn about - // performance problem affecting old versions - ATRACE_FORMAT("%s alpha caused saveLayer %dx%d", getName(), - static_cast<int>(getWidth()), - static_cast<int>(getHeight())); - } - } - if (clipFlags) { - Rect clipRect; - properties().getClippingRectForFlags(clipFlags, &clipRect); - ClipRectOp* op = new (handler.allocator()) ClipRectOp( - clipRect.left, clipRect.top, clipRect.right, clipRect.bottom, - SkRegion::kIntersect_Op); - handler(op, PROPERTY_SAVECOUNT, properties().getClipToBounds()); - } - - // TODO: support nesting round rect clips - if (mProperties.getRevealClip().willClip()) { - Rect bounds; - mProperties.getRevealClip().getBounds(&bounds); - renderer.setClippingRoundRect(handler.allocator(), bounds, mProperties.getRevealClip().getRadius()); - } else if (mProperties.getOutline().willClip()) { - renderer.setClippingOutline(handler.allocator(), &(mProperties.getOutline())); - } -} - /** * Apply property-based transformations to input matrix * @@ -717,14 +486,14 @@ void RenderNode::computeOrdering() { // transform properties are applied correctly to top level children if (mDisplayList == nullptr) return; for (unsigned int i = 0; i < mDisplayList->getChildren().size(); i++) { - renderNodeOp_t* childOp = mDisplayList->getChildren()[i]; + RenderNodeOp* childOp = mDisplayList->getChildren()[i]; childOp->renderNode->computeOrderingImpl(childOp, &mProjectedNodes, &mat4::identity()); } } void RenderNode::computeOrderingImpl( - renderNodeOp_t* opState, - std::vector<renderNodeOp_t*>* compositedChildrenOfProjectionSurface, + RenderNodeOp* opState, + std::vector<RenderNodeOp*>* compositedChildrenOfProjectionSurface, const mat4* transformFromProjectionSurface) { mProjectedNodes.clear(); if (mDisplayList == nullptr || mDisplayList->isEmpty()) return; @@ -748,10 +517,10 @@ void RenderNode::computeOrderingImpl( const bool isProjectionReceiver = mDisplayList->projectionReceiveIndex >= 0; bool haveAppliedPropertiesToProjection = false; for (unsigned int i = 0; i < mDisplayList->getChildren().size(); i++) { - renderNodeOp_t* childOp = mDisplayList->getChildren()[i]; + RenderNodeOp* childOp = mDisplayList->getChildren()[i]; RenderNode* child = childOp->renderNode; - std::vector<renderNodeOp_t*>* projectionChildren = nullptr; + std::vector<RenderNodeOp*>* projectionChildren = nullptr; const mat4* projectionTransform = nullptr; if (isProjectionReceiver && !child->properties().getProjectBackwards()) { // if receiving projections, collect projecting descendant @@ -774,372 +543,5 @@ void RenderNode::computeOrderingImpl( } } -class DeferOperationHandler { -public: - DeferOperationHandler(DeferStateStruct& deferStruct, int level) - : mDeferStruct(deferStruct), mLevel(level) {} - inline void operator()(DisplayListOp* operation, int saveCount, bool clipToBounds) { - operation->defer(mDeferStruct, saveCount, mLevel, clipToBounds); - } - inline LinearAllocator& allocator() { return *(mDeferStruct.mAllocator); } - inline void startMark(const char* name) {} // do nothing - inline void endMark() {} - inline int level() { return mLevel; } - inline int replayFlags() { return mDeferStruct.mReplayFlags; } - inline SkPath* allocPathForFrame() { return mDeferStruct.allocPathForFrame(); } - -private: - DeferStateStruct& mDeferStruct; - const int mLevel; -}; - -void RenderNode::defer(DeferStateStruct& deferStruct, const int level) { - DeferOperationHandler handler(deferStruct, level); - issueOperations<DeferOperationHandler>(deferStruct.mRenderer, handler); -} - -class ReplayOperationHandler { -public: - ReplayOperationHandler(ReplayStateStruct& replayStruct, int level) - : mReplayStruct(replayStruct), mLevel(level) {} - inline void operator()(DisplayListOp* operation, int saveCount, bool clipToBounds) { -#if DEBUG_DISPLAY_LIST_OPS_AS_EVENTS - mReplayStruct.mRenderer.eventMark(operation->name()); -#endif - operation->replay(mReplayStruct, saveCount, mLevel, clipToBounds); - } - inline LinearAllocator& allocator() { return *(mReplayStruct.mAllocator); } - inline void startMark(const char* name) { - mReplayStruct.mRenderer.startMark(name); - } - inline void endMark() { - mReplayStruct.mRenderer.endMark(); - } - inline int level() { return mLevel; } - inline int replayFlags() { return mReplayStruct.mReplayFlags; } - inline SkPath* allocPathForFrame() { return mReplayStruct.allocPathForFrame(); } - -private: - ReplayStateStruct& mReplayStruct; - const int mLevel; -}; - -void RenderNode::replay(ReplayStateStruct& replayStruct, const int level) { - ReplayOperationHandler handler(replayStruct, level); - issueOperations<ReplayOperationHandler>(replayStruct.mRenderer, handler); -} - -void RenderNode::buildZSortedChildList(const DisplayList::Chunk& chunk, - std::vector<ZDrawRenderNodeOpPair>& zTranslatedNodes) { -#if !HWUI_NEW_OPS - if (chunk.beginChildIndex == chunk.endChildIndex) return; - - for (unsigned int i = chunk.beginChildIndex; i < chunk.endChildIndex; i++) { - DrawRenderNodeOp* childOp = mDisplayList->getChildren()[i]; - RenderNode* child = childOp->renderNode; - float childZ = child->properties().getZ(); - - if (!MathUtils::isZero(childZ) && chunk.reorderChildren) { - zTranslatedNodes.push_back(ZDrawRenderNodeOpPair(childZ, childOp)); - childOp->skipInOrderDraw = true; - } else if (!child->properties().getProjectBackwards()) { - // regular, in order drawing DisplayList - childOp->skipInOrderDraw = false; - } - } - - // Z sort any 3d children (stable-ness makes z compare fall back to standard drawing order) - std::stable_sort(zTranslatedNodes.begin(), zTranslatedNodes.end()); -#endif -} - -template <class T> -void RenderNode::issueDrawShadowOperation(const Matrix4& transformFromParent, T& handler) { - if (properties().getAlpha() <= 0.0f - || properties().getOutline().getAlpha() <= 0.0f - || !properties().getOutline().getPath() - || properties().getScaleX() == 0 - || properties().getScaleY() == 0) { - // no shadow to draw - return; - } - - mat4 shadowMatrixXY(transformFromParent); - applyViewPropertyTransforms(shadowMatrixXY); - - // Z matrix needs actual 3d transformation, so mapped z values will be correct - mat4 shadowMatrixZ(transformFromParent); - applyViewPropertyTransforms(shadowMatrixZ, true); - - const SkPath* casterOutlinePath = properties().getOutline().getPath(); - const SkPath* revealClipPath = properties().getRevealClip().getPath(); - if (revealClipPath && revealClipPath->isEmpty()) return; - - float casterAlpha = properties().getAlpha() * properties().getOutline().getAlpha(); - - - // holds temporary SkPath to store the result of intersections - SkPath* frameAllocatedPath = nullptr; - const SkPath* outlinePath = casterOutlinePath; - - // intersect the outline with the reveal clip, if present - if (revealClipPath) { - frameAllocatedPath = handler.allocPathForFrame(); - - Op(*outlinePath, *revealClipPath, kIntersect_SkPathOp, frameAllocatedPath); - outlinePath = frameAllocatedPath; - } - - // intersect the outline with the clipBounds, if present - if (properties().getClippingFlags() & CLIP_TO_CLIP_BOUNDS) { - if (!frameAllocatedPath) { - frameAllocatedPath = handler.allocPathForFrame(); - } - - Rect clipBounds; - properties().getClippingRectForFlags(CLIP_TO_CLIP_BOUNDS, &clipBounds); - SkPath clipBoundsPath; - clipBoundsPath.addRect(clipBounds.left, clipBounds.top, - clipBounds.right, clipBounds.bottom); - - Op(*outlinePath, clipBoundsPath, kIntersect_SkPathOp, frameAllocatedPath); - outlinePath = frameAllocatedPath; - } - - DisplayListOp* shadowOp = new (handler.allocator()) DrawShadowOp( - shadowMatrixXY, shadowMatrixZ, casterAlpha, outlinePath); - handler(shadowOp, PROPERTY_SAVECOUNT, properties().getClipToBounds()); -} - -#define SHADOW_DELTA 0.1f - -template <class T> -void RenderNode::issueOperationsOf3dChildren(ChildrenSelectMode mode, - const Matrix4& initialTransform, const std::vector<ZDrawRenderNodeOpPair>& zTranslatedNodes, - OpenGLRenderer& renderer, T& handler) { - const int size = zTranslatedNodes.size(); - if (size == 0 - || (mode == ChildrenSelectMode::NegativeZChildren && zTranslatedNodes[0].key > 0.0f) - || (mode == ChildrenSelectMode::PositiveZChildren && zTranslatedNodes[size - 1].key < 0.0f)) { - // no 3d children to draw - return; - } - - // Apply the base transform of the parent of the 3d children. This isolates - // 3d children of the current chunk from transformations made in previous chunks. - int rootRestoreTo = renderer.save(SaveFlags::Matrix); - renderer.setGlobalMatrix(initialTransform); - - /** - * Draw shadows and (potential) casters mostly in order, but allow the shadows of casters - * with very similar Z heights to draw together. - * - * This way, if Views A & B have the same Z height and are both casting shadows, the shadows are - * underneath both, and neither's shadow is drawn on top of the other. - */ - const size_t nonNegativeIndex = findNonNegativeIndex(zTranslatedNodes); - size_t drawIndex, shadowIndex, endIndex; - if (mode == ChildrenSelectMode::NegativeZChildren) { - drawIndex = 0; - endIndex = nonNegativeIndex; - shadowIndex = endIndex; // draw no shadows - } else { - drawIndex = nonNegativeIndex; - endIndex = size; - shadowIndex = drawIndex; // potentially draw shadow for each pos Z child - } - - DISPLAY_LIST_LOGD("%*s%d %s 3d children:", (handler.level() + 1) * 2, "", - endIndex - drawIndex, mode == kNegativeZChildren ? "negative" : "positive"); - - float lastCasterZ = 0.0f; - while (shadowIndex < endIndex || drawIndex < endIndex) { - if (shadowIndex < endIndex) { - DrawRenderNodeOp* casterOp = zTranslatedNodes[shadowIndex].value; - RenderNode* caster = casterOp->renderNode; - const float casterZ = zTranslatedNodes[shadowIndex].key; - // attempt to render the shadow if the caster about to be drawn is its caster, - // OR if its caster's Z value is similar to the previous potential caster - if (shadowIndex == drawIndex || casterZ - lastCasterZ < SHADOW_DELTA) { - caster->issueDrawShadowOperation(casterOp->localMatrix, handler); - - lastCasterZ = casterZ; // must do this even if current caster not casting a shadow - shadowIndex++; - continue; - } - } - - // only the actual child DL draw needs to be in save/restore, - // since it modifies the renderer's matrix - int restoreTo = renderer.save(SaveFlags::Matrix); - - DrawRenderNodeOp* childOp = zTranslatedNodes[drawIndex].value; - - renderer.concatMatrix(childOp->localMatrix); - childOp->skipInOrderDraw = false; // this is horrible, I'm so sorry everyone - handler(childOp, renderer.getSaveCount() - 1, properties().getClipToBounds()); - childOp->skipInOrderDraw = true; - - renderer.restoreToCount(restoreTo); - drawIndex++; - } - renderer.restoreToCount(rootRestoreTo); -} - -template <class T> -void RenderNode::issueOperationsOfProjectedChildren(OpenGLRenderer& renderer, T& handler) { - DISPLAY_LIST_LOGD("%*s%d projected children:", (handler.level() + 1) * 2, "", mProjectedNodes.size()); - const SkPath* projectionReceiverOutline = properties().getOutline().getPath(); - int restoreTo = renderer.getSaveCount(); - - LinearAllocator& alloc = handler.allocator(); - handler(new (alloc) SaveOp(SaveFlags::MatrixClip), - PROPERTY_SAVECOUNT, properties().getClipToBounds()); - - // Transform renderer to match background we're projecting onto - // (by offsetting canvas by translationX/Y of background rendernode, since only those are set) - const DisplayListOp* op = -#if HWUI_NEW_OPS - nullptr; - LOG_ALWAYS_FATAL("unsupported"); -#else - (mDisplayList->getOps()[mDisplayList->projectionReceiveIndex]); -#endif - const DrawRenderNodeOp* backgroundOp = reinterpret_cast<const DrawRenderNodeOp*>(op); - const RenderProperties& backgroundProps = backgroundOp->renderNode->properties(); - renderer.translate(backgroundProps.getTranslationX(), backgroundProps.getTranslationY()); - - // If the projection receiver has an outline, we mask projected content to it - // (which we know, apriori, are all tessellated paths) - renderer.setProjectionPathMask(alloc, projectionReceiverOutline); - - // draw projected nodes - for (size_t i = 0; i < mProjectedNodes.size(); i++) { - renderNodeOp_t* childOp = mProjectedNodes[i]; - - // matrix save, concat, and restore can be done safely without allocating operations - int restoreTo = renderer.save(SaveFlags::Matrix); - renderer.concatMatrix(childOp->transformFromCompositingAncestor); - childOp->skipInOrderDraw = false; // this is horrible, I'm so sorry everyone - handler(childOp, renderer.getSaveCount() - 1, properties().getClipToBounds()); - childOp->skipInOrderDraw = true; - renderer.restoreToCount(restoreTo); - } - - handler(new (alloc) RestoreToCountOp(restoreTo), - PROPERTY_SAVECOUNT, properties().getClipToBounds()); -} - -/** - * This function serves both defer and replay modes, and will organize the displayList's component - * operations for a single frame: - * - * Every 'simple' state operation that affects just the matrix and alpha (or other factors of - * DeferredDisplayState) may be issued directly to the renderer, but complex operations (with custom - * defer logic) and operations in displayListOps are issued through the 'handler' which handles the - * defer vs replay logic, per operation - */ -template <class T> -void RenderNode::issueOperations(OpenGLRenderer& renderer, T& handler) { - if (mDisplayList->isEmpty()) { - DISPLAY_LIST_LOGD("%*sEmpty display list (%p, %s)", handler.level() * 2, "", - this, getName()); - return; - } - -#if HWUI_NEW_OPS - const bool drawLayer = false; -#else - const bool drawLayer = (mLayer && (&renderer != mLayer->renderer.get())); -#endif - // If we are updating the contents of mLayer, we don't want to apply any of - // the RenderNode's properties to this issueOperations pass. Those will all - // be applied when the layer is drawn, aka when this is true. - const bool useViewProperties = (!mLayer || drawLayer); - if (useViewProperties) { - const Outline& outline = properties().getOutline(); - if (properties().getAlpha() <= 0 - || (outline.getShouldClip() && outline.isEmpty()) - || properties().getScaleX() == 0 - || properties().getScaleY() == 0) { - DISPLAY_LIST_LOGD("%*sRejected display list (%p, %s)", handler.level() * 2, "", - this, getName()); - return; - } - } - - handler.startMark(getName()); - -#if DEBUG_DISPLAY_LIST - const Rect& clipRect = renderer.getLocalClipBounds(); - DISPLAY_LIST_LOGD("%*sStart display list (%p, %s), localClipBounds: %.0f, %.0f, %.0f, %.0f", - handler.level() * 2, "", this, getName(), - clipRect.left, clipRect.top, clipRect.right, clipRect.bottom); -#endif - - LinearAllocator& alloc = handler.allocator(); - int restoreTo = renderer.getSaveCount(); - handler(new (alloc) SaveOp(SaveFlags::MatrixClip), - PROPERTY_SAVECOUNT, properties().getClipToBounds()); - - DISPLAY_LIST_LOGD("%*sSave %d %d", (handler.level() + 1) * 2, "", - SaveFlags::MatrixClip, restoreTo); - - if (useViewProperties) { - setViewProperties<T>(renderer, handler); - } - -#if HWUI_NEW_OPS - LOG_ALWAYS_FATAL("legacy op traversal not supported"); -#else - bool quickRejected = properties().getClipToBounds() - && renderer.quickRejectConservative(0, 0, properties().getWidth(), properties().getHeight()); - if (!quickRejected) { - Matrix4 initialTransform(*(renderer.currentTransform())); - renderer.setBaseTransform(initialTransform); - - if (drawLayer) { - handler(new (alloc) DrawLayerOp(mLayer), - renderer.getSaveCount() - 1, properties().getClipToBounds()); - } else { - const int saveCountOffset = renderer.getSaveCount() - 1; - const int projectionReceiveIndex = mDisplayList->projectionReceiveIndex; - for (size_t chunkIndex = 0; chunkIndex < mDisplayList->getChunks().size(); chunkIndex++) { - const DisplayList::Chunk& chunk = mDisplayList->getChunks()[chunkIndex]; - - std::vector<ZDrawRenderNodeOpPair> zTranslatedNodes; - buildZSortedChildList(chunk, zTranslatedNodes); - - issueOperationsOf3dChildren(ChildrenSelectMode::NegativeZChildren, - initialTransform, zTranslatedNodes, renderer, handler); - - for (size_t opIndex = chunk.beginOpIndex; opIndex < chunk.endOpIndex; opIndex++) { - DisplayListOp *op = mDisplayList->getOps()[opIndex]; -#if DEBUG_DISPLAY_LIST - op->output(handler.level() + 1); -#endif - handler(op, saveCountOffset, properties().getClipToBounds()); - - if (CC_UNLIKELY(!mProjectedNodes.empty() && projectionReceiveIndex >= 0 && - opIndex == static_cast<size_t>(projectionReceiveIndex))) { - issueOperationsOfProjectedChildren(renderer, handler); - } - } - - issueOperationsOf3dChildren(ChildrenSelectMode::PositiveZChildren, - initialTransform, zTranslatedNodes, renderer, handler); - } - } - } -#endif - - DISPLAY_LIST_LOGD("%*sRestoreToCount %d", (handler.level() + 1) * 2, "", restoreTo); - handler(new (alloc) RestoreToCountOp(restoreTo), - PROPERTY_SAVECOUNT, properties().getClipToBounds()); - - DISPLAY_LIST_LOGD("%*sDone (%p, %s)", handler.level() * 2, "", this, getName()); - handler.endMark(); -} - } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h index f9735a231d7a..b8964f0f16a0 100644 --- a/libs/hwui/RenderNode.h +++ b/libs/hwui/RenderNode.h @@ -13,8 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#ifndef RENDERNODE_H -#define RENDERNODE_H + +#pragma once #include <SkCamera.h> #include <SkMatrix.h> @@ -32,6 +32,8 @@ #include "DisplayList.h" #include "Matrix.h" #include "RenderProperties.h" +#include "pipeline/skia/SkiaDisplayList.h" +#include "pipeline/skia/SkiaLayer.h" #include <vector> @@ -39,34 +41,19 @@ class SkBitmap; class SkPaint; class SkPath; class SkRegion; +class SkSurface; namespace android { namespace uirenderer { class CanvasState; -class DisplayListCanvas; class DisplayListOp; -class OpenGLRenderer; -class Rect; -class SkiaShader; - -#if HWUI_NEW_OPS class FrameBuilder; class OffscreenBuffer; +class Rect; +class SkiaShader; struct RenderNodeOp; -typedef OffscreenBuffer layer_t; -typedef RenderNodeOp renderNodeOp_t; -#else -class Layer; -typedef Layer layer_t; -typedef DrawRenderNodeOp renderNodeOp_t; -#endif - -class ClipRectOp; -class DrawRenderNodeOp; -class SaveLayerOp; -class SaveOp; -class RestoreToCountOp; + class TreeInfo; class TreeObserver; @@ -78,9 +65,8 @@ class RenderNode; * 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 DisplayListCanvas (which manages the recording), DisplayList - * (which holds the actual data), and DisplayList (which holds properties and performs playback onto - * a renderer). + * functionality is split between RecordingCanvas (which manages the recording), DisplayList + * (which holds the actual data), and RenderNode (which holds properties used for render playback). * * Note that DisplayList is swapped out from beneath an individual RenderNode when a view's * recorded stream of canvas operations is refreshed. The RenderNode (and its properties) stay @@ -115,20 +101,11 @@ public: kReplayFlag_ClipChildren = 0x1 }; - void debugDumpLayers(const char* prefix); - ANDROID_API void setStagingDisplayList(DisplayList* newData, TreeObserver* observer); void computeOrdering(); - void defer(DeferStateStruct& deferStruct, const int level); - void replay(ReplayStateStruct& replayStruct, const int level); - -#if HWUI_NEW_OPS - ANDROID_API void output(uint32_t level = 0, const char* label = "Root"); -#else - ANDROID_API void output(uint32_t level = 1); -#endif + ANDROID_API void output(); ANDROID_API int getDebugSize(); void copyTo(proto::RenderNode* node); @@ -223,10 +200,9 @@ public: const DisplayList* getDisplayList() const { return mDisplayList; } -#if HWUI_NEW_OPS OffscreenBuffer* getLayer() const { return mLayer; } OffscreenBuffer** getLayerHandle() { return &mLayer; } // ugh... -#endif + void setLayer(OffscreenBuffer* layer) { mLayer = layer; } // Note: The position callbacks are relying on the listener using // the frameNumber to appropriately batch/synchronize these transactions. @@ -257,73 +233,16 @@ public: } private: - typedef key_value_pair_t<float, DrawRenderNodeOp*> ZDrawRenderNodeOpPair; - - static size_t findNonNegativeIndex(const std::vector<ZDrawRenderNodeOpPair>& nodes) { - for (size_t i = 0; i < nodes.size(); i++) { - if (nodes[i].key >= 0.0f) return i; - } - return nodes.size(); - } - - enum class ChildrenSelectMode { - NegativeZChildren, - PositiveZChildren - }; - - void computeOrderingImpl(renderNodeOp_t* opState, - std::vector<renderNodeOp_t*>* compositedChildrenOfProjectionSurface, + void computeOrderingImpl(RenderNodeOp* opState, + std::vector<RenderNodeOp*>* compositedChildrenOfProjectionSurface, const mat4* transformFromProjectionSurface); - template <class T> - inline void setViewProperties(OpenGLRenderer& renderer, T& handler); - - void buildZSortedChildList(const DisplayList::Chunk& chunk, - std::vector<ZDrawRenderNodeOpPair>& zTranslatedNodes); - - template<class T> - inline void issueDrawShadowOperation(const Matrix4& transformFromParent, T& handler); - - template <class T> - inline void issueOperationsOf3dChildren(ChildrenSelectMode mode, - const Matrix4& initialTransform, const std::vector<ZDrawRenderNodeOpPair>& zTranslatedNodes, - OpenGLRenderer& renderer, T& handler); - - template <class T> - inline void issueOperationsOfProjectedChildren(OpenGLRenderer& renderer, T& handler); - - /** - * Issue the RenderNode's operations into a handler, recursing for subtrees through - * DrawRenderNodeOp's defer() or replay() methods - */ - template <class T> - inline void issueOperations(OpenGLRenderer& renderer, T& handler); - - class TextContainer { - public: - size_t length() const { - return mByteLength; - } - - const char* text() const { - return (const char*) mText; - } - - size_t mByteLength; - const char* mText; - }; - - void syncProperties(); void syncDisplayList(TreeInfo* info); void prepareTreeImpl(TreeInfo& info, bool functorsNeedLayer); void pushStagingPropertiesChanges(TreeInfo& info); void pushStagingDisplayListChanges(TreeInfo& info); - void prepareSubTree(TreeInfo& info, bool functorsNeedLayer, DisplayList* subtree); -#if !HWUI_NEW_OPS - void applyLayerPropertiesToLayer(TreeInfo& info); -#endif void prepareLayer(TreeInfo& info, uint32_t dirtyMask); void pushLayerUpdate(TreeInfo& info); void deleteDisplayList(TreeObserver* observer, TreeInfo* info = nullptr); @@ -331,6 +250,7 @@ private: void incParentRefCount() { mParentCount++; } void decParentRefCount(TreeObserver* observer, TreeInfo* info = nullptr); + void output(std::ostream& output, uint32_t level); String8 mName; sp<VirtualLightRefBase> mUserContext; @@ -349,14 +269,14 @@ private: // Owned by RT. Lifecycle is managed by prepareTree(), with the exception // being in ~RenderNode() which may happen on any thread. - layer_t* mLayer = nullptr; + OffscreenBuffer* mLayer = nullptr; /** * Draw time state - these properties are only set and used during rendering */ // for projection surfaces, contains a list of all children items - std::vector<renderNodeOp_t*> mProjectedNodes; + std::vector<RenderNodeOp*> mProjectedNodes; // How many references our parent(s) have to us. Typically this should alternate // between 2 and 1 (when a staging push happens we inc first then dec) @@ -367,9 +287,79 @@ private: uint32_t mParentCount; sp<PositionListener> mPositionListener; + +// METHODS & FIELDS ONLY USED BY THE SKIA RENDERER +public: + /** + * Detach and transfer ownership of an already allocated displayList for use + * in recording updated content for this renderNode + */ + std::unique_ptr<skiapipeline::SkiaDisplayList> detachAvailableList() { + return std::move(mAvailableDisplayList); + } + + /** + * Attach unused displayList to this node for potential future reuse. + */ + void attachAvailableList(skiapipeline::SkiaDisplayList* skiaDisplayList) { + mAvailableDisplayList.reset(skiaDisplayList); + } + + /** + * Returns true if an offscreen layer from any renderPipeline is attached + * to this node. + */ + bool hasLayer() const { return mLayer || mSkiaLayer.get(); } + + /** + * Used by the RenderPipeline to attach an offscreen surface to the RenderNode. + * The surface is then will be used to store the contents of a layer. + */ + void setLayerSurface(sk_sp<SkSurface> layer) { + if (layer.get()) { + if (!mSkiaLayer.get()) { + mSkiaLayer = std::make_unique<skiapipeline::SkiaLayer>(); + } + mSkiaLayer->layerSurface = std::move(layer); + mSkiaLayer->inverseTransformInWindow.loadIdentity(); + } else { + mSkiaLayer.reset(); + } + } + + /** + * If the RenderNode is of type LayerType::RenderLayer then this method will + * return the an offscreen rendering surface that is used to both render into + * the layer and composite the layer into its parent. If the type is not + * LayerType::RenderLayer then it will return a nullptr. + * + * NOTE: this function is only guaranteed to return accurate results after + * prepareTree has been run for this RenderNode + */ + SkSurface* getLayerSurface() const { + return mSkiaLayer.get() ? mSkiaLayer->layerSurface.get() : nullptr; + } + + skiapipeline::SkiaLayer* getSkiaLayer() const { + return mSkiaLayer.get(); + } + +private: + /** + * If this RenderNode has been used in a previous frame then the SkiaDisplayList + * from that frame is cached here until one of the following conditions is met: + * 1) The RenderNode is deleted (causing this to be deleted) + * 2) It is replaced with the displayList from the next completed frame + * 3) It is detached and used to to record a new displayList for a later frame + */ + std::unique_ptr<skiapipeline::SkiaDisplayList> mAvailableDisplayList; + + /** + * An offscreen rendering target used to contain the contents this RenderNode + * when it has been set to draw as a LayerType::RenderLayer. + */ + std::unique_ptr<skiapipeline::SkiaLayer> mSkiaLayer; }; // class RenderNode } /* namespace uirenderer */ } /* namespace android */ - -#endif /* RENDERNODE_H */ diff --git a/libs/hwui/RenderProperties.cpp b/libs/hwui/RenderProperties.cpp index 5ebf5458da18..146fbe73a48a 100644 --- a/libs/hwui/RenderProperties.cpp +++ b/libs/hwui/RenderProperties.cpp @@ -24,7 +24,6 @@ #include <SkPathOps.h> #include "Matrix.h" -#include "OpenGLRenderer.h" #include "hwui/Canvas.h" #include "utils/MathUtils.h" @@ -53,7 +52,7 @@ bool LayerProperties::setColorFilter(SkColorFilter* filter) { bool LayerProperties::setFromPaint(const SkPaint* paint) { bool changed = false; changed |= setAlpha(static_cast<uint8_t>(PaintUtils::getAlphaDirect(paint))); - changed |= setXferMode(PaintUtils::getXfermodeDirect(paint)); + changed |= setXferMode(PaintUtils::getBlendModeDirect(paint)); changed |= setColorFilter(paint ? paint->getColorFilter() : nullptr); return changed; } @@ -100,26 +99,34 @@ RenderProperties& RenderProperties::operator=(const RenderProperties& other) { return *this; } -void RenderProperties::debugOutputProperties(const int level) const { - if (mPrimitiveFields.mLeft != 0 || mPrimitiveFields.mTop != 0) { - ALOGD("%*s(Translate (left, top) %d, %d)", level * 2, "", - mPrimitiveFields.mLeft, mPrimitiveFields.mTop); - } - if (mStaticMatrix) { - ALOGD("%*s(ConcatMatrix (static) %p: " SK_MATRIX_STRING ")", - level * 2, "", mStaticMatrix, SK_MATRIX_ARGS(mStaticMatrix)); +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 << ")" << std::endl; } - if (mAnimationMatrix) { - ALOGD("%*s(ConcatMatrix (animation) %p: " SK_MATRIX_STRING ")", - level * 2, "", mAnimationMatrix, SK_MATRIX_ARGS(mAnimationMatrix)); +} + +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; } + dumpMatrix(output, indent, "ConcatMatrix (static)", mStaticMatrix); + dumpMatrix(output, indent, "ConcatMatrix (animation)", mAnimationMatrix); + + output << std::fixed << std::setprecision(2); if (hasTransformMatrix()) { if (isTransformTranslateOnly()) { - ALOGD("%*s(Translate %.2f, %.2f, %.2f)", - level * 2, "", getTranslationX(), getTranslationY(), getZ()); + output << indent << "(Translate " << getTranslationX() << ", " << getTranslationY() + << ", " << getZ() << ")" << std::endl; } else { - ALOGD("%*s(ConcatMatrix %p: " SK_MATRIX_STRING ")", - level * 2, "", mComputedFields.mTransformMatrix, SK_MATRIX_ARGS(mComputedFields.mTransformMatrix)); + dumpMatrix(output, indent, "ConcatMatrix ", mComputedFields.mTransformMatrix); } } @@ -133,7 +140,7 @@ void RenderProperties::debugOutputProperties(const int level) const { if (CC_LIKELY(isLayer || !getHasOverlappingRendering())) { // simply scale rendering content's alpha - ALOGD("%*s(ScaleAlpha %.2f)", level * 2, "", mPrimitiveFields.mAlpha); + output << indent << "(ScaleAlpha " << mPrimitiveFields.mAlpha << ")" << std::endl; } else { // savelayeralpha to create an offscreen buffer to apply alpha Rect layerBounds(0, 0, getWidth(), getHeight()); @@ -141,35 +148,40 @@ void RenderProperties::debugOutputProperties(const int level) const { getClippingRectForFlags(clipFlags, &layerBounds); clipFlags = 0; // all clipping done by savelayer } - ALOGD("%*s(SaveLayerAlpha %d, %d, %d, %d, %d, 0x%x)", level * 2, "", - (int)layerBounds.left, (int)layerBounds.top, - (int)layerBounds.right, (int)layerBounds.bottom, - (int)(mPrimitiveFields.mAlpha * 255), - SaveFlags::HasAlphaLayer | SaveFlags::ClipToLayer); + 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); - ALOGD("%*s(ClipRect %d, %d, %d, %d)", level * 2, "", - (int)clipRect.left, (int)clipRect.top, (int)clipRect.right, (int)clipRect.bottom); + 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); - ALOGD("%*s(Clip to reveal clip with bounds %.2f %.2f %.2f %.2f)", level * 2, "", - RECT_ARGS(bounds)); + output << indent << "(Clip to reveal clip with bounds " + << bounds.left << ", " << bounds.top << ", " + << bounds.right << ", " << bounds.bottom << ")" << std::endl; } auto& outline = mPrimitiveFields.mOutline; if (outline.getShouldClip()) { if (outline.isEmpty()) { - ALOGD("%*s(Clip to empty outline)", level * 2, ""); + output << indent << "(Clip to empty outline)"; } else if (outline.willClip()) { - ALOGD("%*s(Clip to outline with bounds %.2f %.2f %.2f %.2f)", level * 2, "", - RECT_ARGS(outline.getBounds())); + const Rect& bounds = outline.getBounds(); + output << indent << "(Clip to outline with bounds " + << bounds.left << ", " << bounds.top << ", " + << bounds.right << ", " << bounds.bottom << ")" << std::endl; } } } diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h index 6a6e8dbb3fcd..9ee2f9c69343 100644 --- a/libs/hwui/RenderProperties.h +++ b/libs/hwui/RenderProperties.h @@ -13,8 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#ifndef RENDERNODEPROPERTIES_H -#define RENDERNODEPROPERTIES_H + +#pragma once #include "Caches.h" #include "DeviceInfo.h" @@ -22,11 +22,12 @@ #include "RevealClip.h" #include "Outline.h" #include "utils/MathUtils.h" +#include "utils/PaintUtils.h" +#include <SkBlendMode.h> #include <SkCamera.h> #include <SkMatrix.h> #include <SkRegion.h> -#include <SkXfermode.h> #include <algorithm> #include <stddef.h> @@ -34,6 +35,7 @@ #include <cutils/compiler.h> #include <androidfw/ResourceTypes.h> #include <utils/Log.h> +#include <ostream> class SkBitmap; class SkColorFilter; @@ -91,11 +93,11 @@ public: return mAlpha; } - bool setXferMode(SkXfermode::Mode mode) { + bool setXferMode(SkBlendMode mode) { return RP_SET(mMode, mode); } - SkXfermode::Mode xferMode() const { + SkBlendMode xferMode() const { return mMode; } @@ -131,7 +133,7 @@ private: // Whether or not that Layer's content is opaque, doesn't include alpha bool mOpaque; uint8_t mAlpha; - SkXfermode::Mode mMode; + SkBlendMode mMode; SkColorFilter* mColorFilter = nullptr; }; @@ -573,7 +575,7 @@ public: return mPrimitiveFields.mProjectBackwards; } - void debugOutputProperties(const int level) const; + void debugOutputProperties(std::ostream& output, const int level) const; void updateMatrix(); @@ -678,5 +680,3 @@ private: } /* namespace uirenderer */ } /* namespace android */ - -#endif /* RENDERNODEPROPERTIES_H */ diff --git a/libs/hwui/ShadowTessellator.h b/libs/hwui/ShadowTessellator.h index 5f4c9c53a9d8..2eaf187a1af1 100644 --- a/libs/hwui/ShadowTessellator.h +++ b/libs/hwui/ShadowTessellator.h @@ -80,8 +80,6 @@ public: static Vector2 centroid2d(const Vector2* poly, int polyLength); - static bool isClockwise(const Vector2* polygon, int len); - static Vector2 calculateNormal(const Vector2& p1, const Vector2& p2); static int getExtraVertexNumber(const Vector2& vector1, const Vector2& vector2, diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp index 3b6fae08773d..89e2a01a5ebe 100644 --- a/libs/hwui/SkiaCanvas.cpp +++ b/libs/hwui/SkiaCanvas.cpp @@ -14,181 +14,31 @@ * limitations under the License. */ +#include "SkiaCanvas.h" + #include "CanvasProperty.h" -#include "Layer.h" -#include "RenderNode.h" -#include "hwui/Canvas.h" +#include "NinePatchUtils.h" +#include "VectorDrawable.h" +#include "hwui/Bitmap.h" +#include "hwui/MinikinUtils.h" +#include "pipeline/skia/AnimatedDrawables.h" -#include <SkCanvas.h> -#include <SkClipStack.h> #include <SkDrawable.h> -#include <SkDevice.h> #include <SkDeque.h> #include <SkDrawFilter.h> #include <SkGraphics.h> #include <SkImage.h> +#include <SkImagePriv.h> +#include <SkRSXform.h> #include <SkShader.h> -#include <SkTArray.h> -#include <SkTLazy.h> #include <SkTemplates.h> - -#include "VectorDrawable.h" +#include <SkTextBlob.h> #include <memory> namespace android { -// Holds an SkCanvas reference plus additional native data. -class SkiaCanvas : public Canvas { -public: - explicit SkiaCanvas(const SkBitmap& bitmap); - - /** - * Create a new SkiaCanvas. - * - * @param canvas SkCanvas to handle calls made to this SkiaCanvas. Must - * not be NULL. This constructor will ref() the SkCanvas, and unref() - * it in its destructor. - */ - explicit SkiaCanvas(SkCanvas* canvas) : mCanvas(canvas) { - SkASSERT(canvas); - canvas->ref(); - } - - virtual SkCanvas* asSkCanvas() override { - return mCanvas.get(); - } - - virtual void resetRecording(int width, int height) override { - LOG_ALWAYS_FATAL("SkiaCanvas cannot be reset as a recording canvas"); - } - - virtual uirenderer::DisplayList* finishRecording() override { - LOG_ALWAYS_FATAL("SkiaCanvas does not produce a DisplayList"); - return nullptr; - } - virtual void insertReorderBarrier(bool enableReorder) override { - LOG_ALWAYS_FATAL("SkiaCanvas does not support reordering barriers"); - } - - virtual void setBitmap(const SkBitmap& bitmap) override; - - virtual bool isOpaque() override; - 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 void getMatrix(SkMatrix* outMatrix) const override; - virtual void setMatrix(const SkMatrix& matrix) override; - virtual void concat(const SkMatrix& matrix) override; - virtual void rotate(float degrees) override; - virtual void scale(float sx, float sy) override; - virtual void skew(float sx, float sy) override; - virtual void translate(float dx, float dy) override; - - 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, - SkRegion::Op op) override; - virtual bool clipPath(const SkPath* path, SkRegion::Op op) override; - virtual bool clipRegion(const SkRegion* region, SkRegion::Op op) override; - - virtual SkDrawFilter* getDrawFilter() override; - virtual void setDrawFilter(SkDrawFilter* drawFilter) override; - - virtual void drawColor(int color, SkXfermode::Mode mode) override; - virtual void drawPaint(const SkPaint& paint) override; - - 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; - 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; - 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 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 drawPath(const SkPath& path, const SkPaint& paint) override; - virtual void drawVertices(SkCanvas::VertexMode vertexMode, int vertexCount, - const float* verts, const float* tex, const int* colors, - const uint16_t* indices, int indexCount, const SkPaint& paint) override; - - virtual void drawBitmap(const SkBitmap& bitmap, float left, float top, - const SkPaint* paint) override; - virtual void drawBitmap(const SkBitmap& bitmap, const SkMatrix& matrix, - const SkPaint* paint) override; - virtual void drawBitmap(const SkBitmap& bitmap, float srcLeft, float srcTop, - float srcRight, float srcBottom, float dstLeft, float dstTop, - float dstRight, float dstBottom, const SkPaint* paint) override; - virtual void drawBitmapMesh(const SkBitmap& bitmap, int meshWidth, int meshHeight, - const float* vertices, const int* colors, const SkPaint* paint) override; - virtual void drawNinePatch(const SkBitmap& 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; } - 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; - virtual void drawCircle(uirenderer::CanvasPropertyPrimitive* x, - 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; - -protected: - virtual void drawGlyphs(const uint16_t* text, const float* positions, int count, - const SkPaint& paint, float x, float y, - float boundsLeft, float boundsTop, float boundsRight, float boundsBottom, - float totalAdvance) override; - virtual void drawGlyphsOnPath(const uint16_t* glyphs, int count, const SkPath& path, - float hOffset, float vOffset, const SkPaint& paint) override; - -private: - struct SaveRec { - int saveCount; - SaveFlags::Flags saveFlags; - }; - - bool mHighContrastText = false; - - void recordPartialSave(SaveFlags::Flags flags); - void saveClipsForFrame(SkTArray<SkClipStack::Element>& clips, int frameSaveCount); - void applyClips(const SkTArray<SkClipStack::Element>& clips); - - void drawPoints(const float* points, int count, const SkPaint& paint, - SkCanvas::PointMode mode); - - SkAutoTUnref<SkCanvas> mCanvas; - std::unique_ptr<SkDeque> mSaveStack; // lazily allocated, tracks partial saves. -}; +using uirenderer::PaintUtils; Canvas* Canvas::create_canvas(const SkBitmap& bitmap) { return new SkiaCanvas(bitmap); @@ -198,8 +48,25 @@ Canvas* Canvas::create_canvas(SkCanvas* skiaCanvas) { return new SkiaCanvas(skiaCanvas); } +SkiaCanvas::SkiaCanvas() {} + +SkiaCanvas::SkiaCanvas(SkCanvas* canvas) + : mCanvas(canvas) {} + SkiaCanvas::SkiaCanvas(const SkBitmap& bitmap) { - mCanvas.reset(new SkCanvas(bitmap)); + mCanvasOwned = std::unique_ptr<SkCanvas>(new SkCanvas(bitmap)); + mCanvas = mCanvasOwned.get(); +} + +SkiaCanvas::~SkiaCanvas() {} + +void SkiaCanvas::reset(SkCanvas* skiaCanvas) { + if (mCanvas != skiaCanvas) { + mCanvas = skiaCanvas; + mCanvasOwned.reset(); + } + mSaveStack.reset(nullptr); + mHighContrastText = false; } // ---------------------------------------------------------------------------- @@ -210,13 +77,13 @@ class ClipCopier : public SkCanvas::ClipVisitor { public: explicit ClipCopier(SkCanvas* dstCanvas) : m_dstCanvas(dstCanvas) {} - virtual void clipRect(const SkRect& rect, SkRegion::Op op, bool antialias) { + virtual void clipRect(const SkRect& rect, SkClipOp op, bool antialias) { m_dstCanvas->clipRect(rect, op, antialias); } - virtual void clipRRect(const SkRRect& rrect, SkRegion::Op op, bool antialias) { + virtual void clipRRect(const SkRRect& rrect, SkClipOp op, bool antialias) { m_dstCanvas->clipRRect(rrect, op, antialias); } - virtual void clipPath(const SkPath& path, SkRegion::Op op, bool antialias) { + virtual void clipPath(const SkPath& path, SkClipOp op, bool antialias) { m_dstCanvas->clipPath(path, op, antialias); } @@ -235,11 +102,12 @@ void SkiaCanvas::setBitmap(const SkBitmap& bitmap) { mCanvas->replayClips(&copier); } - // unrefs the existing canvas - mCanvas.reset(newCanvas); + // deletes the previously owned canvas (if any) + mCanvasOwned = std::unique_ptr<SkCanvas>(newCanvas); + mCanvas = newCanvas; // clean up the old save stack - mSaveStack.reset(NULL); + mSaveStack.reset(nullptr); } // ---------------------------------------------------------------------------- @@ -277,13 +145,8 @@ int SkiaCanvas::save(SaveFlags::Flags flags) { // operation. It does this by explicitly saving off the clip & matrix state // when requested and playing it back after the SkCanvas::restore. void SkiaCanvas::restore() { - const SaveRec* rec = (NULL == mSaveStack.get()) - ? NULL - : static_cast<SaveRec*>(mSaveStack->back()); - int currentSaveCount = mCanvas->getSaveCount(); - SkASSERT(NULL == rec || currentSaveCount >= rec->saveCount); - - if (NULL == rec || rec->saveCount != currentSaveCount) { + const auto* rec = this->currentSaveRec(); + if (!rec) { // Fast path - no record for this frame. mCanvas->restore(); return; @@ -297,27 +160,18 @@ void SkiaCanvas::restore() { savedMatrix = mCanvas->getTotalMatrix(); } - SkTArray<SkClipStack::Element> savedClips; - int topClipStackFrame = mCanvas->getClipStack()->getSaveCount(); - if (preserveClip) { - saveClipsForFrame(savedClips, topClipStackFrame); - } + const size_t clipIndex = rec->clipIndex; mCanvas->restore(); + mSaveStack->pop_back(); if (preserveMatrix) { mCanvas->setMatrix(savedMatrix); } - if (preserveClip && !savedClips.empty() && - topClipStackFrame != mCanvas->getClipStack()->getSaveCount()) { - // Only reapply the saved clips if the top clip stack frame was actually - // popped by restore(). If it wasn't, it means it doesn't belong to the - // restored canvas frame (SkCanvas lazy save/restore kicked in). - applyClips(savedClips); + if (preserveClip) { + this->applyPersistentClips(clipIndex); } - - mSaveStack->pop_back(); } void SkiaCanvas::restoreToCount(int restoreCount) { @@ -344,22 +198,71 @@ static inline SkCanvas::SaveLayerFlags layerFlags(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)); + //always save matrix and clip to match the behaviour of Skia and HWUI pipelines and to ensure + //android state tracking behavior matches that of the Skia API (partial save is not supported) + const SkCanvas::SaveLayerRec rec(&bounds, paint, layerFlags(flags | SaveFlags::MatrixClip)); - int count = mCanvas->saveLayer(rec); - recordPartialSave(flags); - return count; + return mCanvas->saveLayer(rec); } int SkiaCanvas::saveLayerAlpha(float left, float top, float right, float bottom, int alpha, SaveFlags::Flags flags) { - SkTLazy<SkPaint> alphaPaint; if (static_cast<unsigned>(alpha) < 0xFF) { - alphaPaint.init()->setAlpha(alpha); + SkPaint alphaPaint; + alphaPaint.setAlpha(alpha); + return this->saveLayer(left, top, right, bottom, &alphaPaint, flags); } + return this->saveLayer(left, top, right, bottom, nullptr, flags); +} - return this->saveLayer(left, top, right, bottom, alphaPaint.getMaybeNull(), - flags); +class SkiaCanvas::Clip { +public: + Clip(const SkRect& rect, SkClipOp op, const SkMatrix& m) + : 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) {} + Clip(const SkPath& path, SkClipOp op, const SkMatrix& m) + : 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; + } + } + +private: + enum class Type { + Rect, + RRect, + Path, + }; + + Type mType; + SkClipOp mOp; + SkMatrix mMatrix; + + // These are logically a union (tracked separately due to non-POD path). + SkTLazy<SkPath> mPath; + SkRRect mRRect; +}; + +const SkiaCanvas::SaveRec* SkiaCanvas::currentSaveRec() const { + const SaveRec* rec = mSaveStack + ? static_cast<const SaveRec*>(mSaveStack->back()) + : nullptr; + int currentSaveCount = mCanvas->getSaveCount(); + SkASSERT(!rec || currentSaveCount >= rec->saveCount); + + return (rec && rec->saveCount == currentSaveCount) ? rec : nullptr; } // ---------------------------------------------------------------------------- @@ -378,45 +281,48 @@ void SkiaCanvas::recordPartialSave(SaveFlags::Flags flags) { return; } - if (NULL == mSaveStack.get()) { + if (!mSaveStack) { mSaveStack.reset(new SkDeque(sizeof(struct SaveRec), 8)); } SaveRec* rec = static_cast<SaveRec*>(mSaveStack->push_back()); rec->saveCount = mCanvas->getSaveCount(); rec->saveFlags = flags; + rec->clipIndex = mClipStack.size(); } -void SkiaCanvas::saveClipsForFrame(SkTArray<SkClipStack::Element>& clips, - int saveCountToBackup) { - // Each SkClipStack::Element stores the index of the canvas save - // with which it is associated. Backup only those Elements that - // are associated with 'saveCountToBackup' - SkClipStack::Iter clipIterator(*mCanvas->getClipStack(), - SkClipStack::Iter::kTop_IterStart); - while (const SkClipStack::Element* elem = clipIterator.prev()) { - if (elem->getSaveCount() < saveCountToBackup) { - // done with the target save count. - break; - } - SkASSERT(elem->getSaveCount() == saveCountToBackup); - clips.push_back(*elem); +template <typename T> +void SkiaCanvas::recordClip(const T& clip, SkClipOp op) { + // Only need tracking when in a partial save frame which + // doesn't restore the clip. + const SaveRec* rec = this->currentSaveRec(); + if (rec && !(rec->saveFlags & SaveFlags::Clip)) { + mClipStack.emplace_back(clip, op, mCanvas->getTotalMatrix()); } } -void SkiaCanvas::applyClips(const SkTArray<SkClipStack::Element>& clips) { - ClipCopier clipCopier(mCanvas); +// Applies and optionally removes all clips >= index. +void SkiaCanvas::applyPersistentClips(size_t clipStartIndex) { + SkASSERT(clipStartIndex <= mClipStack.size()); + const auto begin = mClipStack.cbegin() + clipStartIndex; + const auto end = mClipStack.cend(); - // The clip stack stores clips in device space. - SkMatrix origMatrix = mCanvas->getTotalMatrix(); - mCanvas->resetMatrix(); + // Clip application mutates the CTM. + const SkMatrix saveMatrix = mCanvas->getTotalMatrix(); - // We pushed the clips in reverse order. - for (int i = clips.count() - 1; i >= 0; --i) { - clips[i].replay(&clipCopier); + for (auto clip = begin; clip != end; ++clip) { + clip->apply(mCanvas); } - mCanvas->setMatrix(origMatrix); + mCanvas->setMatrix(saveMatrix); + + // If the current/post-restore save rec is also persisting clips, we + // leave them on the stack to be reapplied part of the next restore(). + // Otherwise we're done and just pop them. + const auto* rec = this->currentSaveRec(); + if (!rec || (rec->saveFlags & SaveFlags::Clip)) { + mClipStack.erase(begin, end); + } } // ---------------------------------------------------------------------------- @@ -490,27 +396,21 @@ bool SkiaCanvas::quickRejectPath(const SkPath& path) const { return mCanvas->quickReject(path); } -bool SkiaCanvas::clipRect(float left, float top, float right, float bottom, SkRegion::Op op) { +bool SkiaCanvas::clipRect(float left, float top, float right, float bottom, SkClipOp op) { SkRect rect = SkRect::MakeLTRB(left, top, right, bottom); + this->recordClip(rect, op); mCanvas->clipRect(rect, op); return !mCanvas->isClipEmpty(); } -bool SkiaCanvas::clipPath(const SkPath* path, SkRegion::Op op) { - mCanvas->clipPath(*path, op); - return !mCanvas->isClipEmpty(); -} - -bool SkiaCanvas::clipRegion(const SkRegion* region, SkRegion::Op op) { - SkPath rgnPath; - if (region->getBoundaryPath(&rgnPath)) { - // The region is specified in device space. - SkMatrix savedMatrix = mCanvas->getTotalMatrix(); - mCanvas->resetMatrix(); - mCanvas->clipPath(rgnPath, op); - mCanvas->setMatrix(savedMatrix); +bool SkiaCanvas::clipPath(const SkPath* path, SkClipOp op) { + SkRRect roundRect; + if (path->isRRect(&roundRect)) { + this->recordClip(roundRect, op); + mCanvas->clipRRect(roundRect, op); } else { - mCanvas->clipRect(SkRect::MakeEmpty(), op); + this->recordClip(*path, op); + mCanvas->clipPath(*path, op); } return !mCanvas->isClipEmpty(); } @@ -531,7 +431,7 @@ void SkiaCanvas::setDrawFilter(SkDrawFilter* drawFilter) { // Canvas draw operations // ---------------------------------------------------------------------------- -void SkiaCanvas::drawColor(int color, SkXfermode::Mode mode) { +void SkiaCanvas::drawColor(int color, SkBlendMode mode) { mCanvas->drawColor(color, mode); } @@ -545,6 +445,7 @@ void SkiaCanvas::drawPaint(const SkPaint& paint) { void SkiaCanvas::drawPoints(const float* points, int count, const SkPaint& paint, SkCanvas::PointMode mode) { + if (CC_UNLIKELY(count < 2 || PaintUtils::paintWillNotDraw(paint))) return; // convert the floats into SkPoints count >>= 1; // now it is the number of points std::unique_ptr<SkPoint[]> pts(new SkPoint[count]); @@ -570,46 +471,58 @@ void SkiaCanvas::drawLine(float startX, float startY, float stopX, float stopY, } void SkiaCanvas::drawLines(const float* points, int count, const SkPaint& paint) { + if (CC_UNLIKELY(count < 4 || PaintUtils::paintWillNotDraw(paint))) return; this->drawPoints(points, count, paint, SkCanvas::kLines_PointMode); } void SkiaCanvas::drawRect(float left, float top, float right, float bottom, const SkPaint& paint) { + if (CC_UNLIKELY(PaintUtils::paintWillNotDraw(paint))) return; mCanvas->drawRectCoords(left, top, right, bottom, paint); } void SkiaCanvas::drawRegion(const SkRegion& region, const SkPaint& paint) { - SkRegion::Iterator it(region); - while (!it.done()) { - mCanvas->drawRect(SkRect::Make(it.rect()), paint); - it.next(); - } + if (CC_UNLIKELY(PaintUtils::paintWillNotDraw(paint))) return; + mCanvas->drawRegion(region, paint); } void SkiaCanvas::drawRoundRect(float left, float top, float right, float bottom, float rx, float ry, const SkPaint& paint) { + if (CC_UNLIKELY(PaintUtils::paintWillNotDraw(paint))) return; SkRect rect = SkRect::MakeLTRB(left, top, right, bottom); mCanvas->drawRoundRect(rect, rx, ry, paint); } void SkiaCanvas::drawCircle(float x, float y, float radius, const SkPaint& paint) { + if (CC_UNLIKELY(radius <= 0 || PaintUtils::paintWillNotDraw(paint))) return; mCanvas->drawCircle(x, y, radius, paint); } void SkiaCanvas::drawOval(float left, float top, float right, float bottom, const SkPaint& paint) { + if (CC_UNLIKELY(PaintUtils::paintWillNotDraw(paint))) return; SkRect oval = SkRect::MakeLTRB(left, top, right, bottom); mCanvas->drawOval(oval, paint); } void SkiaCanvas::drawArc(float left, float top, float right, float bottom, float startAngle, float sweepAngle, bool useCenter, const SkPaint& paint) { + if (CC_UNLIKELY(PaintUtils::paintWillNotDraw(paint))) return; SkRect arc = SkRect::MakeLTRB(left, top, right, bottom); mCanvas->drawArc(arc, startAngle, sweepAngle, useCenter, paint); } void SkiaCanvas::drawPath(const SkPath& path, const SkPaint& paint) { - mCanvas->drawPath(path, paint); + if (CC_UNLIKELY(PaintUtils::paintWillNotDraw(paint))) return; + SkRect rect; + SkRRect roundRect; + if (path.isOval(&rect)) { + mCanvas->drawOval(rect, paint); + } else if (path.isRRect(&roundRect)) { + mCanvas->drawRRect(roundRect, paint); + } else { + mCanvas->drawPath(path, paint); + } } void SkiaCanvas::drawVertices(SkCanvas::VertexMode vertexMode, int vertexCount, @@ -620,34 +533,41 @@ void SkiaCanvas::drawVertices(SkCanvas::VertexMode vertexMode, int vertexCount, #endif const int ptCount = vertexCount >> 1; mCanvas->drawVertices(vertexMode, ptCount, (SkPoint*)verts, (SkPoint*)texs, - (SkColor*)colors, NULL, indices, indexCount, paint); + (SkColor*)colors, indices, indexCount, paint); } // ---------------------------------------------------------------------------- // Canvas draw operations: Bitmaps // ---------------------------------------------------------------------------- -void SkiaCanvas::drawBitmap(const SkBitmap& bitmap, float left, float top, const SkPaint* paint) { - mCanvas->drawBitmap(bitmap, left, top, paint); +void SkiaCanvas::drawBitmap(Bitmap& bitmap, float left, float top, const SkPaint* paint) { + SkBitmap skBitmap; + bitmap.getSkBitmap(&skBitmap); + mCanvas->drawBitmap(skBitmap, left, top, paint); } -void SkiaCanvas::drawBitmap(const SkBitmap& bitmap, const SkMatrix& matrix, const SkPaint* paint) { +void SkiaCanvas::drawBitmap(Bitmap& hwuiBitmap, const SkMatrix& matrix, const SkPaint* paint) { + SkBitmap bitmap; + hwuiBitmap.getSkBitmap(&bitmap); SkAutoCanvasRestore acr(mCanvas, true); mCanvas->concat(matrix); mCanvas->drawBitmap(bitmap, 0, 0, paint); } -void SkiaCanvas::drawBitmap(const SkBitmap& bitmap, float srcLeft, float srcTop, +void SkiaCanvas::drawBitmap(Bitmap& hwuiBitmap, float srcLeft, float srcTop, float srcRight, float srcBottom, float dstLeft, float dstTop, float dstRight, float dstBottom, const SkPaint* paint) { + SkBitmap bitmap; + hwuiBitmap.getSkBitmap(&bitmap); SkRect srcRect = SkRect::MakeLTRB(srcLeft, srcTop, srcRight, srcBottom); SkRect dstRect = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom); mCanvas->drawBitmapRect(bitmap, srcRect, dstRect, paint); } -void SkiaCanvas::drawBitmapMesh(const SkBitmap& bitmap, int meshWidth, int meshHeight, +void SkiaCanvas::drawBitmapMesh(Bitmap& hwuiBitmap, int meshWidth, int meshHeight, const float* vertices, const int* colors, const SkPaint* paint) { - + SkBitmap bitmap; + hwuiBitmap.getSkBitmap(&bitmap); const int ptCount = (meshWidth + 1) * (meshHeight + 1); const int indexCount = meshWidth * meshHeight * 6; @@ -732,20 +652,40 @@ void SkiaCanvas::drawBitmapMesh(const SkBitmap& bitmap, int meshWidth, int meshH if (paint) { tmpPaint = *paint; } - SkShader* shader = SkShader::CreateBitmapShader(bitmap, - SkShader::kClamp_TileMode, - SkShader::kClamp_TileMode); - SkSafeUnref(tmpPaint.setShader(shader)); + + sk_sp<SkImage> image = SkMakeImageFromRasterBitmap(bitmap, kNever_SkCopyPixelsMode); + tmpPaint.setShader(image->makeShader(SkShader::kClamp_TileMode, SkShader::kClamp_TileMode)); mCanvas->drawVertices(SkCanvas::kTriangles_VertexMode, ptCount, (SkPoint*)vertices, - texs, (const SkColor*)colors, NULL, indices, + texs, (const SkColor*)colors, indices, indexCount, tmpPaint); } -void SkiaCanvas::drawNinePatch(const SkBitmap& bitmap, const Res_png_9patch& chunk, +void SkiaCanvas::drawNinePatch(Bitmap& hwuiBitmap, const Res_png_9patch& chunk, float dstLeft, float dstTop, float dstRight, float dstBottom, const SkPaint* paint) { - SkRect bounds = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom); - NinePatch::Draw(mCanvas, bounds, bitmap, chunk, paint, nullptr); + + SkBitmap bitmap; + hwuiBitmap.getSkBitmap(&bitmap); + + SkCanvas::Lattice lattice; + NinePatchUtils::SetLatticeDivs(&lattice, chunk, bitmap.width(), bitmap.height()); + + lattice.fFlags = 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. + // Skia requires a flag for every rect. + numFlags = (lattice.fXCount + 1) * (lattice.fYCount + 1); + } + + SkAutoSTMalloc<25, SkCanvas::Lattice::Flags> flags(numFlags); + if (numFlags > 0) { + NinePatchUtils::SetLatticeFlags(&lattice, flags.get(), numFlags, chunk); + } + + lattice.fBounds = nullptr; + SkRect dst = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom); + mCanvas->drawBitmapLattice(bitmap, lattice, dst, paint); } void SkiaCanvas::drawVectorDrawable(VectorDrawableRoot* vectorDrawable) { @@ -760,83 +700,72 @@ void SkiaCanvas::drawGlyphs(const uint16_t* text, const float* positions, int co const SkPaint& paint, float x, float y, float boundsLeft, float boundsTop, float boundsRight, float boundsBottom, float totalAdvance) { - static_assert(sizeof(SkPoint) == sizeof(float)*2, "SkPoint is no longer two floats"); - mCanvas->drawPosText(text, count << 1, reinterpret_cast<const SkPoint*>(positions), paint); - drawTextDecorations(x, y, totalAdvance, paint); -} + if (!text || !positions || count <= 0 || PaintUtils::paintWillNotDrawText(paint)) 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); + + SkRect bounds = SkRect::MakeLTRB(boundsLeft + x, boundsTop + y, + boundsRight + x, boundsBottom + y); + + SkTextBlobBuilder builder; + const SkTextBlobBuilder::RunBuffer& buffer = builder.allocRunPos(paintCopy, count, &bounds); + // TODO: we could reduce the number of memcpy's if the this were exposed further up + // in the architecture. + memcpy(buffer.glyphs, text, count * sizeof(uint16_t)); + memcpy(buffer.pos, positions, (count << 1) * sizeof(float)); + + sk_sp<SkTextBlob> textBlob(builder.make()); + mCanvas->drawTextBlob(textBlob, 0, 0, paintCopy); + drawTextDecorations(x, y, totalAdvance, paintCopy); +} + +void SkiaCanvas::drawLayoutOnPath(const minikin::Layout& layout, float hOffset, float vOffset, + 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(); + uint16_t* glyphs = (uint16_t*)(xform + N); + SkPathMeasure meas(path, false); + + for (size_t i = start; i < end; i++) { + glyphs[i - start] = layout.getGlyphId(i); + float x = hOffset + layout.getX(i); + float y = vOffset + layout.getY(i); + + SkPoint pos; + SkVector tan; + if (!meas.getPosTan(x, &pos, &tan)) { + pos.set(x, y); + tan.set(1, 0); + } + 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; + } -void SkiaCanvas::drawGlyphsOnPath(const uint16_t* glyphs, int count, const SkPath& path, - float hOffset, float vOffset, const SkPaint& paint) { - mCanvas->drawTextOnPathHV(glyphs, count << 1, path, hOffset, vOffset, paint); + this->asSkCanvas()->drawTextRSXform(glyphs, sizeof(uint16_t) * N, xform, nullptr, paint); } // ---------------------------------------------------------------------------- // Canvas draw operations: Animations // ---------------------------------------------------------------------------- -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) {} - - protected: - virtual SkRect onGetBounds() override { - return SkRect::MakeLTRB(mLeft->value, mTop->value, mRight->value, mBottom->value); - } - virtual void onDraw(SkCanvas* canvas) override { - SkRect rect = SkRect::MakeLTRB(mLeft->value, mTop->value, mRight->value, mBottom->value); - canvas->drawRoundRect(rect, mRx->value, mRy->value, mPaint->value); - } - - private: - sp<uirenderer::CanvasPropertyPrimitive> mLeft; - sp<uirenderer::CanvasPropertyPrimitive> mTop; - sp<uirenderer::CanvasPropertyPrimitive> mRight; - sp<uirenderer::CanvasPropertyPrimitive> mBottom; - sp<uirenderer::CanvasPropertyPrimitive> mRx; - sp<uirenderer::CanvasPropertyPrimitive> mRy; - sp<uirenderer::CanvasPropertyPaint> mPaint; -}; - -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) {} - - protected: - virtual SkRect onGetBounds() override { - const float x = mX->value; - const float y = mY->value; - const float radius = mRadius->value; - return SkRect::MakeLTRB(x - radius, y - radius, x + radius, y + radius); - } - virtual void onDraw(SkCanvas* canvas) override { - canvas->drawCircle(mX->value, mY->value, mRadius->value, mPaint->value); - } - - private: - sp<uirenderer::CanvasPropertyPrimitive> mX; - sp<uirenderer::CanvasPropertyPrimitive> mY; - sp<uirenderer::CanvasPropertyPrimitive> mRadius; - sp<uirenderer::CanvasPropertyPaint> mPaint; -}; - void SkiaCanvas::drawRoundRect(uirenderer::CanvasPropertyPrimitive* left, uirenderer::CanvasPropertyPrimitive* top, uirenderer::CanvasPropertyPrimitive* right, uirenderer::CanvasPropertyPrimitive* bottom, uirenderer::CanvasPropertyPrimitive* rx, uirenderer::CanvasPropertyPrimitive* ry, uirenderer::CanvasPropertyPaint* paint) { - SkAutoTUnref<AnimatedRoundRect> drawable( - new AnimatedRoundRect(left, top, right, bottom, rx, ry, paint)); + sk_sp<uirenderer::skiapipeline::AnimatedRoundRect> drawable( + 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) { - SkAutoTUnref<AnimatedCircle> drawable(new AnimatedCircle(x, y, radius, paint)); + sk_sp<uirenderer::skiapipeline::AnimatedCircle> drawable(new uirenderer::skiapipeline::AnimatedCircle(x, y, radius, paint)); mCanvas->drawDrawable(drawable.get()); } @@ -844,11 +773,17 @@ void SkiaCanvas::drawCircle(uirenderer::CanvasPropertyPrimitive* x, uirenderer:: // Canvas draw operations: View System // ---------------------------------------------------------------------------- -void SkiaCanvas::drawLayer(uirenderer::DeferredLayerUpdater* layer) { } +void SkiaCanvas::drawLayer(uirenderer::DeferredLayerUpdater* layerUpdater) { + LOG_ALWAYS_FATAL("SkiaCanvas can't directly draw Layers"); +} -void SkiaCanvas::drawRenderNode(uirenderer::RenderNode* renderNode) { } +void SkiaCanvas::drawRenderNode(uirenderer::RenderNode* renderNode) { + LOG_ALWAYS_FATAL("SkiaCanvas can't directly draw RenderNodes"); +} void SkiaCanvas::callDrawGLFunction(Functor* functor, - uirenderer::GlFunctorLifecycleListener* listener) { } + uirenderer::GlFunctorLifecycleListener* listener) { + LOG_ALWAYS_FATAL("SkiaCanvas can't directly draw GL Content"); +} } // namespace android diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h new file mode 100644 index 000000000000..34c3717557ff --- /dev/null +++ b/libs/hwui/SkiaCanvas.h @@ -0,0 +1,192 @@ +/* + * 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. + */ +#pragma once + +#include "hwui/Canvas.h" +#include "CanvasProperty.h" +#include "DeferredLayerUpdater.h" +#include "RenderNode.h" +#include "VectorDrawable.h" + +#include <SkCanvas.h> +#include <SkTLazy.h> + +namespace android { + +// Holds an SkCanvas reference plus additional native data. +class SkiaCanvas : public Canvas { +public: + explicit SkiaCanvas(const SkBitmap& bitmap); + + /** + * Create a new SkiaCanvas. + * + * @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. + */ + explicit SkiaCanvas(SkCanvas* canvas); + + virtual ~SkiaCanvas(); + + virtual SkCanvas* asSkCanvas() override { + return mCanvas; + } + + virtual void resetRecording(int width, int height, + uirenderer::RenderNode* renderNode) override { + LOG_ALWAYS_FATAL("SkiaCanvas cannot be reset as a recording canvas"); + } + + virtual uirenderer::DisplayList* finishRecording() override { + LOG_ALWAYS_FATAL("SkiaCanvas does not produce a DisplayList"); + return nullptr; + } + virtual void insertReorderBarrier(bool enableReorder) override { + LOG_ALWAYS_FATAL("SkiaCanvas does not support reordering barriers"); + } + + virtual void setBitmap(const SkBitmap& bitmap) override; + + virtual bool isOpaque() override; + 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 void getMatrix(SkMatrix* outMatrix) const override; + virtual void setMatrix(const SkMatrix& matrix) override; + virtual void concat(const SkMatrix& matrix) override; + virtual void rotate(float degrees) override; + virtual void scale(float sx, float sy) override; + virtual void skew(float sx, float sy) override; + virtual void translate(float dx, float dy) override; + + 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 clipPath(const SkPath* path, SkClipOp op) override; + + virtual SkDrawFilter* getDrawFilter() override; + virtual void setDrawFilter(SkDrawFilter* drawFilter) override; + + virtual void drawColor(int color, SkBlendMode mode) override; + virtual void drawPaint(const SkPaint& paint) override; + + 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; + 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; + 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 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 drawPath(const SkPath& path, const SkPaint& paint) override; + virtual void drawVertices(SkCanvas::VertexMode vertexMode, int vertexCount, + const float* verts, const float* tex, const int* colors, + const uint16_t* indices, int indexCount, 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 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; } + 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; + virtual void drawCircle(uirenderer::CanvasPropertyPrimitive* x, + 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; + +protected: + SkiaCanvas(); + void reset(SkCanvas* skiaCanvas); + void drawDrawable(SkDrawable* drawable) { mCanvas->drawDrawable(drawable); } + + virtual void drawGlyphs(const uint16_t* text, const float* positions, int count, + const SkPaint& paint, float x, 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; + +private: + struct SaveRec { + int saveCount; + SaveFlags::Flags saveFlags; + size_t clipIndex; + }; + + bool mHighContrastText = false; + + const SaveRec* currentSaveRec() const; + void recordPartialSave(SaveFlags::Flags flags); + + 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); + + class Clip; + + 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 + diff --git a/libs/hwui/SkiaCanvasProxy.cpp b/libs/hwui/SkiaCanvasProxy.cpp index 5db5efb649e7..f32612d21319 100644 --- a/libs/hwui/SkiaCanvasProxy.cpp +++ b/libs/hwui/SkiaCanvasProxy.cpp @@ -20,12 +20,17 @@ #include <log/log.h> +#include "hwui/Bitmap.h" +#include <SkLatticeIter.h> #include <SkPatchUtils.h> #include <SkPaint.h> #include <SkPath.h> #include <SkPixelRef.h> #include <SkRect.h> #include <SkRRect.h> +#include <SkRSXform.h> +#include <SkSurface.h> +#include <SkTextBlobRunIterator.h> namespace android { namespace uirenderer { @@ -104,16 +109,12 @@ void SkiaCanvasProxy::onDrawPath(const SkPath& path, const SkPaint& paint) { void SkiaCanvasProxy::onDrawBitmap(const SkBitmap& bitmap, SkScalar left, SkScalar top, const SkPaint* paint) { - SkPixelRef* pxRef = bitmap.pixelRef(); - + 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 (pxRef && bitmap.dimensions() != pxRef->info().dimensions()) { - SkBitmap fullBitmap; - fullBitmap.setInfo(pxRef->info()); - fullBitmap.setPixelRef(pxRef, 0, 0); + if (hwuiBitmap && bitmap.dimensions() != hwuiBitmap->info().dimensions()) { SkIPoint origin = bitmap.pixelRefOrigin(); - mCanvas->drawBitmap(fullBitmap, origin.fX, origin.fY, + mCanvas->drawBitmap(*hwuiBitmap, origin.fX, origin.fY, origin.fX + bitmap.dimensions().width(), origin.fY + bitmap.dimensions().height(), left, top, @@ -121,15 +122,16 @@ void SkiaCanvasProxy::onDrawBitmap(const SkBitmap& bitmap, SkScalar left, SkScal top + bitmap.dimensions().height(), paint); } else { - mCanvas->drawBitmap(bitmap, left, top, paint); + mCanvas->drawBitmap(*hwuiBitmap, left, top, paint); } } -void SkiaCanvasProxy::onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* srcPtr, +void SkiaCanvasProxy::onDrawBitmapRect(const SkBitmap& skBitmap, const SkRect* srcPtr, const SkRect& dst, const SkPaint* paint, SrcRectConstraint) { - SkRect src = (srcPtr) ? *srcPtr : SkRect::MakeWH(bitmap.width(), bitmap.height()); + SkRect src = (srcPtr) ? *srcPtr : SkRect::MakeWH(skBitmap.width(), skBitmap.height()); // TODO: if bitmap is a subset, do we need to add pixelRefOrigin to src? - mCanvas->drawBitmap(bitmap, src.fLeft, src.fTop, src.fRight, src.fBottom, + 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); } @@ -139,9 +141,43 @@ void SkiaCanvasProxy::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& ce SkDEBUGFAIL("SkiaCanvasProxy::onDrawBitmapNine is not yet supported"); } +void SkiaCanvasProxy::onDrawImage(const SkImage* image, SkScalar left, SkScalar top, + const SkPaint* paint) { + SkBitmap skiaBitmap; + if (image->asLegacyBitmap(&skiaBitmap, SkImage::kRO_LegacyBitmapMode)) { + onDrawBitmap(skiaBitmap, left, top, paint); + } +} + +void SkiaCanvasProxy::onDrawImageRect(const SkImage* image, const SkRect* srcPtr, const SkRect& dst, + const SkPaint* paint, SrcRectConstraint constraint) { + SkBitmap skiaBitmap; + if (image->asLegacyBitmap(&skiaBitmap, SkImage::kRO_LegacyBitmapMode)) { + 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); + } +} + +void SkiaCanvasProxy::onDrawImageNine(const SkImage*, const SkIRect& center, const SkRect& dst, + const SkPaint*) { + SkDEBUGFAIL("SkiaCanvasProxy::onDrawImageNine is not yet supported"); +} + +void SkiaCanvasProxy::onDrawImageLattice(const SkImage* image, const Lattice& lattice, + 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); + } +} + void SkiaCanvasProxy::onDrawVertices(VertexMode mode, int vertexCount, const SkPoint vertices[], - const SkPoint texs[], const SkColor colors[], SkXfermode*, const uint16_t indices[], + const SkPoint texs[], const SkColor colors[], SkBlendMode, const uint16_t indices[], int indexCount, const SkPaint& paint) { + // TODO: should we pass through blendmode if (mFilterHwuiCalls) { return; } @@ -154,7 +190,7 @@ void SkiaCanvasProxy::onDrawVertices(VertexMode mode, int vertexCount, const SkP mCanvas->drawVertices(mode, floatCount, vArray, tArray, cArray, indices, indexCount, paint); } -SkSurface* SkiaCanvasProxy::onNewSurface(const SkImageInfo&, const SkSurfaceProps&) { +sk_sp<SkSurface> SkiaCanvasProxy::onNewSurface(const SkImageInfo&, const SkSurfaceProps&) { SkDEBUGFAIL("SkiaCanvasProxy::onNewSurface is not supported"); return NULL; } @@ -323,9 +359,9 @@ void SkiaCanvasProxy::onDrawPosText(const void* text, size_t byteLength, const S // by Minikin then it had already computed these bounds. Unfortunately, // there is no way to capture those bounds as part of the Skia drawPosText // API so we need to do that computation again here. - SkRect bounds; + SkRect bounds = SkRect::MakeEmpty(); for (int i = 0; i < glyphs.count; i++) { - SkRect glyphBounds; + SkRect glyphBounds = SkRect::MakeEmpty(); glyphs.paint.measureText(&glyphs.glyphIDs[i], sizeof(uint16_t), &glyphBounds); glyphBounds.offset(pos[i].fX, pos[i].fY); bounds.join(glyphBounds); @@ -348,18 +384,68 @@ 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) { - // convert to glyphIDs if necessary - GlyphIDConverter glyphs(text, byteLength, origPaint); - mCanvas->drawGlyphsOnPath(glyphs.glyphIDs, glyphs.count, path, 0, 0, glyphs.paint); + 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 + SkMatrix localM, currM, origM; + mCanvas->getMatrix(&currM); + origM = currM; + for (int i = 0; i < glyphs.count; i++) { + localM.setRSXform(*xform++); + currM.setConcat(origM, localM); + mCanvas->setMatrix(currM); + 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) { - SkDEBUGFAIL("SkiaCanvasProxy::onDrawTextBlob is not supported"); + 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"); + } + } } void SkiaCanvasProxy::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4], - const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) { + const SkPoint texCoords[4], SkBlendMode bmode, const SkPaint& paint) { if (mFilterHwuiCalls) { return; } @@ -373,28 +459,24 @@ void SkiaCanvasProxy::onDrawPatch(const SkPoint cubics[12], const SkColor colors // If it fails to generate the vertices, then we do not draw. if (SkPatchUtils::getVertexData(&data, cubics, colors, texCoords, lod.width(), lod.height())) { this->drawVertices(SkCanvas::kTriangles_VertexMode, data.fVertexCount, data.fPoints, - data.fTexCoords, data.fColors, xmode, data.fIndices, data.fIndexCount, + data.fTexCoords, data.fColors, bmode, data.fIndices, data.fIndexCount, paint); } } -void SkiaCanvasProxy::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle) { +void SkiaCanvasProxy::onClipRect(const SkRect& rect, SkClipOp op, ClipEdgeStyle) { mCanvas->clipRect(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, op); } -void SkiaCanvasProxy::onClipRRect(const SkRRect& roundRect, SkRegion::Op op, ClipEdgeStyle) { +void SkiaCanvasProxy::onClipRRect(const SkRRect& roundRect, SkClipOp op, ClipEdgeStyle) { SkPath path; path.addRRect(roundRect); mCanvas->clipPath(&path, op); } -void SkiaCanvasProxy::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle) { +void SkiaCanvasProxy::onClipPath(const SkPath& path, SkClipOp op, ClipEdgeStyle) { mCanvas->clipPath(&path, op); } -void SkiaCanvasProxy::onClipRegion(const SkRegion& region, SkRegion::Op op) { - mCanvas->clipRegion(®ion, op); -} - }; // namespace uirenderer }; // namespace android diff --git a/libs/hwui/SkiaCanvasProxy.h b/libs/hwui/SkiaCanvasProxy.h index c03c523cd106..b3f6c07dfe64 100644 --- a/libs/hwui/SkiaCanvasProxy.h +++ b/libs/hwui/SkiaCanvasProxy.h @@ -44,7 +44,7 @@ public: protected: - virtual SkSurface* onNewSurface(const SkImageInfo&, const SkSurfaceProps&) override; + virtual sk_sp<SkSurface> onNewSurface(const SkImageInfo&, const SkSurfaceProps&) override; virtual void willSave() override; virtual SaveLayerStrategy getSaveLayerStrategy(const SaveLayerRec&) override; @@ -66,8 +66,15 @@ protected: const SkPaint* paint, SrcRectConstraint) 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); + virtual void onDrawImageNine(const SkImage*, const SkIRect& center, const SkRect& dst, + const SkPaint*); + virtual void onDrawImageLattice(const SkImage*, const Lattice& lattice, const SkRect& dst, + const SkPaint*); virtual void onDrawVertices(VertexMode, int vertexCount, const SkPoint vertices[], - const SkPoint texs[], const SkColor colors[], SkXfermode*, + const SkPoint texs[], const SkColor colors[], SkBlendMode, const uint16_t indices[], int indexCount, const SkPaint&) override; @@ -81,17 +88,18 @@ protected: SkScalar constY, const SkPaint&) override; virtual void onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path, const SkMatrix* matrix, const SkPaint&) override; + virtual void onDrawTextRSXform(const void* text, size_t byteLength, const SkRSXform[], + const SkRect* cullRect, const SkPaint& paint); virtual void onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y, const SkPaint& paint) override; virtual void onDrawPatch(const SkPoint cubics[12], const SkColor colors[4], - const SkPoint texCoords[4], SkXfermode* xmode, + const SkPoint texCoords[4], SkBlendMode, const SkPaint& paint) override; - virtual void onClipRect(const SkRect&, SkRegion::Op, ClipEdgeStyle) override; - virtual void onClipRRect(const SkRRect&, SkRegion::Op, ClipEdgeStyle) override; - virtual void onClipPath(const SkPath&, SkRegion::Op, ClipEdgeStyle) override; - virtual void onClipRegion(const SkRegion&, SkRegion::Op) override; + virtual void onClipRect(const SkRect&, SkClipOp, ClipEdgeStyle) override; + virtual void onClipRRect(const SkRRect&, SkClipOp, ClipEdgeStyle) override; + virtual void onClipPath(const SkPath&, SkClipOp, ClipEdgeStyle) override; private: Canvas* mCanvas; diff --git a/libs/hwui/SkiaShader.cpp b/libs/hwui/SkiaShader.cpp index 6f4a6839be4e..92399864ff23 100644 --- a/libs/hwui/SkiaShader.cpp +++ b/libs/hwui/SkiaShader.cpp @@ -18,9 +18,9 @@ #include "Caches.h" #include "Extensions.h" -#include "Layer.h" #include "Matrix.h" #include "Texture.h" +#include "hwui/Bitmap.h" #include <SkMatrix.h> #include <utils/Log.h> @@ -57,7 +57,7 @@ static inline void bindUniformColor(int slot, FloatColor color) { } static inline void bindTexture(Caches* caches, Texture* texture, GLenum wrapS, GLenum wrapT) { - caches->textureState().bindTexture(texture->id()); + caches->textureState().bindTexture(texture->target(), texture->id()); texture->setWrapST(wrapS, wrapT); } @@ -80,7 +80,7 @@ static void computeScreenSpaceMatrix(mat4& screenSpace, const SkMatrix& unitMatr } /////////////////////////////////////////////////////////////////////////////// -// gradient shader matrix helpers +// Gradient shader matrix helpers /////////////////////////////////////////////////////////////////////////////// static void toLinearUnitMatrix(const SkPoint pts[2], SkMatrix* matrix) { @@ -160,7 +160,7 @@ bool tryStoreGradient(Caches& caches, const SkShader& shader, const Matrix4 mode gradInfo.fColorOffsets = &colorOffsets[0]; shader.asAGradient(&gradInfo); - if (CC_UNLIKELY(!isSimpleGradient(gradInfo))) { + if (CC_UNLIKELY(!description->isSimpleGradient)) { outData->gradientSampler = (*textureUnit)++; #ifndef SK_SCALAR_IS_FLOAT @@ -173,15 +173,16 @@ bool tryStoreGradient(Caches& caches, const SkShader& shader, const Matrix4 mode outData->gradientSampler = 0; outData->gradientTexture = nullptr; - outData->startColor.set(gradInfo.fColors[0]); - outData->endColor.set(gradInfo.fColors[1]); + outData->startColor.setUnPreMultipliedSRGB(gradInfo.fColors[0]); + outData->endColor.setUnPreMultipliedSRGB(gradInfo.fColors[1]); } - outData->ditherSampler = (*textureUnit)++; return true; } -void applyGradient(Caches& caches, const SkiaShaderData::GradientShaderData& data) { +void applyGradient(Caches& caches, const SkiaShaderData::GradientShaderData& data, + const GLsizei width, const GLsizei height) { + if (CC_UNLIKELY(data.gradientTexture)) { caches.textureState().activateTexture(data.gradientSampler); bindTexture(&caches, data.gradientTexture, data.wrapST, data.wrapST); @@ -191,10 +192,7 @@ void applyGradient(Caches& caches, const SkiaShaderData::GradientShaderData& dat bindUniformColor(caches.program().getUniform("endColor"), data.endColor); } - // TODO: remove sampler slot incrementing from dither.setupProgram, - // since this assignment of slots is done at store, not apply time - GLuint ditherSampler = data.ditherSampler; - caches.dither.setupProgram(caches.program(), &ditherSampler); + glUniform2f(caches.program().getUniform("screenSize"), 1.0f / width, 1.0f / height); glUniformMatrix4fv(caches.program().getUniform("screenSpace"), 1, GL_FALSE, &data.screenSpace.data[0]); } @@ -208,13 +206,9 @@ bool tryStoreBitmap(Caches& caches, const SkShader& shader, const Matrix4& model return false; } - /* - * Bypass the AssetAtlas, since those textures: - * 1) require UV mapping, which isn't implemented in matrix computation below - * 2) can't handle REPEAT simply - * 3) are safe to upload here (outside of sync stage), since they're static - */ - outData->bitmapTexture = caches.textureCache.getAndBypassAtlas(&bitmap); + // TODO: create hwui-owned BitmapShader. + Bitmap* hwuiBitmap = static_cast<Bitmap*>(bitmap.pixelRef()); + outData->bitmapTexture = caches.textureCache.get(hwuiBitmap); if (!outData->bitmapTexture) return false; outData->bitmapSampler = (*textureUnit)++; @@ -223,10 +217,14 @@ bool tryStoreBitmap(Caches& caches, const SkShader& shader, const Matrix4& model const float height = outData->bitmapTexture->height(); description->hasBitmap = true; - if (!caches.extensions().hasNPot() + 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)) { - description->isBitmapNpot = true; + && (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; description->bitmapWrapS = gTileModes[xy[0]]; description->bitmapWrapT = gTileModes[xy[1]]; @@ -314,49 +312,10 @@ bool tryStoreCompose(Caches& caches, const SkShader& shader, const Matrix4& mode storeCompose(caches, *rec.fShaderB, *rec.fShaderA, transform, textureUnit, description, outData); } - if (!SkXfermode::AsMode(rec.fMode, &description->shadersMode)) { - // TODO: Support other modes. - description->shadersMode = SkXfermode::kSrcOver_Mode; - } - return true; -} - -bool tryStoreLayer(Caches& caches, const SkShader& shader, const Matrix4& modelViewMatrix, - GLuint* textureUnit, ProgramDescription* description, - SkiaShaderData::LayerShaderData* outData) { - Layer* layer; - if (!shader.asACustomShader(reinterpret_cast<void**>(&layer))) { - return false; - } - - description->hasBitmap = true; - outData->layer = layer; - outData->bitmapSampler = (*textureUnit)++; - - const float width = layer->getWidth(); - const float height = layer->getHeight(); - - computeScreenSpaceMatrix(outData->textureTransform, SkMatrix::I(), shader.getLocalMatrix(), - modelViewMatrix); - - outData->textureDimension[0] = 1.0f / width; - outData->textureDimension[1] = 1.0f / height; + description->shadersMode = rec.fBlendMode; return true; } -void applyLayer(Caches& caches, const SkiaShaderData::LayerShaderData& data) { - caches.textureState().activateTexture(data.bitmapSampler); - - data.layer->bindTexture(); - data.layer->setWrap(GL_CLAMP_TO_EDGE); - data.layer->setFilter(GL_LINEAR); - - glUniform1i(caches.program().getUniform("bitmapSampler"), data.bitmapSampler); - glUniformMatrix4fv(caches.program().getUniform("textureTransform"), 1, - GL_FALSE, &data.textureTransform.data[0]); - glUniform2fv(caches.program().getUniform("textureDimension"), 1, &data.textureDimension[0]); -} - void SkiaShader::store(Caches& caches, const SkShader& shader, const Matrix4& modelViewMatrix, GLuint* textureUnit, ProgramDescription* description, SkiaShaderData* outData) { @@ -378,29 +337,20 @@ void SkiaShader::store(Caches& caches, const SkShader& shader, const Matrix4& mo return; } - if (tryStoreLayer(caches, shader, modelViewMatrix, - textureUnit, description, &outData->layerData)) { - outData->skiaShaderType = kLayer_SkiaShaderType; - return; - } - // Unknown/unsupported type, so explicitly ignore shader outData->skiaShaderType = kNone_SkiaShaderType; } -void SkiaShader::apply(Caches& caches, const SkiaShaderData& data) { +void SkiaShader::apply(Caches& caches, const SkiaShaderData& data, + const GLsizei width, const GLsizei height) { if (!data.skiaShaderType) return; if (data.skiaShaderType & kGradient_SkiaShaderType) { - applyGradient(caches, data.gradientData); + applyGradient(caches, data.gradientData, width, height); } if (data.skiaShaderType & kBitmap_SkiaShaderType) { applyBitmap(caches, data.bitmapData); } - - if (data.skiaShaderType == kLayer_SkiaShaderType) { - applyLayer(caches, data.layerData); - } } }; // namespace uirenderer diff --git a/libs/hwui/SkiaShader.h b/libs/hwui/SkiaShader.h index 884196d9fc62..ab578d540774 100644 --- a/libs/hwui/SkiaShader.h +++ b/libs/hwui/SkiaShader.h @@ -22,7 +22,6 @@ #include <GLES2/gl2.h> #include <SkShader.h> -#include <SkXfermode.h> #include <cutils/compiler.h> namespace android { @@ -30,7 +29,6 @@ namespace uirenderer { class Caches; class Extensions; -class Layer; class Texture; struct ProgramDescription; @@ -46,7 +44,6 @@ enum SkiaShaderType { kBitmap_SkiaShaderType = 1, kGradient_SkiaShaderType = 2, kCompose_SkiaShaderType = kBitmap_SkiaShaderType | kGradient_SkiaShaderType, - kLayer_SkiaShaderType = 4, }; struct SkiaShaderData { @@ -62,7 +59,6 @@ struct SkiaShaderData { } bitmapData; struct GradientShaderData { Matrix4 screenSpace; - GLuint ditherSampler; // simple gradient FloatColor startColor; @@ -72,17 +68,7 @@ struct SkiaShaderData { Texture* gradientTexture; GLuint gradientSampler; GLenum wrapST; - } gradientData; - struct LayerShaderData { - Layer* layer; - GLuint bitmapSampler; - GLenum wrapS; - GLenum wrapT; - - Matrix4 textureTransform; - float textureDimension[2]; - } layerData; }; class SkiaShader { @@ -90,7 +76,8 @@ 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); + static void apply(Caches& caches, const SkiaShaderData& data, + const GLsizei width, const GLsizei height); }; }; // namespace uirenderer diff --git a/libs/hwui/Snapshot.cpp b/libs/hwui/Snapshot.cpp index 7c187fbda457..9d719bd1e997 100644 --- a/libs/hwui/Snapshot.cpp +++ b/libs/hwui/Snapshot.cpp @@ -30,14 +30,11 @@ Snapshot::Snapshot() , previous(nullptr) , layer(nullptr) , fbo(0) - , invisible(false) - , empty(false) , alpha(1.0f) , roundRectClipState(nullptr) , projectionPathMask(nullptr) , mClipArea(&mClipAreaRoot) { transform = &mTransformRoot; - region = nullptr; mRelativeLightCenter.x = mRelativeLightCenter.y = mRelativeLightCenter.z = 0; } @@ -50,8 +47,6 @@ Snapshot::Snapshot(Snapshot* s, int saveFlags) , previous(s) , layer(s->layer) , fbo(s->fbo) - , invisible(s->invisible) - , empty(false) , alpha(s->alpha) , roundRectClipState(s->roundRectClipState) , projectionPathMask(s->projectionPathMask) @@ -71,32 +66,20 @@ Snapshot::Snapshot(Snapshot* s, int saveFlags) } else { mClipArea = s->mClipArea; } - - if (s->flags & Snapshot::kFlagFboTarget) { - flags |= Snapshot::kFlagFboTarget; - region = s->region; - } else { - region = nullptr; - } } /////////////////////////////////////////////////////////////////////////////// // Clipping /////////////////////////////////////////////////////////////////////////////// -void Snapshot::clipRegionTransformed(const SkRegion& region, SkRegion::Op op) { - flags |= Snapshot::kFlagClipSet; - mClipArea->clipRegion(region, op); -} - -void Snapshot::clip(const Rect& localClip, SkRegion::Op op) { +void Snapshot::clip(const Rect& localClip, SkClipOp op) { flags |= Snapshot::kFlagClipSet; - mClipArea->clipRectWithTransform(localClip, transform, op); + mClipArea->clipRectWithTransform(localClip, transform, static_cast<SkRegion::Op>(op)); } -void Snapshot::clipPath(const SkPath& path, SkRegion::Op op) { +void Snapshot::clipPath(const SkPath& path, SkClipOp op) { flags |= Snapshot::kFlagClipSet; - mClipArea->clipPathWithTransform(path, transform, op); + mClipArea->clipPathWithTransform(path, transform, static_cast<SkRegion::Op>(op)); } void Snapshot::setClip(float left, float top, float right, float bottom) { @@ -127,58 +110,6 @@ void Snapshot::resetClip(float left, float top, float right, float bottom) { } /////////////////////////////////////////////////////////////////////////////// -// Transforms -/////////////////////////////////////////////////////////////////////////////// - -void Snapshot::resetTransform(float x, float y, float z) { -#if HWUI_NEW_OPS - LOG_ALWAYS_FATAL("not supported - light center managed differently"); -#else - // before resetting, map current light pos with inverse of current transform - Vector3 center = mRelativeLightCenter; - mat4 inverse; - inverse.loadInverse(*transform); - inverse.mapPoint3d(center); - mRelativeLightCenter = center; - - transform = &mTransformRoot; - transform->loadTranslate(x, y, z); -#endif -} - -void Snapshot::buildScreenSpaceTransform(Matrix4* outTransform) const { -#if HWUI_NEW_OPS - LOG_ALWAYS_FATAL("not supported - not needed by new ops"); -#else - // build (reverse ordered) list of the stack of snapshots, terminated with a NULL - Vector<const Snapshot*> snapshotList; - snapshotList.push(nullptr); - const Snapshot* current = this; - do { - snapshotList.push(current); - current = current->previous; - } while (current); - - // traverse the list, adding in each transform that contributes to the total transform - outTransform->loadIdentity(); - for (size_t i = snapshotList.size() - 1; i > 0; i--) { - // iterate down the stack - const Snapshot* current = snapshotList[i]; - const Snapshot* next = snapshotList[i - 1]; - if (current->flags & kFlagIsFboLayer) { - // if we've hit a layer, translate by the layer's draw offset - outTransform->translate(current->layer->layer.left, current->layer->layer.top); - } - if (!next || (next->flags & kFlagIsFboLayer)) { - // if this snapshot is last, or if this snapshot is last before an - // FBO layer (which reset the transform), apply it - outTransform->multiply(*(current->transform)); - } - } -#endif -} - -/////////////////////////////////////////////////////////////////////////////// // Clipping round rect /////////////////////////////////////////////////////////////////////////////// @@ -227,20 +158,8 @@ void Snapshot::setClippingRoundRect(LinearAllocator& allocator, const Rect& boun roundRectClipState = state; } -void Snapshot::setProjectionPathMask(LinearAllocator& allocator, const SkPath* path) { -#if HWUI_NEW_OPS - // TODO: remove allocator param for HWUI_NEW_OPS +void Snapshot::setProjectionPathMask(const SkPath* path) { projectionPathMask = path; -#else - if (path) { - ProjectionPathMask* mask = new (allocator) ProjectionPathMask; - mask->projectionMask = path; - buildScreenSpaceTransform(&(mask->projectionMaskTransform)); - projectionPathMask = mask; - } else { - projectionPathMask = nullptr; - } -#endif } static Snapshot* getClipRoot(Snapshot* target) { @@ -274,13 +193,9 @@ void Snapshot::applyClip(const ClipBase* recordedClip, const Matrix4& transform) // Queries /////////////////////////////////////////////////////////////////////////////// -bool Snapshot::isIgnored() const { - return invisible || empty; -} - void Snapshot::dump() const { - ALOGD("Snapshot %p, flags %x, prev %p, height %d, ignored %d, hasComplexClip %d", - this, flags, previous, getViewportHeight(), isIgnored(), !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()); diff --git a/libs/hwui/Snapshot.h b/libs/hwui/Snapshot.h index d8f926ef3925..8cd90a68a9bb 100644 --- a/libs/hwui/Snapshot.h +++ b/libs/hwui/Snapshot.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef ANDROID_HWUI_SNAPSHOT_H -#define ANDROID_HWUI_SNAPSHOT_H +#pragma once #include <GLES2/gl2.h> #include <GLES2/gl2ext.h> @@ -24,6 +23,7 @@ #include <utils/RefBase.h> #include <ui/Region.h> +#include <SkClipOp.h> #include <SkRegion.h> #include "ClipArea.h" @@ -63,18 +63,6 @@ public: float radius; }; -// TODO: remove for HWUI_NEW_OPS -class ProjectionPathMask { -public: - static void* operator new(size_t size) = delete; - static void* operator new(size_t size, LinearAllocator& allocator) { - return allocator.alloc<ProjectionPathMask>(size); - } - - const SkPath* projectionMask; - Matrix4 projectionMaskTransform; -}; - /** * A snapshot holds information about the current state of the rendering * surface. A snapshot is usually created whenever the user calls save() @@ -113,11 +101,6 @@ public: * restored when this snapshot is restored. */ kFlagIsFboLayer = 0x4, - /** - * Indicates that this snapshot or an ancestor snapshot is - * an FBO layer. - */ - kFlagFboTarget = 0x8, // TODO: remove for HWUI_NEW_OPS }; /** @@ -125,25 +108,19 @@ public: * the specified operation. The specified rectangle is transformed * by this snapshot's trasnformation. */ - void clip(const Rect& localClip, SkRegion::Op op); + void clip(const Rect& localClip, SkClipOp op); /** * Modifies the current clip with the new clip rectangle and * the specified operation. The specified rectangle is considered * already transformed. */ - void clipTransformed(const Rect& r, SkRegion::Op op = SkRegion::kIntersect_Op); - - /** - * Modifies the current clip with the specified region and operation. - * The specified region is considered already transformed. - */ - void clipRegionTransformed(const SkRegion& region, SkRegion::Op op); + void clipTransformed(const Rect& r, SkClipOp op = SkClipOp::kIntersect); /** * Modifies the current clip with the specified path and operation. */ - void clipPath(const SkPath& path, SkRegion::Op op); + void clipPath(const SkPath& path, SkClipOp op); /** * Sets the current clip. @@ -179,11 +156,6 @@ public: */ void resetClip(float left, float top, float right, float bottom); - /** - * Resets the current transform to a pure 3D translation. - */ - void resetTransform(float x, float y, float z); - void initializeViewport(int width, int height) { mViewportData.initialize(width, height); mClipAreaRoot.setViewportDimensions(width, height); @@ -207,13 +179,7 @@ public: /** * Sets (and replaces) the current projection mask */ - void setProjectionPathMask(LinearAllocator& allocator, const SkPath* path); - - /** - * Indicates whether this snapshot should be ignored. A snapshot - * is typically ignored if its layer is invisible or empty. - */ - bool isIgnored() const; + void setProjectionPathMask(const SkPath* path); /** * Indicates whether the current transform has perspective components. @@ -221,13 +187,6 @@ public: bool hasPerspectiveTransform() const; /** - * Fills outTransform with the current, total transform to screen space, - * across layer boundaries. - */ - // TODO: remove for HWUI_NEW_OPS - void buildScreenSpaceTransform(Matrix4* outTransform) const; - - /** * Dirty flags. */ int flags; @@ -251,19 +210,6 @@ public: GLuint fbo; /** - * Indicates that this snapshot is invisible and nothing should be drawn - * inside it. This flag is set only when the layer clips drawing to its - * bounds and is passed to subsequent snapshots. - */ - bool invisible; - - /** - * If set to true, the layer will not be composited. This is similar to - * invisible but this flag is not passed to subsequent snapshots. - */ - bool empty; - - /** * Local transformation. Holds the current translation, scale and * rotation values. * @@ -273,14 +219,6 @@ public: mat4* transform; /** - * The ancestor layer's dirty region. - * - * This is a reference to a region owned by a layer. This pointer must - * not be freed. - */ - Region* region; - - /** * Current alpha value. This value is 1 by default, but may be set by a DisplayList which * has translucent rendering in a non-overlapping View. This value will be used by * the renderer to set the alpha in the current color being used for ensuing drawing @@ -302,11 +240,7 @@ public: /** * Current projection masking path - used exclusively to mask projected, tessellated circles. */ -#if HWUI_NEW_OPS const SkPath* projectionPathMask; -#else - const ProjectionPathMask* projectionPathMask; -#endif void dump() const; @@ -345,5 +279,3 @@ private: }; // namespace uirenderer }; // namespace android - -#endif // ANDROID_HWUI_SNAPSHOT_H diff --git a/libs/hwui/SpotShadow.cpp b/libs/hwui/SpotShadow.cpp index cc96a137c306..7b0a1bc3e93e 100644 --- a/libs/hwui/SpotShadow.cpp +++ b/libs/hwui/SpotShadow.cpp @@ -302,21 +302,6 @@ bool SpotShadow::testPointInsidePolygon(const Vector2 testPoint, } /** - * Make the polygon turn clockwise. - * - * @param polygon the polygon as a Vector2 array. - * @param len the number of points of the polygon - */ -void SpotShadow::makeClockwise(Vector2* polygon, int len) { - if (polygon == nullptr || len == 0) { - return; - } - if (!ShadowTessellator::isClockwise(polygon, len)) { - reverse(polygon, len); - } -} - -/** * Reverse the polygon * * @param polygon the polygon as a Vector2 array diff --git a/libs/hwui/SpotShadow.h b/libs/hwui/SpotShadow.h index 62a7e5dc8a3e..6108bb6bd585 100644 --- a/libs/hwui/SpotShadow.h +++ b/libs/hwui/SpotShadow.h @@ -54,7 +54,6 @@ private: static void quicksortX(Vector2* points, int low, int high); static bool testPointInsidePolygon(const Vector2 testPoint, const Vector2* poly, int len); - static void makeClockwise(Vector2* polygon, int len); static void reverse(Vector2* polygon, int len); static void generateTriangleStrip(bool isCasterOpaque, float shadowStrengthScale, diff --git a/libs/hwui/TessellationCache.cpp b/libs/hwui/TessellationCache.cpp index d9e811684610..91e7ac39af90 100644 --- a/libs/hwui/TessellationCache.cpp +++ b/libs/hwui/TessellationCache.cpp @@ -18,7 +18,6 @@ #include <utils/Trace.h> #include "Caches.h" -#include "OpenGLRenderer.h" #include "PathTessellator.h" #include "ShadowTessellator.h" #include "TessellationCache.h" @@ -369,21 +368,6 @@ void TessellationCache::precacheShadows(const Matrix4* drawTransform, const Rect mShadowCache.put(key, task.get()); } -void TessellationCache::getShadowBuffers(const Matrix4* drawTransform, const Rect& localClip, - bool opaque, const SkPath* casterPerimeter, - const Matrix4* transformXY, const Matrix4* transformZ, - const Vector3& lightCenter, float lightRadius, vertexBuffer_pair_t& outBuffers) { - ShadowDescription key(casterPerimeter, drawTransform); - ShadowTask* task = static_cast<ShadowTask*>(mShadowCache.get(key)); - if (!task) { - 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"); - outBuffers = task->getResult(); -} - sp<TessellationCache::ShadowTask> TessellationCache::getShadowTask( const Matrix4* drawTransform, const Rect& localClip, bool opaque, const SkPath* casterPerimeter, diff --git a/libs/hwui/TessellationCache.h b/libs/hwui/TessellationCache.h index 6141b4ef63d7..ccad1b7bd415 100644 --- a/libs/hwui/TessellationCache.h +++ b/libs/hwui/TessellationCache.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef ANDROID_HWUI_TESSELLATION_CACHE_H -#define ANDROID_HWUI_TESSELLATION_CACHE_H +#pragma once #include "Debug.h" #include "Matrix.h" @@ -161,17 +160,6 @@ public: const VertexBuffer* getRoundRect(const Matrix4& transform, const SkPaint& paint, float width, float height, float rx, float ry); - // TODO: delete these when switching to HWUI_NEW_OPS - 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 getShadowBuffers(const Matrix4* drawTransform, const Rect& localClip, - bool opaque, const SkPath* casterPerimeter, - const Matrix4* transformXY, const Matrix4* transformZ, - const Vector3& lightCenter, float lightRadius, - vertexBuffer_pair_t& outBuffers); - sp<ShadowTask> getShadowTask(const Matrix4* drawTransform, const Rect& localClip, bool opaque, const SkPath* casterPerimeter, const Matrix4* transformXY, const Matrix4* transformZ, @@ -184,6 +172,11 @@ 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); + Buffer* getRectBuffer(const Matrix4& transform, const SkPaint& paint, float width, float height); Buffer* getRoundRectBuffer(const Matrix4& transform, const SkPaint& paint, @@ -232,5 +225,3 @@ void tessellateShadows( }; // namespace uirenderer }; // namespace android - -#endif // ANDROID_HWUI_PATH_CACHE_H diff --git a/libs/hwui/Texture.cpp b/libs/hwui/Texture.cpp index 4f49a3518be0..705395e1e2b7 100644 --- a/libs/hwui/Texture.cpp +++ b/libs/hwui/Texture.cpp @@ -26,71 +26,85 @@ namespace android { 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 16; + return 8; default: LOG_ALWAYS_FATAL("UNKNOWN FORMAT %d", glFormat); } } -void Texture::setWrapST(GLenum wrapS, GLenum wrapT, bool bindTexture, bool force, - GLenum renderTarget) { +void Texture::setWrapST(GLenum wrapS, GLenum wrapT, bool bindTexture, bool force) { if (force || wrapS != mWrapS || wrapT != mWrapT) { mWrapS = wrapS; mWrapT = wrapT; if (bindTexture) { - mCaches.textureState().bindTexture(renderTarget, mId); + mCaches.textureState().bindTexture(mTarget, mId); } - glTexParameteri(renderTarget, GL_TEXTURE_WRAP_S, wrapS); - glTexParameteri(renderTarget, GL_TEXTURE_WRAP_T, wrapT); + glTexParameteri(mTarget, GL_TEXTURE_WRAP_S, wrapS); + glTexParameteri(mTarget, GL_TEXTURE_WRAP_T, wrapT); } } -void Texture::setFilterMinMag(GLenum min, GLenum mag, bool bindTexture, bool force, - GLenum renderTarget) { - +void Texture::setFilterMinMag(GLenum min, GLenum mag, bool bindTexture, bool force) { if (force || min != mMinFilter || mag != mMagFilter) { mMinFilter = min; mMagFilter = mag; if (bindTexture) { - mCaches.textureState().bindTexture(renderTarget, mId); + mCaches.textureState().bindTexture(mTarget, mId); } if (mipMap && min == GL_LINEAR) min = GL_LINEAR_MIPMAP_LINEAR; - glTexParameteri(renderTarget, GL_TEXTURE_MIN_FILTER, min); - glTexParameteri(renderTarget, GL_TEXTURE_MAG_FILTER, mag); + glTexParameteri(mTarget, GL_TEXTURE_MIN_FILTER, min); + glTexParameteri(mTarget, GL_TEXTURE_MAG_FILTER, mag); } } void Texture::deleteTexture() { mCaches.textureState().deleteTexture(mId); mId = 0; + mTarget = GL_NONE; + if (mEglImageHandle != EGL_NO_IMAGE_KHR) { + EGLDisplay eglDisplayHandle = eglGetCurrentDisplay(); + eglDestroyImageKHR(eglDisplayHandle, mEglImageHandle); + mEglImageHandle = EGL_NO_IMAGE_KHR; + } } -bool Texture::updateSize(uint32_t width, uint32_t height, GLint format) { - if (mWidth == width && mHeight == height && mFormat == format) { +bool Texture::updateSize(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; mHeight = height; mFormat = format; - notifySizeChanged(mWidth * mHeight * bytesPerPixel(mFormat)); + mInternalFormat = internalFormat; + mTarget = target; + notifySizeChanged(mWidth * mHeight * bytesPerPixel(internalFormat)); return true; } @@ -101,10 +115,10 @@ void Texture::resetCachedParams() { mMagFilter = GL_LINEAR; } -void Texture::upload(GLint internalformat, uint32_t width, uint32_t height, +void Texture::upload(GLint internalFormat, uint32_t width, uint32_t height, GLenum format, GLenum type, const void* pixels) { GL_CHECKPOINT(MODERATE); - bool needsAlloc = updateSize(width, height, internalformat); + bool needsAlloc = updateSize(width, height, internalFormat, format, GL_TEXTURE_2D); if (!mId) { glGenTextures(1, &mId); needsAlloc = true; @@ -112,17 +126,28 @@ 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, mFormat, mWidth, mHeight, 0, + glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, mWidth, mHeight, 0, format, type, pixels); } else if (pixels) { - glTexSubImage2D(GL_TEXTURE_2D, 0, mFormat, mWidth, mHeight, 0, + glTexSubImage2D(GL_TEXTURE_2D, 0, internalFormat, mWidth, mHeight, 0, format, type, pixels); } GL_CHECKPOINT(MODERATE); } -static void uploadToTexture(bool resize, GLenum format, GLenum type, GLsizei stride, GLsizei bpp, - GLsizei width, GLsizei height, const GLvoid * data) { +void Texture::uploadHardwareBitmapToTexture(GraphicBuffer* buffer) { + EGLDisplay eglDisplayHandle = eglGetCurrentDisplay(); + if (mEglImageHandle != EGL_NO_IMAGE_KHR) { + eglDestroyImageKHR(eglDisplayHandle, mEglImageHandle); + mEglImageHandle = EGL_NO_IMAGE_KHR; + } + mEglImageHandle = eglCreateImageKHR(eglDisplayHandle, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, + 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(); @@ -132,7 +157,7 @@ static void uploadToTexture(bool resize, GLenum format, GLenum type, GLsizei str } if (resize) { - glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, type, data); + glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height, 0, format, type, data); } else { glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, format, type, data); } @@ -156,7 +181,7 @@ static void uploadToTexture(bool resize, GLenum format, GLenum type, GLsizei str } if (resize) { - glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, type, temp); + glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height, 0, format, type, temp); } else { glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, format, type, temp); } @@ -165,43 +190,72 @@ static void uploadToTexture(bool resize, GLenum format, GLenum type, GLsizei str } } -static void uploadSkBitmapToTexture(const SkBitmap& bitmap, - bool resize, GLenum format, GLenum type) { - uploadToTexture(resize, format, type, bitmap.rowBytesAsPixels(), bitmap.bytesPerPixel(), - bitmap.width(), bitmap.height(), bitmap.getPixels()); -} - -static void colorTypeToGlFormatAndType(SkColorType colorType, - 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: - *outFormat = GL_RGB; - *outType = GL_UNSIGNED_SHORT_5_6_5; + 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 and Index_8 are both upconverted to RGBA_8888 case kARGB_4444_SkColorType: case kIndex_8_SkColorType: case kN32_SkColorType: *outFormat = GL_RGBA; + *outInternalFormat = caches.rgbaInternalFormat(needSRGB); *outType = GL_UNSIGNED_BYTE; break; case kGray_8_SkColorType: + // TODO: Handle sRGB *outFormat = GL_LUMINANCE; + *outInternalFormat = GL_LUMINANCE; *outType = GL_UNSIGNED_BYTE; break; + case kRGBA_F16_SkColorType: + // This format is always linear + *outFormat = GL_RGBA; + *outInternalFormat = GL_RGBA16F; + *outType = GL_HALF_FLOAT; + break; default: LOG_ALWAYS_FATAL("Unsupported bitmap colorType: %d", colorType); break; } } -void Texture::upload(const SkBitmap& bitmap) { - SkAutoLockPixels alp(bitmap); +SkBitmap Texture::uploadToN32(const SkBitmap& bitmap, bool hasSRGB, sk_sp<SkColorSpace> sRGB) { + SkBitmap rgbaBitmap; + rgbaBitmap.allocPixels(SkImageInfo::MakeN32(bitmap.width(), bitmap.height(), + bitmap.info().alphaType(), hasSRGB ? sRGB : nullptr)); + rgbaBitmap.eraseColor(0); + SkCanvas canvas(rgbaBitmap); + canvas.drawBitmap(bitmap, 0.0f, 0.0f, nullptr); + return rgbaBitmap; +} + +bool Texture::hasUnsupportedColorType(const SkImageInfo& info, bool hasSRGB, SkColorSpace* sRGB) { + bool needSRGB = info.colorSpace() == sRGB; + return info.colorType() == kARGB_4444_SkColorType + || info.colorType() == kIndex_8_SkColorType + || (info.colorType() == kRGB_565_SkColorType && hasSRGB && needSRGB); +} + +void Texture::upload(Bitmap& bitmap) { if (!bitmap.readyToDraw()) { ALOGE("Cannot generate texture from bitmap"); return; @@ -224,29 +278,32 @@ void Texture::upload(const SkBitmap& bitmap) { setDefaultParams = true; } - GLint format, type; - colorTypeToGlFormatAndType(bitmap.colorType(), &format, &type); - - if (updateSize(bitmap.width(), bitmap.height(), format)) { - needsAlloc = true; - } - - blend = !bitmap.isOpaque(); - mCaches.textureState().bindTexture(mId); + sk_sp<SkColorSpace> sRGB = SkColorSpace::MakeNamed(SkColorSpace::kSRGB_Named); + bool needSRGB = bitmap.info().colorSpace() == sRGB.get(); - if (CC_UNLIKELY(bitmap.colorType() == kARGB_4444_SkColorType - || bitmap.colorType() == kIndex_8_SkColorType)) { - SkBitmap rgbaBitmap; - rgbaBitmap.allocPixels(SkImageInfo::MakeN32(mWidth, mHeight, - bitmap.alphaType())); - rgbaBitmap.eraseColor(0); + GLint internalFormat, format, type; + colorTypeToGlFormatAndType(mCaches, bitmap.colorType(), needSRGB, &internalFormat, &format, &type); - SkCanvas canvas(rgbaBitmap); - canvas.drawBitmap(bitmap, 0.0f, 0.0f, nullptr); + GLenum target = bitmap.isHardware() ? GL_TEXTURE_EXTERNAL_OES : GL_TEXTURE_2D; + needsAlloc |= updateSize(bitmap.width(), bitmap.height(), internalFormat, format, target); - uploadSkBitmapToTexture(rgbaBitmap, needsAlloc, format, type); + blend = !bitmap.isOpaque(); + mCaches.textureState().bindTexture(mTarget, mId); + + // TODO: Handle sRGB gray bitmaps + bool hasSRGB = mCaches.extensions().hasSRGB(); + if (CC_UNLIKELY(hasUnsupportedColorType(bitmap.info(), hasSRGB, sRGB.get()))) { + SkBitmap skBitmap; + bitmap.getSkBitmap(&skBitmap); + SkBitmap rgbaBitmap = uploadToN32(skBitmap, hasSRGB, std::move(sRGB)); + uploadToTexture(needsAlloc, internalFormat, format, type, rgbaBitmap.rowBytesAsPixels(), + rgbaBitmap.bytesPerPixel(), rgbaBitmap.width(), + rgbaBitmap.height(), rgbaBitmap.getPixels()); + } else if (bitmap.isHardware()) { + uploadHardwareBitmapToTexture(bitmap.graphicBuffer()); } else { - uploadSkBitmapToTexture(bitmap, needsAlloc, format, type); + uploadToTexture(needsAlloc, internalFormat, format, type, bitmap.rowBytesAsPixels(), + bitmap.info().bytesPerPixel(), bitmap.width(), bitmap.height(), bitmap.pixels()); } if (canMipMap) { @@ -262,11 +319,14 @@ void Texture::upload(const SkBitmap& bitmap) { } } -void Texture::wrap(GLuint id, uint32_t width, uint32_t height, GLint format) { +void Texture::wrap(GLuint id, uint32_t width, uint32_t height, + GLint internalFormat, GLint format, GLenum target) { mId = id; mWidth = width; mHeight = height; mFormat = format; + mInternalFormat = internalFormat; + mTarget = target; // We're wrapping an existing texture, so don't double count this memory notifySizeChanged(0); } diff --git a/libs/hwui/Texture.h b/libs/hwui/Texture.h index b72742f45654..b8397ccade9c 100644 --- a/libs/hwui/Texture.h +++ b/libs/hwui/Texture.h @@ -18,11 +18,17 @@ #define ANDROID_HWUI_TEXTURE_H #include "GpuMemoryTracker.h" +#include "hwui/Bitmap.h" #include <GLES2/gl2.h> +#include <EGL/egl.h> +#include <EGL/eglext.h> #include <SkBitmap.h> namespace android { + +class GraphicBuffer; + namespace uirenderer { class Caches; @@ -34,6 +40,11 @@ class Layer; */ class Texture : public GpuMemoryTracker { public: + static SkBitmap uploadToN32(const SkBitmap& bitmap, bool hasSRGB, sk_sp<SkColorSpace> sRGB); + static bool hasUnsupportedColorType(const SkImageInfo& info, bool hasSRGB, SkColorSpace* sRGB); + static void colorTypeToGlFormatAndType(const Caches& caches, SkColorType colorType, + bool needSRGB, GLint* outInternalFormat, GLint* outFormat, GLint* outType); + explicit Texture(Caches& caches) : GpuMemoryTracker(GpuObjectType::Texture) , mCaches(caches) @@ -41,21 +52,19 @@ public: virtual ~Texture() { } - inline void setWrap(GLenum wrap, bool bindTexture = false, bool force = false, - GLenum renderTarget = GL_TEXTURE_2D) { - setWrapST(wrap, wrap, bindTexture, force, renderTarget); + 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, GLenum renderTarget = GL_TEXTURE_2D); + bool force = false); - inline void setFilter(GLenum filter, bool bindTexture = false, bool force = false, - GLenum renderTarget = GL_TEXTURE_2D) { - setFilterMinMag(filter, filter, bindTexture, force, renderTarget); + 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, GLenum renderTarget = GL_TEXTURE_2D); + bool force = false); /** * Convenience method to call glDeleteTextures() on this texture's id. @@ -69,29 +78,30 @@ public: * * The image data is undefined after calling this. */ - void resize(uint32_t width, uint32_t height, GLint format) { - upload(format, width, height, format, GL_UNSIGNED_BYTE, nullptr); + void resize(uint32_t width, uint32_t height, GLint internalFormat, GLint format) { + upload(internalFormat, width, height, format, GL_UNSIGNED_BYTE, nullptr); } /** - * Updates this Texture with the contents of the provided SkBitmap, + * Updates this Texture with the contents of the provided Bitmap, * also setting the appropriate width, height, and format. It is not necessary * to call resize() prior to this. * - * Note this does not set the generation from the SkBitmap. + * Note this does not set the generation from the Bitmap. */ - void upload(const SkBitmap& source); + void upload(Bitmap& source); /** * Basically glTexImage2D/glTexSubImage2D. */ - void upload(GLint internalformat, uint32_t width, uint32_t height, + 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 format); + void wrap(GLuint id, uint32_t width, uint32_t height, GLint internalFormat, + GLint format, GLenum target); GLuint id() const { return mId; @@ -109,6 +119,14 @@ public: return mFormat; } + GLint internalFormat() const { + return mInternalFormat; + } + + GLenum target() const { + return mTarget; + } + /** * Generation of the backing bitmap, */ @@ -140,21 +158,25 @@ public: * the current frame. This is reset at the start of a new frame. */ void* isInUse = nullptr; - private: - // TODO: Temporarily grant private access to Layer, remove once - // Layer can be de-tangled from being a dual-purpose render target + // TODO: Temporarily grant private access to GlLayer, remove once + // GlLayer can be de-tangled from being a dual-purpose render target // and external texture wrapper - friend class Layer; + friend class GlLayer; // Returns true if the size changed, false if it was the same - bool updateSize(uint32_t width, uint32_t height, GLint format); + bool updateSize(uint32_t width, uint32_t height, GLint internalFormat, + GLint format, GLenum target); + void uploadHardwareBitmapToTexture(GraphicBuffer* buffer); void resetCachedParams(); GLuint mId = 0; uint32_t mWidth = 0; uint32_t mHeight = 0; GLint mFormat = 0; + GLint mInternalFormat = 0; + GLenum mTarget = GL_NONE; + EGLImageKHR mEglImageHandle = EGL_NO_IMAGE_KHR; /* See GLES spec section 3.8.14 * "In the initial state, the value assigned to TEXTURE_MIN_FILTER is diff --git a/libs/hwui/TextureCache.cpp b/libs/hwui/TextureCache.cpp index 523924af5ef1..14ffc85a4f68 100644 --- a/libs/hwui/TextureCache.cpp +++ b/libs/hwui/TextureCache.cpp @@ -18,12 +18,12 @@ #include <utils/Mutex.h> -#include "AssetAtlas.h" #include "Caches.h" #include "Texture.h" #include "TextureCache.h" #include "Properties.h" #include "utils/TraceUtils.h" +#include "hwui/Bitmap.h" namespace android { namespace uirenderer { @@ -36,8 +36,7 @@ TextureCache::TextureCache() : mCache(LruCache<uint32_t, Texture*>::kUnlimitedCapacity) , mSize(0) , mMaxSize(Properties::textureCacheSize) - , mFlushRate(Properties::textureCacheFlushRate) - , mAssetAtlas(nullptr) { + , mFlushRate(Properties::textureCacheFlushRate) { mCache.setOnEntryRemovedListener(this); glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize); @@ -84,10 +83,6 @@ void TextureCache::operator()(uint32_t&, Texture*& texture) { // Caching /////////////////////////////////////////////////////////////////////////////// -void TextureCache::setAssetAtlas(AssetAtlas* assetAtlas) { - mAssetAtlas = assetAtlas; -} - void TextureCache::resetMarkInUse(void* ownerToken) { LruCache<uint32_t, Texture*>::Iterator iter(mCache); while (iter.next()) { @@ -97,7 +92,7 @@ void TextureCache::resetMarkInUse(void* ownerToken) { } } -bool TextureCache::canMakeTextureFromBitmap(const SkBitmap* bitmap) { +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); @@ -108,15 +103,8 @@ bool TextureCache::canMakeTextureFromBitmap(const SkBitmap* bitmap) { // Returns a prepared Texture* that either is already in the cache or can fit // in the cache (and is thus added to the cache) -Texture* TextureCache::getCachedTexture(const SkBitmap* bitmap, AtlasUsageType atlasUsageType) { - if (CC_LIKELY(mAssetAtlas != nullptr) && atlasUsageType == AtlasUsageType::Use) { - AssetAtlas::Entry* entry = mAssetAtlas->getEntry(bitmap->pixelRef()); - if (CC_UNLIKELY(entry)) { - return entry->texture; - } - } - - Texture* texture = mCache.get(bitmap->pixelRef()->getStableID()); +Texture* TextureCache::getCachedTexture(Bitmap* bitmap) { + Texture* texture = mCache.get(bitmap->getStableID()); if (!texture) { if (!canMakeTextureFromBitmap(bitmap)) { @@ -147,7 +135,7 @@ Texture* TextureCache::getCachedTexture(const SkBitmap* bitmap, AtlasUsageType a if (mDebugEnabled) { ALOGD("Texture created, size = %d", size); } - mCache.put(bitmap->pixelRef()->getStableID(), texture); + mCache.put(bitmap->getStableID(), texture); } } else if (!texture->isInUse && bitmap->getGenerationID() != texture->generation) { // Texture was in the cache but is dirty, re-upload @@ -159,20 +147,20 @@ Texture* TextureCache::getCachedTexture(const SkBitmap* bitmap, AtlasUsageType a return texture; } -bool TextureCache::prefetchAndMarkInUse(void* ownerToken, const SkBitmap* bitmap) { - Texture* texture = getCachedTexture(bitmap, AtlasUsageType::Use); +bool TextureCache::prefetchAndMarkInUse(void* ownerToken, Bitmap* bitmap) { + Texture* texture = getCachedTexture(bitmap); if (texture) { texture->isInUse = ownerToken; } return texture; } -bool TextureCache::prefetch(const SkBitmap* bitmap) { - return getCachedTexture(bitmap, AtlasUsageType::Use); +bool TextureCache::prefetch(Bitmap* bitmap) { + return getCachedTexture(bitmap); } -Texture* TextureCache::get(const SkBitmap* bitmap, AtlasUsageType atlasUsageType) { - Texture* texture = getCachedTexture(bitmap, atlasUsageType); +Texture* TextureCache::get(Bitmap* bitmap) { + Texture* texture = getCachedTexture(bitmap); if (!texture) { if (!canMakeTextureFromBitmap(bitmap)) { diff --git a/libs/hwui/TextureCache.h b/libs/hwui/TextureCache.h index 0a61b6b1a522..68a548bdb1ff 100644 --- a/libs/hwui/TextureCache.h +++ b/libs/hwui/TextureCache.h @@ -28,6 +28,9 @@ #include <unordered_map> namespace android { + +class Bitmap; + namespace uirenderer { class Texture; @@ -47,8 +50,6 @@ class Texture; // Classes /////////////////////////////////////////////////////////////////////////////// -class AssetAtlas; - /** * A simple LRU texture cache. The cache has a maximum size expressed in bytes. * Any texture added to the cache causing the cache to grow beyond the maximum @@ -75,30 +76,20 @@ public: * acquired for the bitmap, false otherwise. If a Texture was acquired it is * marked as in use. */ - bool prefetchAndMarkInUse(void* ownerToken, const SkBitmap* bitmap); + bool prefetchAndMarkInUse(void* ownerToken, Bitmap* bitmap); /** * Attempts to precache the SkBitmap. Returns true if a Texture was successfully * acquired for the bitmap, false otherwise. Does not mark the Texture * as in use and won't update currently in-use Textures. */ - bool prefetch(const SkBitmap* bitmap); + bool prefetch(Bitmap* bitmap); /** - * Returns the texture associated with the specified bitmap from either within the cache, or - * the AssetAtlas. If the texture cannot be found in the cache, a new texture is generated. + * Returns the texture associated with the specified bitmap from within the cache. + * If the texture cannot be found in the cache, a new texture is generated. */ - Texture* get(const SkBitmap* bitmap) { - return get(bitmap, AtlasUsageType::Use); - } - - /** - * Returns the texture associated with the specified bitmap. If the texture cannot be found in - * the cache, a new texture is generated, even if it resides in the AssetAtlas. - */ - Texture* getAndBypassAtlas(const SkBitmap* bitmap) { - return get(bitmap, AtlasUsageType::Bypass); - } + Texture* get(Bitmap* bitmap); /** * Removes the texture associated with the specified pixelRef. This is meant @@ -130,18 +121,10 @@ public: */ void flush(); - void setAssetAtlas(AssetAtlas* assetAtlas); - private: - enum class AtlasUsageType { - Use, - Bypass, - }; + bool canMakeTextureFromBitmap(Bitmap* bitmap); - bool canMakeTextureFromBitmap(const SkBitmap* bitmap); - - Texture* get(const SkBitmap* bitmap, AtlasUsageType atlasUsageType); - Texture* getCachedTexture(const SkBitmap* bitmap, AtlasUsageType atlasUsageType); + Texture* getCachedTexture(Bitmap* bitmap); LruCache<uint32_t, Texture*> mCache; @@ -155,8 +138,6 @@ private: std::vector<uint32_t> mGarbage; mutable Mutex mLock; - - AssetAtlas* mAssetAtlas; }; // class TextureCache }; // namespace uirenderer diff --git a/libs/hwui/TreeInfo.h b/libs/hwui/TreeInfo.h index ac2bdccf5255..749efdd26927 100644 --- a/libs/hwui/TreeInfo.h +++ b/libs/hwui/TreeInfo.h @@ -13,8 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#ifndef TREEINFO_H -#define TREEINFO_H + +#pragma once #include "utils/Macros.h" @@ -31,7 +31,6 @@ class CanvasContext; class DamageAccumulator; class LayerUpdateQueue; -class OpenGLRenderer; class RenderNode; class RenderState; @@ -89,13 +88,7 @@ public: // Must not be null during actual usage DamageAccumulator* damageAccumulator = nullptr; -#if HWUI_NEW_OPS LayerUpdateQueue* layerUpdateQueue = nullptr; -#else - // The renderer that will be drawing the next frame. Use this to push any - // layer updates or similar. May be NULL. - OpenGLRenderer* renderer = nullptr; -#endif ErrorHandler* errorHandler = nullptr; // Optional, may be nullptr. Used to allow things to observe interesting @@ -123,10 +116,11 @@ public: bool canDrawThisFrame = true; } out; + // This flag helps to disable projection for receiver nodes that do not have any backward + // projected children. + bool hasBackwardProjectedNodes = false; // TODO: Damage calculations }; } /* namespace uirenderer */ } /* namespace android */ - -#endif /* TREEINFO_H */ diff --git a/libs/hwui/VectorDrawable.cpp b/libs/hwui/VectorDrawable.cpp index 223605fa34ed..208107f65743 100644 --- a/libs/hwui/VectorDrawable.cpp +++ b/libs/hwui/VectorDrawable.cpp @@ -201,10 +201,7 @@ void FullPath::drawPath(SkCanvas* outCanvas, SkPath& renderPath, float strokeSca SkPaint paint; if (properties.getFillGradient() != nullptr) { paint.setColor(applyAlpha(SK_ColorBLACK, properties.getFillAlpha())); - SkShader* newShader = properties.getFillGradient()->newWithLocalMatrix(matrix); - // newWithLocalMatrix(...) creates a new SkShader and returns a bare pointer. We need to - // remove the extra ref so that the ref count is correctly managed. - paint.setShader(newShader)->unref(); + paint.setShader(properties.getFillGradient()->makeWithLocalMatrix(matrix)); needsFill = true; } else if (properties.getFillColor() != SK_ColorTRANSPARENT) { paint.setColor(applyAlpha(properties.getFillColor(), properties.getFillAlpha())); @@ -223,10 +220,7 @@ void FullPath::drawPath(SkCanvas* outCanvas, SkPath& renderPath, float strokeSca bool needsStroke = false; if (properties.getStrokeGradient() != nullptr) { paint.setColor(applyAlpha(SK_ColorBLACK, properties.getStrokeAlpha())); - SkShader* newShader = properties.getStrokeGradient()->newWithLocalMatrix(matrix); - // newWithLocalMatrix(...) creates a new SkShader and returns a bare pointer. We need to - // remove the extra ref so that the ref count is correctly managed. - paint.setShader(newShader)->unref(); + paint.setShader(properties.getStrokeGradient()->makeWithLocalMatrix(matrix)); needsStroke = true; } else if (properties.getStrokeColor() != SK_ColorTRANSPARENT) { paint.setColor(applyAlpha(properties.getStrokeColor(), properties.getStrokeAlpha())); @@ -314,7 +308,7 @@ void FullPath::FullPathProperties::setPropertyValue(int propertyId, float value) void ClipPath::drawPath(SkCanvas* outCanvas, SkPath& renderPath, float strokeScale, const SkMatrix& matrix, bool useStagingData){ - outCanvas->clipPath(renderPath, SkRegion::kIntersect_Op); + outCanvas->clipPath(renderPath); } Group::Group(const Group& group) : Node(group) { @@ -508,18 +502,18 @@ int Tree::draw(Canvas* outCanvas, SkColorFilter* colorFilter, } void Tree::drawStaging(Canvas* outCanvas) { - bool redrawNeeded = allocateBitmapIfNeeded(&mStagingCache.bitmap, + bool redrawNeeded = allocateBitmapIfNeeded(mStagingCache, mStagingProperties.getScaledWidth(), mStagingProperties.getScaledHeight()); // draw bitmap cache if (redrawNeeded || mStagingCache.dirty) { - updateBitmapCache(&mStagingCache.bitmap, true); + updateBitmapCache(*mStagingCache.bitmap, true); mStagingCache.dirty = false; } SkPaint tmpPaint; SkPaint* paint = updatePaint(&tmpPaint, &mStagingProperties); - outCanvas->drawBitmap(mStagingCache.bitmap, 0, 0, - mStagingCache.bitmap.width(), mStagingCache.bitmap.height(), + 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); } @@ -534,49 +528,53 @@ SkPaint* Tree::updatePaint(SkPaint* outPaint, TreeProperties* prop) { if (prop->getRootAlpha() == 1.0f && prop->getColorFilter() == nullptr) { return nullptr; } else { - outPaint->setColorFilter(prop->getColorFilter()); + outPaint->setColorFilter(sk_ref_sp(prop->getColorFilter())); outPaint->setFilterQuality(kLow_SkFilterQuality); outPaint->setAlpha(prop->getRootAlpha() * 255); return outPaint; } } -const SkBitmap& Tree::getBitmapUpdateIfDirty() { - bool redrawNeeded = allocateBitmapIfNeeded(&mCache.bitmap, mProperties.getScaledWidth(), +Bitmap& Tree::getBitmapUpdateIfDirty() { + bool redrawNeeded = allocateBitmapIfNeeded(mCache, mProperties.getScaledWidth(), mProperties.getScaledHeight()); if (redrawNeeded || mCache.dirty) { - updateBitmapCache(&mCache.bitmap, false); + updateBitmapCache(*mCache.bitmap, false); mCache.dirty = false; } - return mCache.bitmap; + return *mCache.bitmap; } -void Tree::updateBitmapCache(SkBitmap* outCache, bool useStagingData) { - outCache->eraseColor(SK_ColorTRANSPARENT); - SkCanvas outCanvas(*outCache); +void Tree::updateBitmapCache(Bitmap& bitmap, bool useStagingData) { + SkBitmap outCache; + bitmap.getSkBitmap(&outCache); + outCache.eraseColor(SK_ColorTRANSPARENT); + SkCanvas outCanvas(outCache); float viewportWidth = useStagingData ? mStagingProperties.getViewportWidth() : mProperties.getViewportWidth(); float viewportHeight = useStagingData ? mStagingProperties.getViewportHeight() : mProperties.getViewportHeight(); - float scaleX = outCache->width() / viewportWidth; - float scaleY = outCache->height() / viewportHeight; + float scaleX = outCache.width() / viewportWidth; + float scaleY = outCache.height() / viewportHeight; mRootNode->draw(&outCanvas, SkMatrix::I(), scaleX, scaleY, useStagingData); } -bool Tree::allocateBitmapIfNeeded(SkBitmap* outCache, int width, int height) { - if (!canReuseBitmap(*outCache, width, height)) { - SkImageInfo info = SkImageInfo::Make(width, height, - kN32_SkColorType, kPremul_SkAlphaType); - outCache->setInfo(info); - // TODO: Count the bitmap cache against app's java heap - outCache->allocPixels(info); +bool Tree::allocateBitmapIfNeeded(Cache& cache, int width, int height) { + if (!canReuseBitmap(cache.bitmap.get(), width, height)) { +#ifndef ANDROID_ENABLE_LINEAR_BLENDING + sk_sp<SkColorSpace> colorSpace = nullptr; +#else + sk_sp<SkColorSpace> colorSpace = SkColorSpace::MakeNamed(SkColorSpace::kSRGB_Named); +#endif + SkImageInfo info = SkImageInfo::MakeN32(width, height, kPremul_SkAlphaType, colorSpace); + cache.bitmap = Bitmap::allocateHeapBitmap(info); return true; } return false; } -bool Tree::canReuseBitmap(const SkBitmap& bitmap, int width, int height) { - return width <= bitmap.width() && height <= bitmap.height(); +bool Tree::canReuseBitmap(Bitmap* bitmap, int width, int height) { + return bitmap && width <= bitmap->width() && height <= bitmap->height(); } void Tree::onPropertyChanged(TreeProperties* prop) { diff --git a/libs/hwui/VectorDrawable.h b/libs/hwui/VectorDrawable.h index b867dbc59714..8244a3911183 100644 --- a/libs/hwui/VectorDrawable.h +++ b/libs/hwui/VectorDrawable.h @@ -18,6 +18,7 @@ #define ANDROID_HWUI_VPATH_H #include "hwui/Canvas.h" +#include "hwui/Bitmap.h" #include "DisplayList.h" #include <SkBitmap.h> @@ -39,6 +40,13 @@ namespace android { namespace uirenderer { +// Debug +#if DEBUG_VECTOR_DRAWABLE + #define VECTOR_DRAWABLE_LOGD(...) ALOGD(__VA_ARGS__) +#else + #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_PROP(field, value) ((value) != (field) ? ((field) = (value), true) : false) @@ -554,7 +562,7 @@ public: const SkRect& bounds, bool needsMirroring, bool canReuseCache); void drawStaging(Canvas* canvas); - const SkBitmap& getBitmapUpdateIfDirty(); + Bitmap& getBitmapUpdateIfDirty(); void setAllowCaching(bool allowCaching) { mAllowCaching = allowCaching; } @@ -689,11 +697,15 @@ public: void setPropertyChangeWillBeConsumed(bool willBeConsumed) { mWillBeConsumed = willBeConsumed; } private: + struct Cache { + sk_sp<Bitmap> bitmap; + bool dirty = true; + }; SkPaint* updatePaint(SkPaint* outPaint, TreeProperties* prop); - bool allocateBitmapIfNeeded(SkBitmap* outCache, int width, int height); - bool canReuseBitmap(const SkBitmap&, int width, int height); - void updateBitmapCache(SkBitmap* outCache, bool useStagingData); + bool allocateBitmapIfNeeded(Cache& cache, int width, int height); + bool canReuseBitmap(Bitmap*, int width, int height); + void updateBitmapCache(Bitmap& outCache, bool useStagingData); // 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. @@ -706,10 +718,6 @@ private: TreeProperties mStagingProperties = TreeProperties(this); SkPaint mPaint; - struct Cache { - SkBitmap bitmap; - bool dirty = true; - }; Cache mStagingCache; Cache mCache; diff --git a/libs/hwui/Vertex.h b/libs/hwui/Vertex.h index c1bf980658b2..db982ad0c8f4 100644 --- a/libs/hwui/Vertex.h +++ b/libs/hwui/Vertex.h @@ -19,6 +19,7 @@ #include "Vector.h" +#include "FloatColor.h" #include "utils/Macros.h" namespace android { @@ -76,21 +77,19 @@ struct TextureVertex { REQUIRE_COMPATIBLE_LAYOUT(TextureVertex); /** - * Simple structure to describe a vertex with a position, texture UV and ARGB color. + * Simple structure to describe a vertex with a position, texture UV and an + * sRGB color with alpha. The color is stored pre-multiplied in linear space. */ struct ColorTextureVertex { float x, y; float u, v; - float r, g, b, a; + float r, g, b, a; // pre-multiplied linear static inline void set(ColorTextureVertex* vertex, float x, float y, - float u, float v, int color) { - - float a = ((color >> 24) & 0xff) / 255.0f; - float r = a * ((color >> 16) & 0xff) / 255.0f; - float g = a * ((color >> 8) & 0xff) / 255.0f; - float b = a * ((color) & 0xff) / 255.0f; - *vertex = { x, y, u, v, r, g, b, a }; + 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 }; } }; // struct ColorTextureVertex diff --git a/libs/hwui/VkLayer.cpp b/libs/hwui/VkLayer.cpp new file mode 100644 index 000000000000..537b3eaa3ad5 --- /dev/null +++ b/libs/hwui/VkLayer.cpp @@ -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. + */ + +#include "VkLayer.h" + +#include "renderstate/RenderState.h" + +#include <SkCanvas.h> +#include <SkSurface.h> + +namespace android { +namespace uirenderer { + +void VkLayer::updateTexture() { + sk_sp<SkSurface> surface; + SkImageInfo info = SkImageInfo::MakeS32(mWidth, mHeight, kPremul_SkAlphaType); + surface = SkSurface::MakeRenderTarget(mRenderState.getGrContext(), SkBudgeted::kNo, info); + surface->getCanvas()->clear(SK_ColorBLUE); + mImage = surface->makeImageSnapshot(SkBudgeted::kNo); +} + +void VkLayer::onVkContextDestroyed() { + mImage = nullptr; +} + +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/VkLayer.h b/libs/hwui/VkLayer.h new file mode 100644 index 000000000000..39522b3c0dda --- /dev/null +++ b/libs/hwui/VkLayer.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. + */ + +#pragma once + +#include "Layer.h" + +#include <SkImage.h> + +namespace android { +namespace uirenderer { +/** + * A layer has dimensions and is backed by a VkImage. + */ +class VkLayer : public Layer { +public: + VkLayer(RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight) + : Layer(renderState, Api::Vulkan) {} + + virtual ~VkLayer() {} + + uint32_t getWidth() const override { + return mWidth; + } + + 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; + } + + bool isBlend() const override { + return mBlend; + } + + sk_sp<SkImage> getImage() { + return mImage; + } + + void updateTexture(); + + // If we've destroyed the vulkan context (VkInstance, VkDevice, etc.), we must make sure to + // destroy any VkImages that were made with that context. + void onVkContextDestroyed(); + +private: + int mWidth; + int mHeight; + bool mBlend; + + sk_sp<SkImage> mImage; + +}; // struct VkLayer + +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/debug/DefaultGlesDriver.cpp b/libs/hwui/debug/DefaultGlesDriver.cpp new file mode 100644 index 000000000000..4515ec1f25a5 --- /dev/null +++ b/libs/hwui/debug/DefaultGlesDriver.cpp @@ -0,0 +1,40 @@ +/* + * 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 "DefaultGlesDriver.h" + +#include "gles_undefine.h" + +#include <EGL/egl.h> + +namespace android { +namespace uirenderer { +namespace debug { + +// Generate the proxy +#define API_ENTRY(x) DefaultGlesDriver::x##_ +#define CALL_GL_API(x, ...) x(__VA_ARGS__); +#define CALL_GL_API_RETURN(x, ...) return x(__VA_ARGS__); + +#include "gles_stubs.in" + +#undef API_ENTRY +#undef CALL_GL_API +#undef CALL_GL_API_RETURN + +} // namespace debug +} // namespace uirenderer +} // namespace android diff --git a/libs/hwui/debug/DefaultGlesDriver.h b/libs/hwui/debug/DefaultGlesDriver.h new file mode 100644 index 000000000000..3eab97077004 --- /dev/null +++ b/libs/hwui/debug/DefaultGlesDriver.h @@ -0,0 +1,35 @@ +/* + * 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. + */ + +#pragma once + +#include "GlesDriver.h" + +namespace android { +namespace uirenderer { +namespace debug { + +class DefaultGlesDriver : public GlesDriver { +public: +#define GL_ENTRY(ret, api, ...) virtual ret api##_(__VA_ARGS__) override; + #include "gles_decls.in" +#undef GL_ENTRY + +}; + +} // namespace debug +} // namespace uirenderer +} // namespace android diff --git a/libs/hwui/debug/FatalBaseDriver.cpp b/libs/hwui/debug/FatalBaseDriver.cpp new file mode 100644 index 000000000000..4c38fac33521 --- /dev/null +++ b/libs/hwui/debug/FatalBaseDriver.cpp @@ -0,0 +1,40 @@ +/* + * 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 "FatalBaseDriver.h" + +#include <log/log.h> + +namespace android { +namespace uirenderer { +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); + +#include "gles_stubs.in" + +#undef API_ENTRY +#undef CALL_GL_API +#undef CALL_GL_API_RETURN + +} // namespace debug +} // namespace uirenderer +} // namespace android diff --git a/libs/hwui/debug/FatalBaseDriver.h b/libs/hwui/debug/FatalBaseDriver.h new file mode 100644 index 000000000000..76c30e90bd39 --- /dev/null +++ b/libs/hwui/debug/FatalBaseDriver.h @@ -0,0 +1,37 @@ +/* + * 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. + */ + +#pragma once + +#include "GlesDriver.h" + +namespace android { +namespace uirenderer { +namespace debug { + +// A base driver that implements all the pure virtuals in the form of +// LOG_ALWAYS_FATALS. Suitable for selective-override implementations +// where only a known subset of methods need to be overridden +class FatalBaseDriver : public GlesDriver { +public: +#define GL_ENTRY(ret, api, ...) virtual ret api##_(__VA_ARGS__) override; + #include "gles_decls.in" +#undef GL_ENTRY +}; + +} // namespace debug +} // namespace uirenderer +} // namespace android diff --git a/libs/hwui/debug/GlesDriver.cpp b/libs/hwui/debug/GlesDriver.cpp new file mode 100644 index 000000000000..97e8f3a81b74 --- /dev/null +++ b/libs/hwui/debug/GlesDriver.cpp @@ -0,0 +1,46 @@ +/* + * 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 "GlesDriver.h" +#include "DefaultGlesDriver.h" +#include "GlesErrorCheckWrapper.h" + +namespace android { +namespace uirenderer { +namespace debug { + +static DefaultGlesDriver sDefaultDriver; + +static std::unique_ptr<GlesDriver> sGlesDriver(new GlesErrorCheckWrapper(sDefaultDriver)); + +GlesDriver* GlesDriver::get() { + return sGlesDriver.get(); +} + +std::unique_ptr<GlesDriver> GlesDriver::replace(std::unique_ptr<GlesDriver>&& driver) { + std::unique_ptr<GlesDriver> ret = std::move(sGlesDriver); + sGlesDriver = std::move(driver); + return ret; +} + +sk_sp<const GrGLInterface> GlesDriver::getSkiaInterface() { + sk_sp<const GrGLInterface> skiaInterface(GrGLCreateNativeInterface()); + return skiaInterface; +} + +} // namespace debug +} // namespace uirenderer +} // namespace android diff --git a/libs/hwui/debug/GlesDriver.h b/libs/hwui/debug/GlesDriver.h new file mode 100644 index 000000000000..3c36487f72cf --- /dev/null +++ b/libs/hwui/debug/GlesDriver.h @@ -0,0 +1,55 @@ +/* + * 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. + */ + +#pragma once + +#ifndef HWUI_GLES_WRAP_ENABLED +#error Wrapping wasn't enabled, can't use this! +#endif + +#include <GLES/gl.h> +#include <GLES/glext.h> +#include <GLES2/gl2.h> +#include <GLES2/gl2ext.h> +#include <GLES3/gl3.h> +#include <GLES3/gl31.h> +#include <GLES3/gl32.h> + +#include <gl/GrGLInterface.h> +#include <memory> + +namespace android { +namespace uirenderer { +namespace debug { + +// All the gl methods on GlesDriver have a trailing underscore +// This is to avoid collision with gles_redefine/gles_undefine +class GlesDriver { +public: + virtual ~GlesDriver() {} + virtual sk_sp<const GrGLInterface> getSkiaInterface(); + +#define GL_ENTRY(ret, api, ...) virtual ret api##_(__VA_ARGS__) = 0; + #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 diff --git a/libs/hwui/debug/GlesErrorCheckWrapper.cpp b/libs/hwui/debug/GlesErrorCheckWrapper.cpp new file mode 100644 index 000000000000..7ededaaa7fc2 --- /dev/null +++ b/libs/hwui/debug/GlesErrorCheckWrapper.cpp @@ -0,0 +1,74 @@ +/* + * 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 "GlesErrorCheckWrapper.h" + +#include <log/log.h> + +namespace android { +namespace uirenderer { +namespace debug { + +void GlesErrorCheckWrapper::assertNoErrors(const char* apicall) { + GLenum status = GL_NO_ERROR; + GLenum lastError = GL_NO_ERROR; + const char* lastErrorName = nullptr; + 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"; + } + } + 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_RETURN(x, ...) \ + auto ret = mBase.x##_(__VA_ARGS__); \ + assertNoErrors(#x); \ + return ret + +#include "gles_stubs.in" + +#undef API_ENTRY +#undef CALL_GL_API +#undef CALL_GL_API_RETURN + +} // namespace debug +} // namespace uirenderer +} // namespace android diff --git a/libs/hwui/debug/GlesErrorCheckWrapper.h b/libs/hwui/debug/GlesErrorCheckWrapper.h new file mode 100644 index 000000000000..fd45fc0184a1 --- /dev/null +++ b/libs/hwui/debug/GlesErrorCheckWrapper.h @@ -0,0 +1,41 @@ +/* + * 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. + */ + +#pragma once + +#include "GlesDriver.h" + +namespace android { +namespace uirenderer { +namespace debug { + +class GlesErrorCheckWrapper : public GlesDriver { +public: + GlesErrorCheckWrapper(GlesDriver& base) : mBase(base) {} + +#define GL_ENTRY(ret, api, ...) virtual ret api##_(__VA_ARGS__) override; + #include "gles_decls.in" +#undef GL_ENTRY + +private: + void assertNoErrors(const char* apicall); + + GlesDriver& mBase; +}; + +} // namespace debug +} // namespace uirenderer +} // namespace android diff --git a/libs/hwui/debug/MockGlesDriver.h b/libs/hwui/debug/MockGlesDriver.h new file mode 100644 index 000000000000..e0bfc5780b21 --- /dev/null +++ b/libs/hwui/debug/MockGlesDriver.h @@ -0,0 +1,36 @@ +/* + * 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. + */ + +#pragma once + +#include "FatalBaseDriver.h" + +#include <gmock/gmock.h> + +namespace android { +namespace uirenderer { +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)); +}; + +} // namespace debug +} // namespace uirenderer +} // namespace android diff --git a/libs/hwui/debug/NullGlesDriver.cpp b/libs/hwui/debug/NullGlesDriver.cpp new file mode 100644 index 000000000000..8fbe4bfe7033 --- /dev/null +++ b/libs/hwui/debug/NullGlesDriver.cpp @@ -0,0 +1,170 @@ +/* + * 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 <debug/NullGlesDriver.h> + +namespace android { +namespace uirenderer { +namespace debug { + +sk_sp<const GrGLInterface> NullGlesDriver::getSkiaInterface() { + sk_sp<const GrGLInterface> skiaInterface(GrGLCreateNullInterface()); + return skiaInterface; +} + +struct { + GLboolean scissorEnabled; +} gState; + +static void nullglGenCommon(GLsizei n, GLuint *buffers) { + static GLuint nextId = 0; + int i; + for(i = 0; i < n; i++) { + buffers[i] = ++nextId; + } +} + +void NullGlesDriver::glGenBuffers_(GLsizei n, GLuint *buffers) { + nullglGenCommon(n, buffers); +} + +void NullGlesDriver::glGenFramebuffers_(GLsizei n, GLuint *framebuffers) { + nullglGenCommon(n, framebuffers); +} + +void NullGlesDriver::glGenRenderbuffers_(GLsizei n, GLuint *renderbuffers) { + nullglGenCommon(n, renderbuffers); +} + +void NullGlesDriver::glGenTextures_(GLsizei n, GLuint *textures) { + nullglGenCommon(n, textures); +} + +GLuint NullGlesDriver::glCreateProgram_(void) { + static GLuint nextProgram = 0; + return ++nextProgram; +} + +GLuint NullGlesDriver::glCreateShader_(GLenum type) { + static GLuint nextShader = 0; + return ++nextShader; +} + +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; + } +} + +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) { + switch (pname) { + case GL_COMPILE_STATUS: + case GL_DELETE_STATUS: + *params = GL_TRUE; + } +} + +void NullGlesDriver::glGetShaderInfoLog_(GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog) { + *length = snprintf(infoLog, bufSize, "success"); + if (*length >= bufSize) { + *length = bufSize - 1; + } +} + +void setBooleanState(GLenum cap, GLboolean value) { + switch (cap) { + case GL_SCISSOR_TEST: + gState.scissorEnabled = value; + break; + } +} + +void NullGlesDriver::glEnable_(GLenum cap) { + setBooleanState(cap, GL_TRUE); +} + +void NullGlesDriver::glDisable_(GLenum cap) { + setBooleanState(cap, GL_FALSE); +} + +GLboolean NullGlesDriver::glIsEnabled_(GLenum cap) { + switch (cap) { + case GL_SCISSOR_TEST: + return gState.scissorEnabled; + default: + return GL_FALSE; + } +} + +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; + } +} + +GLenum NullGlesDriver::glCheckFramebufferStatus_(GLenum target) { + switch (target) { + 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 ""; + } +} + +const GLubyte* NullGlesDriver::glGetString_(GLenum name) { + return (GLubyte*) getString(name); +} + +} // namespace debug +} // namespace uirenderer +} // namespace android diff --git a/libs/hwui/debug/NullGlesDriver.h b/libs/hwui/debug/NullGlesDriver.h new file mode 100644 index 000000000000..37ca8f34f87b --- /dev/null +++ b/libs/hwui/debug/NullGlesDriver.h @@ -0,0 +1,169 @@ +/* + * 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. + */ + +#pragma once + +#include "FatalBaseDriver.h" + +namespace android { +namespace uirenderer { +namespace debug { + +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 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 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 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 glBindBuffer_(GLenum target, GLuint buffer) override {} + virtual void glBindFramebuffer_(GLenum target, GLuint framebuffer) override {} + virtual void glBindRenderbuffer_(GLenum target, GLuint renderbuffer) override {} + virtual void glBindTexture_(GLenum target, GLuint texture) override {} + virtual void glBlendColor_(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha) override {} + 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 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 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 glCullFace_(GLenum mode) 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 glDeleteShader_(GLuint shader) 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 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 glFrontFace_(GLenum mode) override {} + virtual void glGenerateMipmap_(GLenum target) override {} + 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 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 glReleaseShaderCompiler_(void) 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 glStencilFunc_(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 glTexParameterf_(GLenum target, GLenum pname, GLfloat param) 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 glUniform1f_(GLint location, GLfloat v0) 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 glUniform2f_(GLint location, GLfloat v0, GLfloat v1) 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 glUniform3f_(GLint location, GLfloat v0, GLfloat v1, GLfloat v2) 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 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 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 glVertexAttrib2f_(GLuint index, GLfloat x, GLfloat y) 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 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 glPopGroupMarkerEXT_(void) 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 { + return 0; + } + + virtual GLboolean glUnmapBuffer_(GLenum target) override { + return GL_FALSE; + } +}; + +} // namespace debug +} // namespace uirenderer +} // namespace android diff --git a/libs/hwui/debug/ScopedReplaceDriver.h b/libs/hwui/debug/ScopedReplaceDriver.h new file mode 100644 index 000000000000..342c9d223727 --- /dev/null +++ b/libs/hwui/debug/ScopedReplaceDriver.h @@ -0,0 +1,46 @@ +/* + * 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. + */ + +#pragma once + +#include "GlesDriver.h" + +namespace android { +namespace uirenderer { +namespace debug { + +template <typename Driver> +class ScopedReplaceDriver { +public: + ScopedReplaceDriver() { + std::unique_ptr<Driver> glDriver = std::make_unique<Driver>(); + mCurrentDriver = glDriver.get(); + mOldDriver = GlesDriver::replace(std::move(glDriver)); + } + + Driver& get() { return *mCurrentDriver; } + + ~ScopedReplaceDriver() { + GlesDriver::replace(std::move(mOldDriver)); + } +private: + std::unique_ptr<GlesDriver> mOldDriver; + Driver* mCurrentDriver; +}; + +} // namespace debug +} // namespace uirenderer +} // namespace android diff --git a/libs/hwui/debug/gles_decls.in b/libs/hwui/debug/gles_decls.in new file mode 100644 index 000000000000..16574a7fb074 --- /dev/null +++ b/libs/hwui/debug/gles_decls.in @@ -0,0 +1,544 @@ +GL_ENTRY(void, glActiveTexture, GLenum texture) +GL_ENTRY(void, glAttachShader, GLuint program, GLuint shader) +GL_ENTRY(void, glBindAttribLocation, GLuint program, GLuint index, const GLchar *name) +GL_ENTRY(void, glBindBuffer, GLenum target, GLuint buffer) +GL_ENTRY(void, glBindFramebuffer, GLenum target, GLuint framebuffer) +GL_ENTRY(void, glBindRenderbuffer, GLenum target, GLuint renderbuffer) +GL_ENTRY(void, glBindTexture, GLenum target, GLuint texture) +GL_ENTRY(void, glBlendColor, GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha) +GL_ENTRY(void, glBlendEquation, GLenum mode) +GL_ENTRY(void, glBlendEquationSeparate, GLenum modeRGB, GLenum modeAlpha) +GL_ENTRY(void, glBlendFunc, GLenum sfactor, GLenum dfactor) +GL_ENTRY(void, glBlendFuncSeparate, GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha) +GL_ENTRY(void, glBufferData, GLenum target, GLsizeiptr size, const void *data, GLenum usage) +GL_ENTRY(void, glBufferSubData, GLenum target, GLintptr offset, GLsizeiptr size, const void *data) +GL_ENTRY(GLenum, glCheckFramebufferStatus, GLenum target) +GL_ENTRY(void, glClear, GLbitfield mask) +GL_ENTRY(void, glClearColor, GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha) +GL_ENTRY(void, glClearDepthf, GLfloat d) +GL_ENTRY(void, glClearStencil, GLint s) +GL_ENTRY(void, glColorMask, GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha) +GL_ENTRY(void, glCompileShader, GLuint shader) +GL_ENTRY(void, glCompressedTexImage2D, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *data) +GL_ENTRY(void, glCompressedTexSubImage2D, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data) +GL_ENTRY(void, glCopyTexImage2D, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border) +GL_ENTRY(void, glCopyTexSubImage2D, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height) +GL_ENTRY(GLuint, glCreateProgram, void) +GL_ENTRY(GLuint, glCreateShader, GLenum type) +GL_ENTRY(void, glCullFace, GLenum mode) +GL_ENTRY(void, glDeleteBuffers, GLsizei n, const GLuint *buffers) +GL_ENTRY(void, glDeleteFramebuffers, GLsizei n, const GLuint *framebuffers) +GL_ENTRY(void, glDeleteProgram, GLuint program) +GL_ENTRY(void, glDeleteRenderbuffers, GLsizei n, const GLuint *renderbuffers) +GL_ENTRY(void, glDeleteShader, GLuint shader) +GL_ENTRY(void, glDeleteTextures, GLsizei n, const GLuint *textures) +GL_ENTRY(void, glDepthFunc, GLenum func) +GL_ENTRY(void, glDepthMask, GLboolean flag) +GL_ENTRY(void, glDepthRangef, GLfloat n, GLfloat f) +GL_ENTRY(void, glDetachShader, GLuint program, GLuint shader) +GL_ENTRY(void, glDisable, GLenum cap) +GL_ENTRY(void, glDisableVertexAttribArray, GLuint index) +GL_ENTRY(void, glDrawArrays, GLenum mode, GLint first, GLsizei count) +GL_ENTRY(void, glDrawElements, GLenum mode, GLsizei count, GLenum type, const void *indices) +GL_ENTRY(void, glEnable, GLenum cap) +GL_ENTRY(void, glEnableVertexAttribArray, GLuint index) +GL_ENTRY(void, glFinish, void) +GL_ENTRY(void, glFlush, void) +GL_ENTRY(void, glFramebufferRenderbuffer, GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer) +GL_ENTRY(void, glFramebufferTexture2D, GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level) +GL_ENTRY(void, glFrontFace, GLenum mode) +GL_ENTRY(void, glGenBuffers, GLsizei n, GLuint *buffers) +GL_ENTRY(void, glGenerateMipmap, GLenum target) +GL_ENTRY(void, glGenFramebuffers, GLsizei n, GLuint *framebuffers) +GL_ENTRY(void, glGenRenderbuffers, GLsizei n, GLuint *renderbuffers) +GL_ENTRY(void, glGenTextures, GLsizei n, GLuint *textures) +GL_ENTRY(void, glGetActiveAttrib, GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name) +GL_ENTRY(void, glGetActiveUniform, GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name) +GL_ENTRY(void, glGetAttachedShaders, GLuint program, GLsizei maxCount, GLsizei *count, GLuint *shaders) +GL_ENTRY(GLint, glGetAttribLocation, GLuint program, const GLchar *name) +GL_ENTRY(void, glGetBooleanv, GLenum pname, GLboolean *data) +GL_ENTRY(void, glGetBufferParameteriv, GLenum target, GLenum pname, GLint *params) +GL_ENTRY(GLenum, glGetError, void) +GL_ENTRY(void, glGetFloatv, GLenum pname, GLfloat *data) +GL_ENTRY(void, glGetFramebufferAttachmentParameteriv, GLenum target, GLenum attachment, GLenum pname, GLint *params) +GL_ENTRY(void, glGetIntegerv, GLenum pname, GLint *data) +GL_ENTRY(void, glGetProgramiv, GLuint program, GLenum pname, GLint *params) +GL_ENTRY(void, glGetProgramInfoLog, GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog) +GL_ENTRY(void, glGetRenderbufferParameteriv, GLenum target, GLenum pname, GLint *params) +GL_ENTRY(void, glGetShaderiv, GLuint shader, GLenum pname, GLint *params) +GL_ENTRY(void, glGetShaderInfoLog, GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog) +GL_ENTRY(void, glGetShaderPrecisionFormat, GLenum shadertype, GLenum precisiontype, GLint *range, GLint *precision) +GL_ENTRY(void, glGetShaderSource, GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *source) +GL_ENTRY(const GLubyte *, glGetString, GLenum name) +GL_ENTRY(void, glGetTexParameterfv, GLenum target, GLenum pname, GLfloat *params) +GL_ENTRY(void, glGetTexParameteriv, GLenum target, GLenum pname, GLint *params) +GL_ENTRY(void, glGetUniformfv, GLuint program, GLint location, GLfloat *params) +GL_ENTRY(void, glGetUniformiv, GLuint program, GLint location, GLint *params) +GL_ENTRY(GLint, glGetUniformLocation, GLuint program, const GLchar *name) +GL_ENTRY(void, glGetVertexAttribfv, GLuint index, GLenum pname, GLfloat *params) +GL_ENTRY(void, glGetVertexAttribiv, GLuint index, GLenum pname, GLint *params) +GL_ENTRY(void, glGetVertexAttribPointerv, GLuint index, GLenum pname, void **pointer) +GL_ENTRY(void, glHint, GLenum target, GLenum mode) +GL_ENTRY(GLboolean, glIsBuffer, GLuint buffer) +GL_ENTRY(GLboolean, glIsEnabled, GLenum cap) +GL_ENTRY(GLboolean, glIsFramebuffer, GLuint framebuffer) +GL_ENTRY(GLboolean, glIsProgram, GLuint program) +GL_ENTRY(GLboolean, glIsRenderbuffer, GLuint renderbuffer) +GL_ENTRY(GLboolean, glIsShader, GLuint shader) +GL_ENTRY(GLboolean, glIsTexture, GLuint texture) +GL_ENTRY(void, glLineWidth, GLfloat width) +GL_ENTRY(void, glLinkProgram, GLuint program) +GL_ENTRY(void, glPixelStorei, GLenum pname, GLint param) +GL_ENTRY(void, glPolygonOffset, GLfloat factor, GLfloat units) +GL_ENTRY(void, glReadPixels, GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void *pixels) +GL_ENTRY(void, glReleaseShaderCompiler, void) +GL_ENTRY(void, glRenderbufferStorage, GLenum target, GLenum internalformat, GLsizei width, GLsizei height) +GL_ENTRY(void, glSampleCoverage, GLfloat value, GLboolean invert) +GL_ENTRY(void, glScissor, GLint x, GLint y, GLsizei width, GLsizei height) +GL_ENTRY(void, glShaderBinary, GLsizei count, const GLuint *shaders, GLenum binaryformat, const void *binary, GLsizei length) +GL_ENTRY(void, glShaderSource, GLuint shader, GLsizei count, const GLchar *const*string, const GLint *length) +GL_ENTRY(void, glStencilFunc, GLenum func, GLint ref, GLuint mask) +GL_ENTRY(void, glStencilFuncSeparate, GLenum face, GLenum func, GLint ref, GLuint mask) +GL_ENTRY(void, glStencilMask, GLuint mask) +GL_ENTRY(void, glStencilMaskSeparate, GLenum face, GLuint mask) +GL_ENTRY(void, glStencilOp, GLenum fail, GLenum zfail, GLenum zpass) +GL_ENTRY(void, glStencilOpSeparate, GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass) +GL_ENTRY(void, glTexImage2D, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels) +GL_ENTRY(void, glTexParameterf, GLenum target, GLenum pname, GLfloat param) +GL_ENTRY(void, glTexParameterfv, GLenum target, GLenum pname, const GLfloat *params) +GL_ENTRY(void, glTexParameteri, GLenum target, GLenum pname, GLint param) +GL_ENTRY(void, glTexParameteriv, GLenum target, GLenum pname, const GLint *params) +GL_ENTRY(void, glTexSubImage2D, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels) +GL_ENTRY(void, glUniform1f, GLint location, GLfloat v0) +GL_ENTRY(void, glUniform1fv, GLint location, GLsizei count, const GLfloat *value) +GL_ENTRY(void, glUniform1i, GLint location, GLint v0) +GL_ENTRY(void, glUniform1iv, GLint location, GLsizei count, const GLint *value) +GL_ENTRY(void, glUniform2f, GLint location, GLfloat v0, GLfloat v1) +GL_ENTRY(void, glUniform2fv, GLint location, GLsizei count, const GLfloat *value) +GL_ENTRY(void, glUniform2i, GLint location, GLint v0, GLint v1) +GL_ENTRY(void, glUniform2iv, GLint location, GLsizei count, const GLint *value) +GL_ENTRY(void, glUniform3f, GLint location, GLfloat v0, GLfloat v1, GLfloat v2) +GL_ENTRY(void, glUniform3fv, GLint location, GLsizei count, const GLfloat *value) +GL_ENTRY(void, glUniform3i, GLint location, GLint v0, GLint v1, GLint v2) +GL_ENTRY(void, glUniform3iv, GLint location, GLsizei count, const GLint *value) +GL_ENTRY(void, glUniform4f, GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3) +GL_ENTRY(void, glUniform4fv, GLint location, GLsizei count, const GLfloat *value) +GL_ENTRY(void, glUniform4i, GLint location, GLint v0, GLint v1, GLint v2, GLint v3) +GL_ENTRY(void, glUniform4iv, GLint location, GLsizei count, const GLint *value) +GL_ENTRY(void, glUniformMatrix2fv, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) +GL_ENTRY(void, glUniformMatrix3fv, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) +GL_ENTRY(void, glUniformMatrix4fv, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) +GL_ENTRY(void, glUseProgram, GLuint program) +GL_ENTRY(void, glValidateProgram, GLuint program) +GL_ENTRY(void, glVertexAttrib1f, GLuint index, GLfloat x) +GL_ENTRY(void, glVertexAttrib1fv, GLuint index, const GLfloat *v) +GL_ENTRY(void, glVertexAttrib2f, GLuint index, GLfloat x, GLfloat y) +GL_ENTRY(void, glVertexAttrib2fv, GLuint index, const GLfloat *v) +GL_ENTRY(void, glVertexAttrib3f, GLuint index, GLfloat x, GLfloat y, GLfloat z) +GL_ENTRY(void, glVertexAttrib3fv, GLuint index, const GLfloat *v) +GL_ENTRY(void, glVertexAttrib4f, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w) +GL_ENTRY(void, glVertexAttrib4fv, GLuint index, const GLfloat *v) +GL_ENTRY(void, glVertexAttribPointer, GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer) +GL_ENTRY(void, glViewport, GLint x, GLint y, GLsizei width, GLsizei height) +GL_ENTRY(void, glReadBuffer, GLenum src) +GL_ENTRY(void, glDrawRangeElements, GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices) +GL_ENTRY(void, glTexImage3D, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels) +GL_ENTRY(void, glTexSubImage3D, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels) +GL_ENTRY(void, glCopyTexSubImage3D, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height) +GL_ENTRY(void, glCompressedTexImage3D, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *data) +GL_ENTRY(void, glCompressedTexSubImage3D, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *data) +GL_ENTRY(void, glGenQueries, GLsizei n, GLuint *ids) +GL_ENTRY(void, glDeleteQueries, GLsizei n, const GLuint *ids) +GL_ENTRY(GLboolean, glIsQuery, GLuint id) +GL_ENTRY(void, glBeginQuery, GLenum target, GLuint id) +GL_ENTRY(void, glEndQuery, GLenum target) +GL_ENTRY(void, glGetQueryiv, GLenum target, GLenum pname, GLint *params) +GL_ENTRY(void, glGetQueryObjectuiv, GLuint id, GLenum pname, GLuint *params) +GL_ENTRY(GLboolean, glUnmapBuffer, GLenum target) +GL_ENTRY(void, glGetBufferPointerv, GLenum target, GLenum pname, void **params) +GL_ENTRY(void, glDrawBuffers, GLsizei n, const GLenum *bufs) +GL_ENTRY(void, glUniformMatrix2x3fv, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) +GL_ENTRY(void, glUniformMatrix3x2fv, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) +GL_ENTRY(void, glUniformMatrix2x4fv, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) +GL_ENTRY(void, glUniformMatrix4x2fv, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) +GL_ENTRY(void, glUniformMatrix3x4fv, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) +GL_ENTRY(void, glUniformMatrix4x3fv, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) +GL_ENTRY(void, glBlitFramebuffer, GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter) +GL_ENTRY(void, glRenderbufferStorageMultisample, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height) +GL_ENTRY(void, glFramebufferTextureLayer, GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer) +GL_ENTRY(void *, glMapBufferRange, GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access) +GL_ENTRY(void, glFlushMappedBufferRange, GLenum target, GLintptr offset, GLsizeiptr length) +GL_ENTRY(void, glBindVertexArray, GLuint array) +GL_ENTRY(void, glDeleteVertexArrays, GLsizei n, const GLuint *arrays) +GL_ENTRY(void, glGenVertexArrays, GLsizei n, GLuint *arrays) +GL_ENTRY(GLboolean, glIsVertexArray, GLuint array) +GL_ENTRY(void, glGetIntegeri_v, GLenum target, GLuint index, GLint *data) +GL_ENTRY(void, glBeginTransformFeedback, GLenum primitiveMode) +GL_ENTRY(void, glEndTransformFeedback, void) +GL_ENTRY(void, glBindBufferRange, GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size) +GL_ENTRY(void, glBindBufferBase, GLenum target, GLuint index, GLuint buffer) +GL_ENTRY(void, glTransformFeedbackVaryings, GLuint program, GLsizei count, const GLchar *const*varyings, GLenum bufferMode) +GL_ENTRY(void, glGetTransformFeedbackVarying, GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLsizei *size, GLenum *type, GLchar *name) +GL_ENTRY(void, glVertexAttribIPointer, GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer) +GL_ENTRY(void, glGetVertexAttribIiv, GLuint index, GLenum pname, GLint *params) +GL_ENTRY(void, glGetVertexAttribIuiv, GLuint index, GLenum pname, GLuint *params) +GL_ENTRY(void, glVertexAttribI4i, GLuint index, GLint x, GLint y, GLint z, GLint w) +GL_ENTRY(void, glVertexAttribI4ui, GLuint index, GLuint x, GLuint y, GLuint z, GLuint w) +GL_ENTRY(void, glVertexAttribI4iv, GLuint index, const GLint *v) +GL_ENTRY(void, glVertexAttribI4uiv, GLuint index, const GLuint *v) +GL_ENTRY(void, glGetUniformuiv, GLuint program, GLint location, GLuint *params) +GL_ENTRY(GLint, glGetFragDataLocation, GLuint program, const GLchar *name) +GL_ENTRY(void, glUniform1ui, GLint location, GLuint v0) +GL_ENTRY(void, glUniform2ui, GLint location, GLuint v0, GLuint v1) +GL_ENTRY(void, glUniform3ui, GLint location, GLuint v0, GLuint v1, GLuint v2) +GL_ENTRY(void, glUniform4ui, GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3) +GL_ENTRY(void, glUniform1uiv, GLint location, GLsizei count, const GLuint *value) +GL_ENTRY(void, glUniform2uiv, GLint location, GLsizei count, const GLuint *value) +GL_ENTRY(void, glUniform3uiv, GLint location, GLsizei count, const GLuint *value) +GL_ENTRY(void, glUniform4uiv, GLint location, GLsizei count, const GLuint *value) +GL_ENTRY(void, glClearBufferiv, GLenum buffer, GLint drawbuffer, const GLint *value) +GL_ENTRY(void, glClearBufferuiv, GLenum buffer, GLint drawbuffer, const GLuint *value) +GL_ENTRY(void, glClearBufferfv, GLenum buffer, GLint drawbuffer, const GLfloat *value) +GL_ENTRY(void, glClearBufferfi, GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil) +GL_ENTRY(const GLubyte *, glGetStringi, GLenum name, GLuint index) +GL_ENTRY(void, glCopyBufferSubData, GLenum readTarget, GLenum writeTarget, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size) +GL_ENTRY(void, glGetUniformIndices, GLuint program, GLsizei uniformCount, const GLchar *const*uniformNames, GLuint *uniformIndices) +GL_ENTRY(void, glGetActiveUniformsiv, GLuint program, GLsizei uniformCount, const GLuint *uniformIndices, GLenum pname, GLint *params) +GL_ENTRY(GLuint, glGetUniformBlockIndex, GLuint program, const GLchar *uniformBlockName) +GL_ENTRY(void, glGetActiveUniformBlockiv, GLuint program, GLuint uniformBlockIndex, GLenum pname, GLint *params) +GL_ENTRY(void, glGetActiveUniformBlockName, GLuint program, GLuint uniformBlockIndex, GLsizei bufSize, GLsizei *length, GLchar *uniformBlockName) +GL_ENTRY(void, glUniformBlockBinding, GLuint program, GLuint uniformBlockIndex, GLuint uniformBlockBinding) +GL_ENTRY(void, glDrawArraysInstanced, GLenum mode, GLint first, GLsizei count, GLsizei instancecount) +GL_ENTRY(void, glDrawElementsInstanced, GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount) +GL_ENTRY(GLsync, glFenceSync, GLenum condition, GLbitfield flags) +GL_ENTRY(GLboolean, glIsSync, GLsync sync) +GL_ENTRY(void, glDeleteSync, GLsync sync) +GL_ENTRY(GLenum, glClientWaitSync, GLsync sync, GLbitfield flags, GLuint64 timeout) +GL_ENTRY(void, glWaitSync, GLsync sync, GLbitfield flags, GLuint64 timeout) +GL_ENTRY(void, glGetInteger64v, GLenum pname, GLint64 *data) +GL_ENTRY(void, glGetSynciv, GLsync sync, GLenum pname, GLsizei bufSize, GLsizei *length, GLint *values) +GL_ENTRY(void, glGetInteger64i_v, GLenum target, GLuint index, GLint64 *data) +GL_ENTRY(void, glGetBufferParameteri64v, GLenum target, GLenum pname, GLint64 *params) +GL_ENTRY(void, glGenSamplers, GLsizei count, GLuint *samplers) +GL_ENTRY(void, glDeleteSamplers, GLsizei count, const GLuint *samplers) +GL_ENTRY(GLboolean, glIsSampler, GLuint sampler) +GL_ENTRY(void, glBindSampler, GLuint unit, GLuint sampler) +GL_ENTRY(void, glSamplerParameteri, GLuint sampler, GLenum pname, GLint param) +GL_ENTRY(void, glSamplerParameteriv, GLuint sampler, GLenum pname, const GLint *param) +GL_ENTRY(void, glSamplerParameterf, GLuint sampler, GLenum pname, GLfloat param) +GL_ENTRY(void, glSamplerParameterfv, GLuint sampler, GLenum pname, const GLfloat *param) +GL_ENTRY(void, glGetSamplerParameteriv, GLuint sampler, GLenum pname, GLint *params) +GL_ENTRY(void, glGetSamplerParameterfv, GLuint sampler, GLenum pname, GLfloat *params) +GL_ENTRY(void, glVertexAttribDivisor, GLuint index, GLuint divisor) +GL_ENTRY(void, glBindTransformFeedback, GLenum target, GLuint id) +GL_ENTRY(void, glDeleteTransformFeedbacks, GLsizei n, const GLuint *ids) +GL_ENTRY(void, glGenTransformFeedbacks, GLsizei n, GLuint *ids) +GL_ENTRY(GLboolean, glIsTransformFeedback, GLuint id) +GL_ENTRY(void, glPauseTransformFeedback, void) +GL_ENTRY(void, glResumeTransformFeedback, void) +GL_ENTRY(void, glGetProgramBinary, GLuint program, GLsizei bufSize, GLsizei *length, GLenum *binaryFormat, void *binary) +GL_ENTRY(void, glProgramBinary, GLuint program, GLenum binaryFormat, const void *binary, GLsizei length) +GL_ENTRY(void, glProgramParameteri, GLuint program, GLenum pname, GLint value) +GL_ENTRY(void, glInvalidateFramebuffer, GLenum target, GLsizei numAttachments, const GLenum *attachments) +GL_ENTRY(void, glInvalidateSubFramebuffer, GLenum target, GLsizei numAttachments, const GLenum *attachments, GLint x, GLint y, GLsizei width, GLsizei height) +GL_ENTRY(void, glTexStorage2D, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height) +GL_ENTRY(void, glTexStorage3D, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth) +GL_ENTRY(void, glGetInternalformativ, GLenum target, GLenum internalformat, GLenum pname, GLsizei bufSize, GLint *params) +GL_ENTRY(void, glDispatchCompute, GLuint num_groups_x, GLuint num_groups_y, GLuint num_groups_z) +GL_ENTRY(void, glDispatchComputeIndirect, GLintptr indirect) +GL_ENTRY(void, glDrawArraysIndirect, GLenum mode, const void *indirect) +GL_ENTRY(void, glDrawElementsIndirect, GLenum mode, GLenum type, const void *indirect) +GL_ENTRY(void, glFramebufferParameteri, GLenum target, GLenum pname, GLint param) +GL_ENTRY(void, glGetFramebufferParameteriv, GLenum target, GLenum pname, GLint *params) +GL_ENTRY(void, glGetProgramInterfaceiv, GLuint program, GLenum programInterface, GLenum pname, GLint *params) +GL_ENTRY(GLuint, glGetProgramResourceIndex, GLuint program, GLenum programInterface, const GLchar *name) +GL_ENTRY(void, glGetProgramResourceName, GLuint program, GLenum programInterface, GLuint index, GLsizei bufSize, GLsizei *length, GLchar *name) +GL_ENTRY(void, glGetProgramResourceiv, GLuint program, GLenum programInterface, GLuint index, GLsizei propCount, const GLenum *props, GLsizei bufSize, GLsizei *length, GLint *params) +GL_ENTRY(GLint, glGetProgramResourceLocation, GLuint program, GLenum programInterface, const GLchar *name) +GL_ENTRY(void, glUseProgramStages, GLuint pipeline, GLbitfield stages, GLuint program) +GL_ENTRY(void, glActiveShaderProgram, GLuint pipeline, GLuint program) +GL_ENTRY(GLuint, glCreateShaderProgramv, GLenum type, GLsizei count, const GLchar *const*strings) +GL_ENTRY(void, glBindProgramPipeline, GLuint pipeline) +GL_ENTRY(void, glDeleteProgramPipelines, GLsizei n, const GLuint *pipelines) +GL_ENTRY(void, glGenProgramPipelines, GLsizei n, GLuint *pipelines) +GL_ENTRY(GLboolean, glIsProgramPipeline, GLuint pipeline) +GL_ENTRY(void, glGetProgramPipelineiv, GLuint pipeline, GLenum pname, GLint *params) +GL_ENTRY(void, glProgramUniform1i, GLuint program, GLint location, GLint v0) +GL_ENTRY(void, glProgramUniform2i, GLuint program, GLint location, GLint v0, GLint v1) +GL_ENTRY(void, glProgramUniform3i, GLuint program, GLint location, GLint v0, GLint v1, GLint v2) +GL_ENTRY(void, glProgramUniform4i, GLuint program, GLint location, GLint v0, GLint v1, GLint v2, GLint v3) +GL_ENTRY(void, glProgramUniform1ui, GLuint program, GLint location, GLuint v0) +GL_ENTRY(void, glProgramUniform2ui, GLuint program, GLint location, GLuint v0, GLuint v1) +GL_ENTRY(void, glProgramUniform3ui, GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2) +GL_ENTRY(void, glProgramUniform4ui, GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3) +GL_ENTRY(void, glProgramUniform1f, GLuint program, GLint location, GLfloat v0) +GL_ENTRY(void, glProgramUniform2f, GLuint program, GLint location, GLfloat v0, GLfloat v1) +GL_ENTRY(void, glProgramUniform3f, GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2) +GL_ENTRY(void, glProgramUniform4f, GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3) +GL_ENTRY(void, glProgramUniform1iv, GLuint program, GLint location, GLsizei count, const GLint *value) +GL_ENTRY(void, glProgramUniform2iv, GLuint program, GLint location, GLsizei count, const GLint *value) +GL_ENTRY(void, glProgramUniform3iv, GLuint program, GLint location, GLsizei count, const GLint *value) +GL_ENTRY(void, glProgramUniform4iv, GLuint program, GLint location, GLsizei count, const GLint *value) +GL_ENTRY(void, glProgramUniform1uiv, GLuint program, GLint location, GLsizei count, const GLuint *value) +GL_ENTRY(void, glProgramUniform2uiv, GLuint program, GLint location, GLsizei count, const GLuint *value) +GL_ENTRY(void, glProgramUniform3uiv, GLuint program, GLint location, GLsizei count, const GLuint *value) +GL_ENTRY(void, glProgramUniform4uiv, GLuint program, GLint location, GLsizei count, const GLuint *value) +GL_ENTRY(void, glProgramUniform1fv, GLuint program, GLint location, GLsizei count, const GLfloat *value) +GL_ENTRY(void, glProgramUniform2fv, GLuint program, GLint location, GLsizei count, const GLfloat *value) +GL_ENTRY(void, glProgramUniform3fv, GLuint program, GLint location, GLsizei count, const GLfloat *value) +GL_ENTRY(void, glProgramUniform4fv, GLuint program, GLint location, GLsizei count, const GLfloat *value) +GL_ENTRY(void, glProgramUniformMatrix2fv, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) +GL_ENTRY(void, glProgramUniformMatrix3fv, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) +GL_ENTRY(void, glProgramUniformMatrix4fv, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) +GL_ENTRY(void, glProgramUniformMatrix2x3fv, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) +GL_ENTRY(void, glProgramUniformMatrix3x2fv, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) +GL_ENTRY(void, glProgramUniformMatrix2x4fv, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) +GL_ENTRY(void, glProgramUniformMatrix4x2fv, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) +GL_ENTRY(void, glProgramUniformMatrix3x4fv, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) +GL_ENTRY(void, glProgramUniformMatrix4x3fv, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) +GL_ENTRY(void, glValidateProgramPipeline, GLuint pipeline) +GL_ENTRY(void, glGetProgramPipelineInfoLog, GLuint pipeline, GLsizei bufSize, GLsizei *length, GLchar *infoLog) +GL_ENTRY(void, glBindImageTexture, GLuint unit, GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum access, GLenum format) +GL_ENTRY(void, glGetBooleani_v, GLenum target, GLuint index, GLboolean *data) +GL_ENTRY(void, glMemoryBarrier, GLbitfield barriers) +GL_ENTRY(void, glMemoryBarrierByRegion, GLbitfield barriers) +GL_ENTRY(void, glTexStorage2DMultisample, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations) +GL_ENTRY(void, glGetMultisamplefv, GLenum pname, GLuint index, GLfloat *val) +GL_ENTRY(void, glSampleMaski, GLuint maskNumber, GLbitfield mask) +GL_ENTRY(void, glGetTexLevelParameteriv, GLenum target, GLint level, GLenum pname, GLint *params) +GL_ENTRY(void, glGetTexLevelParameterfv, GLenum target, GLint level, GLenum pname, GLfloat *params) +GL_ENTRY(void, glBindVertexBuffer, GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride) +GL_ENTRY(void, glVertexAttribFormat, GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset) +GL_ENTRY(void, glVertexAttribIFormat, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset) +GL_ENTRY(void, glVertexAttribBinding, GLuint attribindex, GLuint bindingindex) +GL_ENTRY(void, glVertexBindingDivisor, GLuint bindingindex, GLuint divisor) +GL_ENTRY(void, glBlendBarrier, void) +GL_ENTRY(void, glCopyImageSubData, GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth) +GL_ENTRY(void, glDebugMessageControl, GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled) +GL_ENTRY(void, glDebugMessageInsert, GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *buf) +GL_ENTRY(void, glDebugMessageCallback, GLDEBUGPROC callback, const void *userParam) +GL_ENTRY(GLuint, glGetDebugMessageLog, GLuint count, GLsizei bufSize, GLenum *sources, GLenum *types, GLuint *ids, GLenum *severities, GLsizei *lengths, GLchar *messageLog) +GL_ENTRY(void, glPushDebugGroup, GLenum source, GLuint id, GLsizei length, const GLchar *message) +GL_ENTRY(void, glPopDebugGroup, void) +GL_ENTRY(void, glObjectLabel, GLenum identifier, GLuint name, GLsizei length, const GLchar *label) +GL_ENTRY(void, glGetObjectLabel, GLenum identifier, GLuint name, GLsizei bufSize, GLsizei *length, GLchar *label) +GL_ENTRY(void, glObjectPtrLabel, const void *ptr, GLsizei length, const GLchar *label) +GL_ENTRY(void, glGetObjectPtrLabel, const void *ptr, GLsizei bufSize, GLsizei *length, GLchar *label) +GL_ENTRY(void, glGetPointerv, GLenum pname, void **params) +GL_ENTRY(void, glEnablei, GLenum target, GLuint index) +GL_ENTRY(void, glDisablei, GLenum target, GLuint index) +GL_ENTRY(void, glBlendEquationi, GLuint buf, GLenum mode) +GL_ENTRY(void, glBlendEquationSeparatei, GLuint buf, GLenum modeRGB, GLenum modeAlpha) +GL_ENTRY(void, glBlendFunci, GLuint buf, GLenum src, GLenum dst) +GL_ENTRY(void, glBlendFuncSeparatei, GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha) +GL_ENTRY(void, glColorMaski, GLuint index, GLboolean r, GLboolean g, GLboolean b, GLboolean a) +GL_ENTRY(GLboolean, glIsEnabledi, GLenum target, GLuint index) +GL_ENTRY(void, glDrawElementsBaseVertex, GLenum mode, GLsizei count, GLenum type, const void *indices, GLint basevertex) +GL_ENTRY(void, glDrawRangeElementsBaseVertex, GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices, GLint basevertex) +GL_ENTRY(void, glDrawElementsInstancedBaseVertex, GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex) +GL_ENTRY(void, glFramebufferTexture, GLenum target, GLenum attachment, GLuint texture, GLint level) +GL_ENTRY(void, glPrimitiveBoundingBox, GLfloat minX, GLfloat minY, GLfloat minZ, GLfloat minW, GLfloat maxX, GLfloat maxY, GLfloat maxZ, GLfloat maxW) +GL_ENTRY(GLenum, glGetGraphicsResetStatus, void) +GL_ENTRY(void, glReadnPixels, GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, void *data) +GL_ENTRY(void, glGetnUniformfv, GLuint program, GLint location, GLsizei bufSize, GLfloat *params) +GL_ENTRY(void, glGetnUniformiv, GLuint program, GLint location, GLsizei bufSize, GLint *params) +GL_ENTRY(void, glGetnUniformuiv, GLuint program, GLint location, GLsizei bufSize, GLuint *params) +GL_ENTRY(void, glMinSampleShading, GLfloat value) +GL_ENTRY(void, glPatchParameteri, GLenum pname, GLint value) +GL_ENTRY(void, glTexParameterIiv, GLenum target, GLenum pname, const GLint *params) +GL_ENTRY(void, glTexParameterIuiv, GLenum target, GLenum pname, const GLuint *params) +GL_ENTRY(void, glGetTexParameterIiv, GLenum target, GLenum pname, GLint *params) +GL_ENTRY(void, glGetTexParameterIuiv, GLenum target, GLenum pname, GLuint *params) +GL_ENTRY(void, glSamplerParameterIiv, GLuint sampler, GLenum pname, const GLint *param) +GL_ENTRY(void, glSamplerParameterIuiv, GLuint sampler, GLenum pname, const GLuint *param) +GL_ENTRY(void, glGetSamplerParameterIiv, GLuint sampler, GLenum pname, GLint *params) +GL_ENTRY(void, glGetSamplerParameterIuiv, GLuint sampler, GLenum pname, GLuint *params) +GL_ENTRY(void, glTexBuffer, GLenum target, GLenum internalformat, GLuint buffer) +GL_ENTRY(void, glTexBufferRange, GLenum target, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size) +GL_ENTRY(void, glTexStorage3DMultisample, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations) +GL_ENTRY(void, glBlendBarrierKHR, void) +GL_ENTRY(void, glDebugMessageControlKHR, GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled) +GL_ENTRY(void, glDebugMessageInsertKHR, GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *buf) +GL_ENTRY(void, glDebugMessageCallbackKHR, GLDEBUGPROCKHR callback, const void *userParam) +GL_ENTRY(GLuint, glGetDebugMessageLogKHR, GLuint count, GLsizei bufSize, GLenum *sources, GLenum *types, GLuint *ids, GLenum *severities, GLsizei *lengths, GLchar *messageLog) +GL_ENTRY(void, glPushDebugGroupKHR, GLenum source, GLuint id, GLsizei length, const GLchar *message) +GL_ENTRY(void, glPopDebugGroupKHR, void) +GL_ENTRY(void, glObjectLabelKHR, GLenum identifier, GLuint name, GLsizei length, const GLchar *label) +GL_ENTRY(void, glGetObjectLabelKHR, GLenum identifier, GLuint name, GLsizei bufSize, GLsizei *length, GLchar *label) +GL_ENTRY(void, glObjectPtrLabelKHR, const void *ptr, GLsizei length, const GLchar *label) +GL_ENTRY(void, glGetObjectPtrLabelKHR, const void *ptr, GLsizei bufSize, GLsizei *length, GLchar *label) +GL_ENTRY(void, glGetPointervKHR, GLenum pname, void **params) +GL_ENTRY(GLenum, glGetGraphicsResetStatusKHR, void) +GL_ENTRY(void, glReadnPixelsKHR, GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, void *data) +GL_ENTRY(void, glGetnUniformfvKHR, GLuint program, GLint location, GLsizei bufSize, GLfloat *params) +GL_ENTRY(void, glGetnUniformivKHR, GLuint program, GLint location, GLsizei bufSize, GLint *params) +GL_ENTRY(void, glGetnUniformuivKHR, GLuint program, GLint location, GLsizei bufSize, GLuint *params) +GL_ENTRY(void, glEGLImageTargetTexture2DOES, GLenum target, GLeglImageOES image) +GL_ENTRY(void, glEGLImageTargetRenderbufferStorageOES, GLenum target, GLeglImageOES image) +GL_ENTRY(void, glCopyImageSubDataOES, GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth) +GL_ENTRY(void, glEnableiOES, GLenum target, GLuint index) +GL_ENTRY(void, glDisableiOES, GLenum target, GLuint index) +GL_ENTRY(void, glBlendEquationiOES, GLuint buf, GLenum mode) +GL_ENTRY(void, glBlendEquationSeparateiOES, GLuint buf, GLenum modeRGB, GLenum modeAlpha) +GL_ENTRY(void, glBlendFunciOES, GLuint buf, GLenum src, GLenum dst) +GL_ENTRY(void, glBlendFuncSeparateiOES, GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha) +GL_ENTRY(void, glColorMaskiOES, GLuint index, GLboolean r, GLboolean g, GLboolean b, GLboolean a) +GL_ENTRY(GLboolean, glIsEnablediOES, GLenum target, GLuint index) +GL_ENTRY(void, glDrawElementsBaseVertexOES, GLenum mode, GLsizei count, GLenum type, const void *indices, GLint basevertex) +GL_ENTRY(void, glDrawRangeElementsBaseVertexOES, GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices, GLint basevertex) +GL_ENTRY(void, glDrawElementsInstancedBaseVertexOES, GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex) +GL_ENTRY(void, glMultiDrawElementsBaseVertexOES, GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei primcount, const GLint *basevertex) +GL_ENTRY(void, glFramebufferTextureOES, GLenum target, GLenum attachment, GLuint texture, GLint level) +GL_ENTRY(void, glGetProgramBinaryOES, GLuint program, GLsizei bufSize, GLsizei *length, GLenum *binaryFormat, void *binary) +GL_ENTRY(void, glProgramBinaryOES, GLuint program, GLenum binaryFormat, const void *binary, GLint length) +GL_ENTRY(void *, glMapBufferOES, GLenum target, GLenum access) +GL_ENTRY(GLboolean, glUnmapBufferOES, GLenum target) +GL_ENTRY(void, glGetBufferPointervOES, GLenum target, GLenum pname, void **params) +GL_ENTRY(void, glPrimitiveBoundingBoxOES, GLfloat minX, GLfloat minY, GLfloat minZ, GLfloat minW, GLfloat maxX, GLfloat maxY, GLfloat maxZ, GLfloat maxW) +GL_ENTRY(void, glMinSampleShadingOES, GLfloat value) +GL_ENTRY(void, glPatchParameteriOES, GLenum pname, GLint value) +GL_ENTRY(void, glTexImage3DOES, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels) +GL_ENTRY(void, glTexSubImage3DOES, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels) +GL_ENTRY(void, glCopyTexSubImage3DOES, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height) +GL_ENTRY(void, glCompressedTexImage3DOES, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *data) +GL_ENTRY(void, glCompressedTexSubImage3DOES, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *data) +GL_ENTRY(void, glFramebufferTexture3DOES, GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset) +GL_ENTRY(void, glTexParameterIivOES, GLenum target, GLenum pname, const GLint *params) +GL_ENTRY(void, glTexParameterIuivOES, GLenum target, GLenum pname, const GLuint *params) +GL_ENTRY(void, glGetTexParameterIivOES, GLenum target, GLenum pname, GLint *params) +GL_ENTRY(void, glGetTexParameterIuivOES, GLenum target, GLenum pname, GLuint *params) +GL_ENTRY(void, glSamplerParameterIivOES, GLuint sampler, GLenum pname, const GLint *param) +GL_ENTRY(void, glSamplerParameterIuivOES, GLuint sampler, GLenum pname, const GLuint *param) +GL_ENTRY(void, glGetSamplerParameterIivOES, GLuint sampler, GLenum pname, GLint *params) +GL_ENTRY(void, glGetSamplerParameterIuivOES, GLuint sampler, GLenum pname, GLuint *params) +GL_ENTRY(void, glTexBufferOES, GLenum target, GLenum internalformat, GLuint buffer) +GL_ENTRY(void, glTexBufferRangeOES, GLenum target, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size) +GL_ENTRY(void, glTexStorage3DMultisampleOES, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations) +GL_ENTRY(void, glTextureViewOES, GLuint texture, GLenum target, GLuint origtexture, GLenum internalformat, GLuint minlevel, GLuint numlevels, GLuint minlayer, GLuint numlayers) +GL_ENTRY(void, glBindVertexArrayOES, GLuint array) +GL_ENTRY(void, glDeleteVertexArraysOES, GLsizei n, const GLuint *arrays) +GL_ENTRY(void, glGenVertexArraysOES, GLsizei n, GLuint *arrays) +GL_ENTRY(GLboolean, glIsVertexArrayOES, GLuint array) +GL_ENTRY(void, glDrawArraysInstancedBaseInstanceEXT, GLenum mode, GLint first, GLsizei count, GLsizei instancecount, GLuint baseinstance) +GL_ENTRY(void, glDrawElementsInstancedBaseInstanceEXT, GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLuint baseinstance) +GL_ENTRY(void, glDrawElementsInstancedBaseVertexBaseInstanceEXT, GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex, GLuint baseinstance) +GL_ENTRY(void, glBindFragDataLocationIndexedEXT, GLuint program, GLuint colorNumber, GLuint index, const GLchar *name) +GL_ENTRY(void, glBindFragDataLocationEXT, GLuint program, GLuint color, const GLchar *name) +GL_ENTRY(GLint, glGetProgramResourceLocationIndexEXT, GLuint program, GLenum programInterface, const GLchar *name) +GL_ENTRY(GLint, glGetFragDataIndexEXT, GLuint program, const GLchar *name) +GL_ENTRY(void, glBufferStorageEXT, GLenum target, GLsizeiptr size, const void *data, GLbitfield flags) +GL_ENTRY(void, glCopyImageSubDataEXT, GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth) +GL_ENTRY(void, glLabelObjectEXT, GLenum type, GLuint object, GLsizei length, const GLchar *label) +GL_ENTRY(void, glGetObjectLabelEXT, GLenum type, GLuint object, GLsizei bufSize, GLsizei *length, GLchar *label) +GL_ENTRY(void, glInsertEventMarkerEXT, GLsizei length, const GLchar *marker) +GL_ENTRY(void, glPushGroupMarkerEXT, GLsizei length, const GLchar *marker) +GL_ENTRY(void, glPopGroupMarkerEXT, void) +GL_ENTRY(void, glDiscardFramebufferEXT, GLenum target, GLsizei numAttachments, const GLenum *attachments) +GL_ENTRY(void, glGenQueriesEXT, GLsizei n, GLuint *ids) +GL_ENTRY(void, glDeleteQueriesEXT, GLsizei n, const GLuint *ids) +GL_ENTRY(GLboolean, glIsQueryEXT, GLuint id) +GL_ENTRY(void, glBeginQueryEXT, GLenum target, GLuint id) +GL_ENTRY(void, glEndQueryEXT, GLenum target) +GL_ENTRY(void, glQueryCounterEXT, GLuint id, GLenum target) +GL_ENTRY(void, glGetQueryivEXT, GLenum target, GLenum pname, GLint *params) +GL_ENTRY(void, glGetQueryObjectivEXT, GLuint id, GLenum pname, GLint *params) +GL_ENTRY(void, glGetQueryObjectuivEXT, GLuint id, GLenum pname, GLuint *params) +GL_ENTRY(void, glGetQueryObjecti64vEXT, GLuint id, GLenum pname, GLint64 *params) +GL_ENTRY(void, glGetQueryObjectui64vEXT, GLuint id, GLenum pname, GLuint64 *params) +GL_ENTRY(void, glDrawBuffersEXT, GLsizei n, const GLenum *bufs) +GL_ENTRY(void, glEnableiEXT, GLenum target, GLuint index) +GL_ENTRY(void, glDisableiEXT, GLenum target, GLuint index) +GL_ENTRY(void, glBlendEquationiEXT, GLuint buf, GLenum mode) +GL_ENTRY(void, glBlendEquationSeparateiEXT, GLuint buf, GLenum modeRGB, GLenum modeAlpha) +GL_ENTRY(void, glBlendFunciEXT, GLuint buf, GLenum src, GLenum dst) +GL_ENTRY(void, glBlendFuncSeparateiEXT, GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha) +GL_ENTRY(void, glColorMaskiEXT, GLuint index, GLboolean r, GLboolean g, GLboolean b, GLboolean a) +GL_ENTRY(GLboolean, glIsEnablediEXT, GLenum target, GLuint index) +GL_ENTRY(void, glDrawElementsBaseVertexEXT, GLenum mode, GLsizei count, GLenum type, const void *indices, GLint basevertex) +GL_ENTRY(void, glDrawRangeElementsBaseVertexEXT, GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices, GLint basevertex) +GL_ENTRY(void, glDrawElementsInstancedBaseVertexEXT, GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex) +GL_ENTRY(void, glMultiDrawElementsBaseVertexEXT, GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei primcount, const GLint *basevertex) +GL_ENTRY(void, glDrawArraysInstancedEXT, GLenum mode, GLint start, GLsizei count, GLsizei primcount) +GL_ENTRY(void, glDrawElementsInstancedEXT, GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei primcount) +GL_ENTRY(void, glFramebufferTextureEXT, GLenum target, GLenum attachment, GLuint texture, GLint level) +GL_ENTRY(void, glVertexAttribDivisorEXT, GLuint index, GLuint divisor) +GL_ENTRY(void *, glMapBufferRangeEXT, GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access) +GL_ENTRY(void, glFlushMappedBufferRangeEXT, GLenum target, GLintptr offset, GLsizeiptr length) +GL_ENTRY(void, glMultiDrawArraysEXT, GLenum mode, const GLint *first, const GLsizei *count, GLsizei primcount) +GL_ENTRY(void, glMultiDrawElementsEXT, GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei primcount) +GL_ENTRY(void, glMultiDrawArraysIndirectEXT, GLenum mode, const void *indirect, GLsizei drawcount, GLsizei stride) +GL_ENTRY(void, glMultiDrawElementsIndirectEXT, GLenum mode, GLenum type, const void *indirect, GLsizei drawcount, GLsizei stride) +GL_ENTRY(void, glRenderbufferStorageMultisampleEXT, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height) +GL_ENTRY(void, glFramebufferTexture2DMultisampleEXT, GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLsizei samples) +GL_ENTRY(void, glReadBufferIndexedEXT, GLenum src, GLint index) +GL_ENTRY(void, glDrawBuffersIndexedEXT, GLint n, const GLenum *location, const GLint *indices) +GL_ENTRY(void, glGetIntegeri_vEXT, GLenum target, GLuint index, GLint *data) +GL_ENTRY(void, glPrimitiveBoundingBoxEXT, GLfloat minX, GLfloat minY, GLfloat minZ, GLfloat minW, GLfloat maxX, GLfloat maxY, GLfloat maxZ, GLfloat maxW) +GL_ENTRY(void, glRasterSamplesEXT, GLuint samples, GLboolean fixedsamplelocations) +GL_ENTRY(GLenum, glGetGraphicsResetStatusEXT, void) +GL_ENTRY(void, glReadnPixelsEXT, GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, void *data) +GL_ENTRY(void, glGetnUniformfvEXT, GLuint program, GLint location, GLsizei bufSize, GLfloat *params) +GL_ENTRY(void, glGetnUniformivEXT, GLuint program, GLint location, GLsizei bufSize, GLint *params) +GL_ENTRY(void, glActiveShaderProgramEXT, GLuint pipeline, GLuint program) +GL_ENTRY(void, glBindProgramPipelineEXT, GLuint pipeline) +GL_ENTRY(GLuint, glCreateShaderProgramvEXT, GLenum type, GLsizei count, const GLchar **strings) +GL_ENTRY(void, glDeleteProgramPipelinesEXT, GLsizei n, const GLuint *pipelines) +GL_ENTRY(void, glGenProgramPipelinesEXT, GLsizei n, GLuint *pipelines) +GL_ENTRY(void, glGetProgramPipelineInfoLogEXT, GLuint pipeline, GLsizei bufSize, GLsizei *length, GLchar *infoLog) +GL_ENTRY(void, glGetProgramPipelineivEXT, GLuint pipeline, GLenum pname, GLint *params) +GL_ENTRY(GLboolean, glIsProgramPipelineEXT, GLuint pipeline) +GL_ENTRY(void, glProgramParameteriEXT, GLuint program, GLenum pname, GLint value) +GL_ENTRY(void, glProgramUniform1fEXT, GLuint program, GLint location, GLfloat v0) +GL_ENTRY(void, glProgramUniform1fvEXT, GLuint program, GLint location, GLsizei count, const GLfloat *value) +GL_ENTRY(void, glProgramUniform1iEXT, GLuint program, GLint location, GLint v0) +GL_ENTRY(void, glProgramUniform1ivEXT, GLuint program, GLint location, GLsizei count, const GLint *value) +GL_ENTRY(void, glProgramUniform2fEXT, GLuint program, GLint location, GLfloat v0, GLfloat v1) +GL_ENTRY(void, glProgramUniform2fvEXT, GLuint program, GLint location, GLsizei count, const GLfloat *value) +GL_ENTRY(void, glProgramUniform2iEXT, GLuint program, GLint location, GLint v0, GLint v1) +GL_ENTRY(void, glProgramUniform2ivEXT, GLuint program, GLint location, GLsizei count, const GLint *value) +GL_ENTRY(void, glProgramUniform3fEXT, GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2) +GL_ENTRY(void, glProgramUniform3fvEXT, GLuint program, GLint location, GLsizei count, const GLfloat *value) +GL_ENTRY(void, glProgramUniform3iEXT, GLuint program, GLint location, GLint v0, GLint v1, GLint v2) +GL_ENTRY(void, glProgramUniform3ivEXT, GLuint program, GLint location, GLsizei count, const GLint *value) +GL_ENTRY(void, glProgramUniform4fEXT, GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3) +GL_ENTRY(void, glProgramUniform4fvEXT, GLuint program, GLint location, GLsizei count, const GLfloat *value) +GL_ENTRY(void, glProgramUniform4iEXT, GLuint program, GLint location, GLint v0, GLint v1, GLint v2, GLint v3) +GL_ENTRY(void, glProgramUniform4ivEXT, GLuint program, GLint location, GLsizei count, const GLint *value) +GL_ENTRY(void, glProgramUniformMatrix2fvEXT, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) +GL_ENTRY(void, glProgramUniformMatrix3fvEXT, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) +GL_ENTRY(void, glProgramUniformMatrix4fvEXT, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) +GL_ENTRY(void, glUseProgramStagesEXT, GLuint pipeline, GLbitfield stages, GLuint program) +GL_ENTRY(void, glValidateProgramPipelineEXT, GLuint pipeline) +GL_ENTRY(void, glProgramUniform1uiEXT, GLuint program, GLint location, GLuint v0) +GL_ENTRY(void, glProgramUniform2uiEXT, GLuint program, GLint location, GLuint v0, GLuint v1) +GL_ENTRY(void, glProgramUniform3uiEXT, GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2) +GL_ENTRY(void, glProgramUniform4uiEXT, GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3) +GL_ENTRY(void, glProgramUniform1uivEXT, GLuint program, GLint location, GLsizei count, const GLuint *value) +GL_ENTRY(void, glProgramUniform2uivEXT, GLuint program, GLint location, GLsizei count, const GLuint *value) +GL_ENTRY(void, glProgramUniform3uivEXT, GLuint program, GLint location, GLsizei count, const GLuint *value) +GL_ENTRY(void, glProgramUniform4uivEXT, GLuint program, GLint location, GLsizei count, const GLuint *value) +GL_ENTRY(void, glProgramUniformMatrix2x3fvEXT, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) +GL_ENTRY(void, glProgramUniformMatrix3x2fvEXT, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) +GL_ENTRY(void, glProgramUniformMatrix2x4fvEXT, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) +GL_ENTRY(void, glProgramUniformMatrix4x2fvEXT, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) +GL_ENTRY(void, glProgramUniformMatrix3x4fvEXT, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) +GL_ENTRY(void, glProgramUniformMatrix4x3fvEXT, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) +GL_ENTRY(void, glTexPageCommitmentEXT, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLboolean commit) +GL_ENTRY(void, glPatchParameteriEXT, GLenum pname, GLint value) +GL_ENTRY(void, glTexParameterIivEXT, GLenum target, GLenum pname, const GLint *params) +GL_ENTRY(void, glTexParameterIuivEXT, GLenum target, GLenum pname, const GLuint *params) +GL_ENTRY(void, glGetTexParameterIivEXT, GLenum target, GLenum pname, GLint *params) +GL_ENTRY(void, glGetTexParameterIuivEXT, GLenum target, GLenum pname, GLuint *params) +GL_ENTRY(void, glSamplerParameterIivEXT, GLuint sampler, GLenum pname, const GLint *param) +GL_ENTRY(void, glSamplerParameterIuivEXT, GLuint sampler, GLenum pname, const GLuint *param) +GL_ENTRY(void, glGetSamplerParameterIivEXT, GLuint sampler, GLenum pname, GLint *params) +GL_ENTRY(void, glGetSamplerParameterIuivEXT, GLuint sampler, GLenum pname, GLuint *params) +GL_ENTRY(void, glTexBufferEXT, GLenum target, GLenum internalformat, GLuint buffer) +GL_ENTRY(void, glTexBufferRangeEXT, GLenum target, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size) +GL_ENTRY(void, glTexStorage1DEXT, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width) +GL_ENTRY(void, glTexStorage2DEXT, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height) +GL_ENTRY(void, glTexStorage3DEXT, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth) +GL_ENTRY(void, glTextureStorage1DEXT, GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width) +GL_ENTRY(void, glTextureStorage2DEXT, GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height) +GL_ENTRY(void, glTextureStorage3DEXT, GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth) +GL_ENTRY(void, glTextureViewEXT, GLuint texture, GLenum target, GLuint origtexture, GLenum internalformat, GLuint minlevel, GLuint numlevels, GLuint minlayer, GLuint numlayers)
\ No newline at end of file diff --git a/libs/hwui/debug/gles_redefine.h b/libs/hwui/debug/gles_redefine.h new file mode 100644 index 000000000000..201f0a945209 --- /dev/null +++ b/libs/hwui/debug/gles_redefine.h @@ -0,0 +1,913 @@ +/* + * 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. + */ + +#define glActiveShaderProgram wrap_glActiveShaderProgram +#define glActiveShaderProgramEXT wrap_glActiveShaderProgramEXT +#define glActiveTexture wrap_glActiveTexture +#define glAlphaFunc wrap_glAlphaFunc +#define glAlphaFuncQCOM wrap_glAlphaFuncQCOM +#define glAlphaFuncx wrap_glAlphaFuncx +#define glAlphaFuncxOES wrap_glAlphaFuncxOES +#define glApplyFramebufferAttachmentCMAAINTEL wrap_glApplyFramebufferAttachmentCMAAINTEL +#define glAttachShader wrap_glAttachShader +#define glBeginConditionalRenderNV wrap_glBeginConditionalRenderNV +#define glBeginPerfMonitorAMD wrap_glBeginPerfMonitorAMD +#define glBeginPerfQueryINTEL wrap_glBeginPerfQueryINTEL +#define glBeginQuery wrap_glBeginQuery +#define glBeginQueryEXT wrap_glBeginQueryEXT +#define glBeginTransformFeedback wrap_glBeginTransformFeedback +#define glBindAttribLocation wrap_glBindAttribLocation +#define glBindBuffer wrap_glBindBuffer +#define glBindBufferBase wrap_glBindBufferBase +#define glBindBufferRange wrap_glBindBufferRange +#define glBindFragDataLocationEXT wrap_glBindFragDataLocationEXT +#define glBindFragDataLocationIndexedEXT wrap_glBindFragDataLocationIndexedEXT +#define glBindFramebuffer wrap_glBindFramebuffer +#define glBindFramebufferOES wrap_glBindFramebufferOES +#define glBindImageTexture wrap_glBindImageTexture +#define glBindProgramPipeline wrap_glBindProgramPipeline +#define glBindProgramPipelineEXT wrap_glBindProgramPipelineEXT +#define glBindRenderbuffer wrap_glBindRenderbuffer +#define glBindRenderbufferOES wrap_glBindRenderbufferOES +#define glBindSampler wrap_glBindSampler +#define glBindTexture wrap_glBindTexture +#define glBindTransformFeedback wrap_glBindTransformFeedback +#define glBindVertexArray wrap_glBindVertexArray +#define glBindVertexArrayOES wrap_glBindVertexArrayOES +#define glBindVertexBuffer wrap_glBindVertexBuffer +#define glBlendBarrier wrap_glBlendBarrier +#define glBlendBarrierKHR wrap_glBlendBarrierKHR +#define glBlendBarrierNV wrap_glBlendBarrierNV +#define glBlendColor wrap_glBlendColor +#define glBlendEquation wrap_glBlendEquation +#define glBlendEquationOES wrap_glBlendEquationOES +#define glBlendEquationSeparate wrap_glBlendEquationSeparate +#define glBlendEquationSeparateOES wrap_glBlendEquationSeparateOES +#define glBlendEquationSeparatei wrap_glBlendEquationSeparatei +#define glBlendEquationSeparateiEXT wrap_glBlendEquationSeparateiEXT +#define glBlendEquationSeparateiOES wrap_glBlendEquationSeparateiOES +#define glBlendEquationi wrap_glBlendEquationi +#define glBlendEquationiEXT wrap_glBlendEquationiEXT +#define glBlendEquationiOES wrap_glBlendEquationiOES +#define glBlendFunc wrap_glBlendFunc +#define glBlendFuncSeparate wrap_glBlendFuncSeparate +#define glBlendFuncSeparateOES wrap_glBlendFuncSeparateOES +#define glBlendFuncSeparatei wrap_glBlendFuncSeparatei +#define glBlendFuncSeparateiEXT wrap_glBlendFuncSeparateiEXT +#define glBlendFuncSeparateiOES wrap_glBlendFuncSeparateiOES +#define glBlendFunci wrap_glBlendFunci +#define glBlendFunciEXT wrap_glBlendFunciEXT +#define glBlendFunciOES wrap_glBlendFunciOES +#define glBlendParameteriNV wrap_glBlendParameteriNV +#define glBlitFramebuffer wrap_glBlitFramebuffer +#define glBlitFramebufferANGLE wrap_glBlitFramebufferANGLE +#define glBlitFramebufferNV wrap_glBlitFramebufferNV +#define glBufferData wrap_glBufferData +#define glBufferStorageEXT wrap_glBufferStorageEXT +#define glBufferSubData wrap_glBufferSubData +#define glCheckFramebufferStatus wrap_glCheckFramebufferStatus +#define glCheckFramebufferStatusOES wrap_glCheckFramebufferStatusOES +#define glClear wrap_glClear +#define glClearBufferfi wrap_glClearBufferfi +#define glClearBufferfv wrap_glClearBufferfv +#define glClearBufferiv wrap_glClearBufferiv +#define glClearBufferuiv wrap_glClearBufferuiv +#define glClearColor wrap_glClearColor +#define glClearColorx wrap_glClearColorx +#define glClearColorxOES wrap_glClearColorxOES +#define glClearDepthf wrap_glClearDepthf +#define glClearDepthfOES wrap_glClearDepthfOES +#define glClearDepthx wrap_glClearDepthx +#define glClearDepthxOES wrap_glClearDepthxOES +#define glClearStencil wrap_glClearStencil +#define glClientActiveTexture wrap_glClientActiveTexture +#define glClientWaitSync wrap_glClientWaitSync +#define glClientWaitSyncAPPLE wrap_glClientWaitSyncAPPLE +#define glClipPlanef wrap_glClipPlanef +#define glClipPlanefIMG wrap_glClipPlanefIMG +#define glClipPlanefOES wrap_glClipPlanefOES +#define glClipPlanex wrap_glClipPlanex +#define glClipPlanexIMG wrap_glClipPlanexIMG +#define glClipPlanexOES wrap_glClipPlanexOES +#define glColor4f wrap_glColor4f +#define glColor4ub wrap_glColor4ub +#define glColor4x wrap_glColor4x +#define glColor4xOES wrap_glColor4xOES +#define glColorMask wrap_glColorMask +#define glColorMaski wrap_glColorMaski +#define glColorMaskiEXT wrap_glColorMaskiEXT +#define glColorMaskiOES wrap_glColorMaskiOES +#define glColorPointer wrap_glColorPointer +#define glCompileShader wrap_glCompileShader +#define glCompressedTexImage2D wrap_glCompressedTexImage2D +#define glCompressedTexImage3D wrap_glCompressedTexImage3D +#define glCompressedTexImage3DOES wrap_glCompressedTexImage3DOES +#define glCompressedTexSubImage2D wrap_glCompressedTexSubImage2D +#define glCompressedTexSubImage3D wrap_glCompressedTexSubImage3D +#define glCompressedTexSubImage3DOES wrap_glCompressedTexSubImage3DOES +#define glCopyBufferSubData wrap_glCopyBufferSubData +#define glCopyBufferSubDataNV wrap_glCopyBufferSubDataNV +#define glCopyImageSubData wrap_glCopyImageSubData +#define glCopyImageSubDataEXT wrap_glCopyImageSubDataEXT +#define glCopyImageSubDataOES wrap_glCopyImageSubDataOES +#define glCopyPathNV wrap_glCopyPathNV +#define glCopyTexImage2D wrap_glCopyTexImage2D +#define glCopyTexSubImage2D wrap_glCopyTexSubImage2D +#define glCopyTexSubImage3D wrap_glCopyTexSubImage3D +#define glCopyTexSubImage3DOES wrap_glCopyTexSubImage3DOES +#define glCopyTextureLevelsAPPLE wrap_glCopyTextureLevelsAPPLE +#define glCoverFillPathInstancedNV wrap_glCoverFillPathInstancedNV +#define glCoverFillPathNV wrap_glCoverFillPathNV +#define glCoverStrokePathInstancedNV wrap_glCoverStrokePathInstancedNV +#define glCoverStrokePathNV wrap_glCoverStrokePathNV +#define glCoverageMaskNV wrap_glCoverageMaskNV +#define glCoverageModulationNV wrap_glCoverageModulationNV +#define glCoverageModulationTableNV wrap_glCoverageModulationTableNV +#define glCoverageOperationNV wrap_glCoverageOperationNV +#define glCreatePerfQueryINTEL wrap_glCreatePerfQueryINTEL +#define glCreateProgram wrap_glCreateProgram +#define glCreateShader wrap_glCreateShader +#define glCreateShaderProgramv wrap_glCreateShaderProgramv +#define glCreateShaderProgramvEXT wrap_glCreateShaderProgramvEXT +#define glCullFace wrap_glCullFace +#define glCurrentPaletteMatrixOES wrap_glCurrentPaletteMatrixOES +#define glDebugMessageCallback wrap_glDebugMessageCallback +#define glDebugMessageCallbackKHR wrap_glDebugMessageCallbackKHR +#define glDebugMessageControl wrap_glDebugMessageControl +#define glDebugMessageControlKHR wrap_glDebugMessageControlKHR +#define glDebugMessageInsert wrap_glDebugMessageInsert +#define glDebugMessageInsertKHR wrap_glDebugMessageInsertKHR +#define glDeleteBuffers wrap_glDeleteBuffers +#define glDeleteFencesNV wrap_glDeleteFencesNV +#define glDeleteFramebuffers wrap_glDeleteFramebuffers +#define glDeleteFramebuffersOES wrap_glDeleteFramebuffersOES +#define glDeletePathsNV wrap_glDeletePathsNV +#define glDeletePerfMonitorsAMD wrap_glDeletePerfMonitorsAMD +#define glDeletePerfQueryINTEL wrap_glDeletePerfQueryINTEL +#define glDeleteProgram wrap_glDeleteProgram +#define glDeleteProgramPipelines wrap_glDeleteProgramPipelines +#define glDeleteProgramPipelinesEXT wrap_glDeleteProgramPipelinesEXT +#define glDeleteQueries wrap_glDeleteQueries +#define glDeleteQueriesEXT wrap_glDeleteQueriesEXT +#define glDeleteRenderbuffers wrap_glDeleteRenderbuffers +#define glDeleteRenderbuffersOES wrap_glDeleteRenderbuffersOES +#define glDeleteSamplers wrap_glDeleteSamplers +#define glDeleteShader wrap_glDeleteShader +#define glDeleteSync wrap_glDeleteSync +#define glDeleteSyncAPPLE wrap_glDeleteSyncAPPLE +#define glDeleteTextures wrap_glDeleteTextures +#define glDeleteTransformFeedbacks wrap_glDeleteTransformFeedbacks +#define glDeleteVertexArrays wrap_glDeleteVertexArrays +#define glDeleteVertexArraysOES wrap_glDeleteVertexArraysOES +#define glDepthFunc wrap_glDepthFunc +#define glDepthMask wrap_glDepthMask +#define glDepthRangeArrayfvNV wrap_glDepthRangeArrayfvNV +#define glDepthRangeIndexedfNV wrap_glDepthRangeIndexedfNV +#define glDepthRangef wrap_glDepthRangef +#define glDepthRangefOES wrap_glDepthRangefOES +#define glDepthRangex wrap_glDepthRangex +#define glDepthRangexOES wrap_glDepthRangexOES +#define glDetachShader wrap_glDetachShader +#define glDisable wrap_glDisable +#define glDisableClientState wrap_glDisableClientState +#define glDisableDriverControlQCOM wrap_glDisableDriverControlQCOM +#define glDisableVertexAttribArray wrap_glDisableVertexAttribArray +#define glDisablei wrap_glDisablei +#define glDisableiEXT wrap_glDisableiEXT +#define glDisableiNV wrap_glDisableiNV +#define glDisableiOES wrap_glDisableiOES +#define glDiscardFramebufferEXT wrap_glDiscardFramebufferEXT +#define glDispatchCompute wrap_glDispatchCompute +#define glDispatchComputeIndirect wrap_glDispatchComputeIndirect +#define glDrawArrays wrap_glDrawArrays +#define glDrawArraysIndirect wrap_glDrawArraysIndirect +#define glDrawArraysInstanced wrap_glDrawArraysInstanced +#define glDrawArraysInstancedANGLE wrap_glDrawArraysInstancedANGLE +#define glDrawArraysInstancedBaseInstanceEXT wrap_glDrawArraysInstancedBaseInstanceEXT +#define glDrawArraysInstancedEXT wrap_glDrawArraysInstancedEXT +#define glDrawArraysInstancedNV wrap_glDrawArraysInstancedNV +#define glDrawBuffers wrap_glDrawBuffers +#define glDrawBuffersEXT wrap_glDrawBuffersEXT +#define glDrawBuffersIndexedEXT wrap_glDrawBuffersIndexedEXT +#define glDrawBuffersNV wrap_glDrawBuffersNV +#define glDrawElements wrap_glDrawElements +#define glDrawElementsBaseVertex wrap_glDrawElementsBaseVertex +#define glDrawElementsBaseVertexEXT wrap_glDrawElementsBaseVertexEXT +#define glDrawElementsBaseVertexOES wrap_glDrawElementsBaseVertexOES +#define glDrawElementsIndirect wrap_glDrawElementsIndirect +#define glDrawElementsInstanced wrap_glDrawElementsInstanced +#define glDrawElementsInstancedANGLE wrap_glDrawElementsInstancedANGLE +#define glDrawElementsInstancedBaseInstanceEXT wrap_glDrawElementsInstancedBaseInstanceEXT +#define glDrawElementsInstancedBaseVertex wrap_glDrawElementsInstancedBaseVertex +#define glDrawElementsInstancedBaseVertexBaseInstanceEXT wrap_glDrawElementsInstancedBaseVertexBaseInstanceEXT +#define glDrawElementsInstancedBaseVertexEXT wrap_glDrawElementsInstancedBaseVertexEXT +#define glDrawElementsInstancedBaseVertexOES wrap_glDrawElementsInstancedBaseVertexOES +#define glDrawElementsInstancedEXT wrap_glDrawElementsInstancedEXT +#define glDrawElementsInstancedNV wrap_glDrawElementsInstancedNV +#define glDrawRangeElements wrap_glDrawRangeElements +#define glDrawRangeElementsBaseVertex wrap_glDrawRangeElementsBaseVertex +#define glDrawRangeElementsBaseVertexEXT wrap_glDrawRangeElementsBaseVertexEXT +#define glDrawRangeElementsBaseVertexOES wrap_glDrawRangeElementsBaseVertexOES +#define glDrawTexfOES wrap_glDrawTexfOES +#define glDrawTexfvOES wrap_glDrawTexfvOES +#define glDrawTexiOES wrap_glDrawTexiOES +#define glDrawTexivOES wrap_glDrawTexivOES +#define glDrawTexsOES wrap_glDrawTexsOES +#define glDrawTexsvOES wrap_glDrawTexsvOES +#define glDrawTexxOES wrap_glDrawTexxOES +#define glDrawTexxvOES wrap_glDrawTexxvOES +#define glEGLImageTargetRenderbufferStorageOES wrap_glEGLImageTargetRenderbufferStorageOES +#define glEGLImageTargetTexture2DOES wrap_glEGLImageTargetTexture2DOES +#define glEnable wrap_glEnable +#define glEnableClientState wrap_glEnableClientState +#define glEnableDriverControlQCOM wrap_glEnableDriverControlQCOM +#define glEnableVertexAttribArray wrap_glEnableVertexAttribArray +#define glEnablei wrap_glEnablei +#define glEnableiEXT wrap_glEnableiEXT +#define glEnableiNV wrap_glEnableiNV +#define glEnableiOES wrap_glEnableiOES +#define glEndConditionalRenderNV wrap_glEndConditionalRenderNV +#define glEndPerfMonitorAMD wrap_glEndPerfMonitorAMD +#define glEndPerfQueryINTEL wrap_glEndPerfQueryINTEL +#define glEndQuery wrap_glEndQuery +#define glEndQueryEXT wrap_glEndQueryEXT +#define glEndTilingQCOM wrap_glEndTilingQCOM +#define glEndTransformFeedback wrap_glEndTransformFeedback +#define glExtGetBufferPointervQCOM wrap_glExtGetBufferPointervQCOM +#define glExtGetBuffersQCOM wrap_glExtGetBuffersQCOM +#define glExtGetFramebuffersQCOM wrap_glExtGetFramebuffersQCOM +#define glExtGetProgramBinarySourceQCOM wrap_glExtGetProgramBinarySourceQCOM +#define glExtGetProgramsQCOM wrap_glExtGetProgramsQCOM +#define glExtGetRenderbuffersQCOM wrap_glExtGetRenderbuffersQCOM +#define glExtGetShadersQCOM wrap_glExtGetShadersQCOM +#define glExtGetTexLevelParameterivQCOM wrap_glExtGetTexLevelParameterivQCOM +#define glExtGetTexSubImageQCOM wrap_glExtGetTexSubImageQCOM +#define glExtGetTexturesQCOM wrap_glExtGetTexturesQCOM +#define glExtIsProgramBinaryQCOM wrap_glExtIsProgramBinaryQCOM +#define glExtTexObjectStateOverrideiQCOM wrap_glExtTexObjectStateOverrideiQCOM +#define glFenceSync wrap_glFenceSync +#define glFenceSyncAPPLE wrap_glFenceSyncAPPLE +#define glFinish wrap_glFinish +#define glFinishFenceNV wrap_glFinishFenceNV +#define glFlush wrap_glFlush +#define glFlushMappedBufferRange wrap_glFlushMappedBufferRange +#define glFlushMappedBufferRangeEXT wrap_glFlushMappedBufferRangeEXT +#define glFogf wrap_glFogf +#define glFogfv wrap_glFogfv +#define glFogx wrap_glFogx +#define glFogxOES wrap_glFogxOES +#define glFogxv wrap_glFogxv +#define glFogxvOES wrap_glFogxvOES +#define glFragmentCoverageColorNV wrap_glFragmentCoverageColorNV +#define glFramebufferParameteri wrap_glFramebufferParameteri +#define glFramebufferRenderbuffer wrap_glFramebufferRenderbuffer +#define glFramebufferRenderbufferOES wrap_glFramebufferRenderbufferOES +#define glFramebufferSampleLocationsfvNV wrap_glFramebufferSampleLocationsfvNV +#define glFramebufferTexture wrap_glFramebufferTexture +#define glFramebufferTexture2D wrap_glFramebufferTexture2D +#define glFramebufferTexture2DMultisampleEXT wrap_glFramebufferTexture2DMultisampleEXT +#define glFramebufferTexture2DMultisampleIMG wrap_glFramebufferTexture2DMultisampleIMG +#define glFramebufferTexture2DOES wrap_glFramebufferTexture2DOES +#define glFramebufferTexture3DOES wrap_glFramebufferTexture3DOES +#define glFramebufferTextureEXT wrap_glFramebufferTextureEXT +#define glFramebufferTextureLayer wrap_glFramebufferTextureLayer +#define glFramebufferTextureMultisampleMultiviewOVR wrap_glFramebufferTextureMultisampleMultiviewOVR +#define glFramebufferTextureMultiviewOVR wrap_glFramebufferTextureMultiviewOVR +#define glFramebufferTextureOES wrap_glFramebufferTextureOES +#define glFrontFace wrap_glFrontFace +#define glFrustumf wrap_glFrustumf +#define glFrustumfOES wrap_glFrustumfOES +#define glFrustumx wrap_glFrustumx +#define glFrustumxOES wrap_glFrustumxOES +#define glGenBuffers wrap_glGenBuffers +#define glGenFencesNV wrap_glGenFencesNV +#define glGenFramebuffers wrap_glGenFramebuffers +#define glGenFramebuffersOES wrap_glGenFramebuffersOES +#define glGenPathsNV wrap_glGenPathsNV +#define glGenPerfMonitorsAMD wrap_glGenPerfMonitorsAMD +#define glGenProgramPipelines wrap_glGenProgramPipelines +#define glGenProgramPipelinesEXT wrap_glGenProgramPipelinesEXT +#define glGenQueries wrap_glGenQueries +#define glGenQueriesEXT wrap_glGenQueriesEXT +#define glGenRenderbuffers wrap_glGenRenderbuffers +#define glGenRenderbuffersOES wrap_glGenRenderbuffersOES +#define glGenSamplers wrap_glGenSamplers +#define glGenTextures wrap_glGenTextures +#define glGenTransformFeedbacks wrap_glGenTransformFeedbacks +#define glGenVertexArrays wrap_glGenVertexArrays +#define glGenVertexArraysOES wrap_glGenVertexArraysOES +#define glGenerateMipmap wrap_glGenerateMipmap +#define glGenerateMipmapOES wrap_glGenerateMipmapOES +#define glGetActiveAttrib wrap_glGetActiveAttrib +#define glGetActiveUniform wrap_glGetActiveUniform +#define glGetActiveUniformBlockName wrap_glGetActiveUniformBlockName +#define glGetActiveUniformBlockiv wrap_glGetActiveUniformBlockiv +#define glGetActiveUniformsiv wrap_glGetActiveUniformsiv +#define glGetAttachedShaders wrap_glGetAttachedShaders +#define glGetAttribLocation wrap_glGetAttribLocation +#define glGetBooleani_v wrap_glGetBooleani_v +#define glGetBooleanv wrap_glGetBooleanv +#define glGetBufferParameteri64v wrap_glGetBufferParameteri64v +#define glGetBufferParameteriv wrap_glGetBufferParameteriv +#define glGetBufferPointerv wrap_glGetBufferPointerv +#define glGetBufferPointervOES wrap_glGetBufferPointervOES +#define glGetClipPlanef wrap_glGetClipPlanef +#define glGetClipPlanefOES wrap_glGetClipPlanefOES +#define glGetClipPlanex wrap_glGetClipPlanex +#define glGetClipPlanexOES wrap_glGetClipPlanexOES +#define glGetCoverageModulationTableNV wrap_glGetCoverageModulationTableNV +#define glGetDebugMessageLog wrap_glGetDebugMessageLog +#define glGetDebugMessageLogKHR wrap_glGetDebugMessageLogKHR +#define glGetDriverControlStringQCOM wrap_glGetDriverControlStringQCOM +#define glGetDriverControlsQCOM wrap_glGetDriverControlsQCOM +#define glGetError wrap_glGetError +#define glGetFenceivNV wrap_glGetFenceivNV +#define glGetFirstPerfQueryIdINTEL wrap_glGetFirstPerfQueryIdINTEL +#define glGetFixedv wrap_glGetFixedv +#define glGetFixedvOES wrap_glGetFixedvOES +#define glGetFloati_vNV wrap_glGetFloati_vNV +#define glGetFloatv wrap_glGetFloatv +#define glGetFragDataIndexEXT wrap_glGetFragDataIndexEXT +#define glGetFragDataLocation wrap_glGetFragDataLocation +#define glGetFramebufferAttachmentParameteriv wrap_glGetFramebufferAttachmentParameteriv +#define glGetFramebufferAttachmentParameterivOES wrap_glGetFramebufferAttachmentParameterivOES +#define glGetFramebufferParameteriv wrap_glGetFramebufferParameteriv +#define glGetGraphicsResetStatus wrap_glGetGraphicsResetStatus +#define glGetGraphicsResetStatusEXT wrap_glGetGraphicsResetStatusEXT +#define glGetGraphicsResetStatusKHR wrap_glGetGraphicsResetStatusKHR +#define glGetImageHandleNV wrap_glGetImageHandleNV +#define glGetInteger64i_v wrap_glGetInteger64i_v +#define glGetInteger64v wrap_glGetInteger64v +#define glGetInteger64vAPPLE wrap_glGetInteger64vAPPLE +#define glGetIntegeri_v wrap_glGetIntegeri_v +#define glGetIntegeri_vEXT wrap_glGetIntegeri_vEXT +#define glGetIntegerv wrap_glGetIntegerv +#define glGetInternalformatSampleivNV wrap_glGetInternalformatSampleivNV +#define glGetInternalformativ wrap_glGetInternalformativ +#define glGetLightfv wrap_glGetLightfv +#define glGetLightxv wrap_glGetLightxv +#define glGetLightxvOES wrap_glGetLightxvOES +#define glGetMaterialfv wrap_glGetMaterialfv +#define glGetMaterialxv wrap_glGetMaterialxv +#define glGetMaterialxvOES wrap_glGetMaterialxvOES +#define glGetMultisamplefv wrap_glGetMultisamplefv +#define glGetNextPerfQueryIdINTEL wrap_glGetNextPerfQueryIdINTEL +#define glGetObjectLabel wrap_glGetObjectLabel +#define glGetObjectLabelEXT wrap_glGetObjectLabelEXT +#define glGetObjectLabelKHR wrap_glGetObjectLabelKHR +#define glGetObjectPtrLabel wrap_glGetObjectPtrLabel +#define glGetObjectPtrLabelKHR wrap_glGetObjectPtrLabelKHR +#define glGetPathCommandsNV wrap_glGetPathCommandsNV +#define glGetPathCoordsNV wrap_glGetPathCoordsNV +#define glGetPathDashArrayNV wrap_glGetPathDashArrayNV +#define glGetPathLengthNV wrap_glGetPathLengthNV +#define glGetPathMetricRangeNV wrap_glGetPathMetricRangeNV +#define glGetPathMetricsNV wrap_glGetPathMetricsNV +#define glGetPathParameterfvNV wrap_glGetPathParameterfvNV +#define glGetPathParameterivNV wrap_glGetPathParameterivNV +#define glGetPathSpacingNV wrap_glGetPathSpacingNV +#define glGetPerfCounterInfoINTEL wrap_glGetPerfCounterInfoINTEL +#define glGetPerfMonitorCounterDataAMD wrap_glGetPerfMonitorCounterDataAMD +#define glGetPerfMonitorCounterInfoAMD wrap_glGetPerfMonitorCounterInfoAMD +#define glGetPerfMonitorCounterStringAMD wrap_glGetPerfMonitorCounterStringAMD +#define glGetPerfMonitorCountersAMD wrap_glGetPerfMonitorCountersAMD +#define glGetPerfMonitorGroupStringAMD wrap_glGetPerfMonitorGroupStringAMD +#define glGetPerfMonitorGroupsAMD wrap_glGetPerfMonitorGroupsAMD +#define glGetPerfQueryDataINTEL wrap_glGetPerfQueryDataINTEL +#define glGetPerfQueryIdByNameINTEL wrap_glGetPerfQueryIdByNameINTEL +#define glGetPerfQueryInfoINTEL wrap_glGetPerfQueryInfoINTEL +#define glGetPointerv wrap_glGetPointerv +#define glGetPointervKHR wrap_glGetPointervKHR +#define glGetProgramBinary wrap_glGetProgramBinary +#define glGetProgramBinaryOES wrap_glGetProgramBinaryOES +#define glGetProgramInfoLog wrap_glGetProgramInfoLog +#define glGetProgramInterfaceiv wrap_glGetProgramInterfaceiv +#define glGetProgramPipelineInfoLog wrap_glGetProgramPipelineInfoLog +#define glGetProgramPipelineInfoLogEXT wrap_glGetProgramPipelineInfoLogEXT +#define glGetProgramPipelineiv wrap_glGetProgramPipelineiv +#define glGetProgramPipelineivEXT wrap_glGetProgramPipelineivEXT +#define glGetProgramResourceIndex wrap_glGetProgramResourceIndex +#define glGetProgramResourceLocation wrap_glGetProgramResourceLocation +#define glGetProgramResourceLocationIndexEXT wrap_glGetProgramResourceLocationIndexEXT +#define glGetProgramResourceName wrap_glGetProgramResourceName +#define glGetProgramResourcefvNV wrap_glGetProgramResourcefvNV +#define glGetProgramResourceiv wrap_glGetProgramResourceiv +#define glGetProgramiv wrap_glGetProgramiv +#define glGetQueryObjecti64vEXT wrap_glGetQueryObjecti64vEXT +#define glGetQueryObjectivEXT wrap_glGetQueryObjectivEXT +#define glGetQueryObjectui64vEXT wrap_glGetQueryObjectui64vEXT +#define glGetQueryObjectuiv wrap_glGetQueryObjectuiv +#define glGetQueryObjectuivEXT wrap_glGetQueryObjectuivEXT +#define glGetQueryiv wrap_glGetQueryiv +#define glGetQueryivEXT wrap_glGetQueryivEXT +#define glGetRenderbufferParameteriv wrap_glGetRenderbufferParameteriv +#define glGetRenderbufferParameterivOES wrap_glGetRenderbufferParameterivOES +#define glGetSamplerParameterIiv wrap_glGetSamplerParameterIiv +#define glGetSamplerParameterIivEXT wrap_glGetSamplerParameterIivEXT +#define glGetSamplerParameterIivOES wrap_glGetSamplerParameterIivOES +#define glGetSamplerParameterIuiv wrap_glGetSamplerParameterIuiv +#define glGetSamplerParameterIuivEXT wrap_glGetSamplerParameterIuivEXT +#define glGetSamplerParameterIuivOES wrap_glGetSamplerParameterIuivOES +#define glGetSamplerParameterfv wrap_glGetSamplerParameterfv +#define glGetSamplerParameteriv wrap_glGetSamplerParameteriv +#define glGetShaderInfoLog wrap_glGetShaderInfoLog +#define glGetShaderPrecisionFormat wrap_glGetShaderPrecisionFormat +#define glGetShaderSource wrap_glGetShaderSource +#define glGetShaderiv wrap_glGetShaderiv +#define glGetString wrap_glGetString +#define glGetStringi wrap_glGetStringi +#define glGetSynciv wrap_glGetSynciv +#define glGetSyncivAPPLE wrap_glGetSyncivAPPLE +#define glGetTexEnvfv wrap_glGetTexEnvfv +#define glGetTexEnviv wrap_glGetTexEnviv +#define glGetTexEnvxv wrap_glGetTexEnvxv +#define glGetTexEnvxvOES wrap_glGetTexEnvxvOES +#define glGetTexGenfvOES wrap_glGetTexGenfvOES +#define glGetTexGenivOES wrap_glGetTexGenivOES +#define glGetTexGenxvOES wrap_glGetTexGenxvOES +#define glGetTexLevelParameterfv wrap_glGetTexLevelParameterfv +#define glGetTexLevelParameteriv wrap_glGetTexLevelParameteriv +#define glGetTexParameterIiv wrap_glGetTexParameterIiv +#define glGetTexParameterIivEXT wrap_glGetTexParameterIivEXT +#define glGetTexParameterIivOES wrap_glGetTexParameterIivOES +#define glGetTexParameterIuiv wrap_glGetTexParameterIuiv +#define glGetTexParameterIuivEXT wrap_glGetTexParameterIuivEXT +#define glGetTexParameterIuivOES wrap_glGetTexParameterIuivOES +#define glGetTexParameterfv wrap_glGetTexParameterfv +#define glGetTexParameteriv wrap_glGetTexParameteriv +#define glGetTexParameterxv wrap_glGetTexParameterxv +#define glGetTexParameterxvOES wrap_glGetTexParameterxvOES +#define glGetTextureHandleNV wrap_glGetTextureHandleNV +#define glGetTextureSamplerHandleNV wrap_glGetTextureSamplerHandleNV +#define glGetTransformFeedbackVarying wrap_glGetTransformFeedbackVarying +#define glGetTranslatedShaderSourceANGLE wrap_glGetTranslatedShaderSourceANGLE +#define glGetUniformBlockIndex wrap_glGetUniformBlockIndex +#define glGetUniformIndices wrap_glGetUniformIndices +#define glGetUniformLocation wrap_glGetUniformLocation +#define glGetUniformfv wrap_glGetUniformfv +#define glGetUniformiv wrap_glGetUniformiv +#define glGetUniformuiv wrap_glGetUniformuiv +#define glGetVertexAttribIiv wrap_glGetVertexAttribIiv +#define glGetVertexAttribIuiv wrap_glGetVertexAttribIuiv +#define glGetVertexAttribPointerv wrap_glGetVertexAttribPointerv +#define glGetVertexAttribfv wrap_glGetVertexAttribfv +#define glGetVertexAttribiv wrap_glGetVertexAttribiv +#define glGetnUniformfv wrap_glGetnUniformfv +#define glGetnUniformfvEXT wrap_glGetnUniformfvEXT +#define glGetnUniformfvKHR wrap_glGetnUniformfvKHR +#define glGetnUniformiv wrap_glGetnUniformiv +#define glGetnUniformivEXT wrap_glGetnUniformivEXT +#define glGetnUniformivKHR wrap_glGetnUniformivKHR +#define glGetnUniformuiv wrap_glGetnUniformuiv +#define glGetnUniformuivKHR wrap_glGetnUniformuivKHR +#define glHint wrap_glHint +#define glInsertEventMarkerEXT wrap_glInsertEventMarkerEXT +#define glInterpolatePathsNV wrap_glInterpolatePathsNV +#define glInvalidateFramebuffer wrap_glInvalidateFramebuffer +#define glInvalidateSubFramebuffer wrap_glInvalidateSubFramebuffer +#define glIsBuffer wrap_glIsBuffer +#define glIsEnabled wrap_glIsEnabled +#define glIsEnabledi wrap_glIsEnabledi +#define glIsEnablediEXT wrap_glIsEnablediEXT +#define glIsEnablediNV wrap_glIsEnablediNV +#define glIsEnablediOES wrap_glIsEnablediOES +#define glIsFenceNV wrap_glIsFenceNV +#define glIsFramebuffer wrap_glIsFramebuffer +#define glIsFramebufferOES wrap_glIsFramebufferOES +#define glIsImageHandleResidentNV wrap_glIsImageHandleResidentNV +#define glIsPathNV wrap_glIsPathNV +#define glIsPointInFillPathNV wrap_glIsPointInFillPathNV +#define glIsPointInStrokePathNV wrap_glIsPointInStrokePathNV +#define glIsProgram wrap_glIsProgram +#define glIsProgramPipeline wrap_glIsProgramPipeline +#define glIsProgramPipelineEXT wrap_glIsProgramPipelineEXT +#define glIsQuery wrap_glIsQuery +#define glIsQueryEXT wrap_glIsQueryEXT +#define glIsRenderbuffer wrap_glIsRenderbuffer +#define glIsRenderbufferOES wrap_glIsRenderbufferOES +#define glIsSampler wrap_glIsSampler +#define glIsShader wrap_glIsShader +#define glIsSync wrap_glIsSync +#define glIsSyncAPPLE wrap_glIsSyncAPPLE +#define glIsTexture wrap_glIsTexture +#define glIsTextureHandleResidentNV wrap_glIsTextureHandleResidentNV +#define glIsTransformFeedback wrap_glIsTransformFeedback +#define glIsVertexArray wrap_glIsVertexArray +#define glIsVertexArrayOES wrap_glIsVertexArrayOES +#define glLabelObjectEXT wrap_glLabelObjectEXT +#define glLightModelf wrap_glLightModelf +#define glLightModelfv wrap_glLightModelfv +#define glLightModelx wrap_glLightModelx +#define glLightModelxOES wrap_glLightModelxOES +#define glLightModelxv wrap_glLightModelxv +#define glLightModelxvOES wrap_glLightModelxvOES +#define glLightf wrap_glLightf +#define glLightfv wrap_glLightfv +#define glLightx wrap_glLightx +#define glLightxOES wrap_glLightxOES +#define glLightxv wrap_glLightxv +#define glLightxvOES wrap_glLightxvOES +#define glLineWidth wrap_glLineWidth +#define glLineWidthx wrap_glLineWidthx +#define glLineWidthxOES wrap_glLineWidthxOES +#define glLinkProgram wrap_glLinkProgram +#define glLoadIdentity wrap_glLoadIdentity +#define glLoadMatrixf wrap_glLoadMatrixf +#define glLoadMatrixx wrap_glLoadMatrixx +#define glLoadMatrixxOES wrap_glLoadMatrixxOES +#define glLoadPaletteFromModelViewMatrixOES wrap_glLoadPaletteFromModelViewMatrixOES +#define glLogicOp wrap_glLogicOp +#define glMakeImageHandleNonResidentNV wrap_glMakeImageHandleNonResidentNV +#define glMakeImageHandleResidentNV wrap_glMakeImageHandleResidentNV +#define glMakeTextureHandleNonResidentNV wrap_glMakeTextureHandleNonResidentNV +#define glMakeTextureHandleResidentNV wrap_glMakeTextureHandleResidentNV +#define glMapBufferOES wrap_glMapBufferOES +#define glMapBufferRange wrap_glMapBufferRange +#define glMapBufferRangeEXT wrap_glMapBufferRangeEXT +#define glMaterialf wrap_glMaterialf +#define glMaterialfv wrap_glMaterialfv +#define glMaterialx wrap_glMaterialx +#define glMaterialxOES wrap_glMaterialxOES +#define glMaterialxv wrap_glMaterialxv +#define glMaterialxvOES wrap_glMaterialxvOES +#define glMatrixIndexPointerOES wrap_glMatrixIndexPointerOES +#define glMatrixLoad3x2fNV wrap_glMatrixLoad3x2fNV +#define glMatrixLoad3x3fNV wrap_glMatrixLoad3x3fNV +#define glMatrixLoadTranspose3x3fNV wrap_glMatrixLoadTranspose3x3fNV +#define glMatrixMode wrap_glMatrixMode +#define glMatrixMult3x2fNV wrap_glMatrixMult3x2fNV +#define glMatrixMult3x3fNV wrap_glMatrixMult3x3fNV +#define glMatrixMultTranspose3x3fNV wrap_glMatrixMultTranspose3x3fNV +#define glMemoryBarrier wrap_glMemoryBarrier +#define glMemoryBarrierByRegion wrap_glMemoryBarrierByRegion +#define glMinSampleShading wrap_glMinSampleShading +#define glMinSampleShadingOES wrap_glMinSampleShadingOES +#define glMultMatrixf wrap_glMultMatrixf +#define glMultMatrixx wrap_glMultMatrixx +#define glMultMatrixxOES wrap_glMultMatrixxOES +#define glMultiDrawArraysEXT wrap_glMultiDrawArraysEXT +#define glMultiDrawArraysIndirectEXT wrap_glMultiDrawArraysIndirectEXT +#define glMultiDrawElementsBaseVertexEXT wrap_glMultiDrawElementsBaseVertexEXT +#define glMultiDrawElementsBaseVertexOES wrap_glMultiDrawElementsBaseVertexOES +#define glMultiDrawElementsEXT wrap_glMultiDrawElementsEXT +#define glMultiDrawElementsIndirectEXT wrap_glMultiDrawElementsIndirectEXT +#define glMultiTexCoord4f wrap_glMultiTexCoord4f +#define glMultiTexCoord4x wrap_glMultiTexCoord4x +#define glMultiTexCoord4xOES wrap_glMultiTexCoord4xOES +#define glNamedFramebufferSampleLocationsfvNV wrap_glNamedFramebufferSampleLocationsfvNV +#define glNormal3f wrap_glNormal3f +#define glNormal3x wrap_glNormal3x +#define glNormal3xOES wrap_glNormal3xOES +#define glNormalPointer wrap_glNormalPointer +#define glObjectLabel wrap_glObjectLabel +#define glObjectLabelKHR wrap_glObjectLabelKHR +#define glObjectPtrLabel wrap_glObjectPtrLabel +#define glObjectPtrLabelKHR wrap_glObjectPtrLabelKHR +#define glOrthof wrap_glOrthof +#define glOrthofOES wrap_glOrthofOES +#define glOrthox wrap_glOrthox +#define glOrthoxOES wrap_glOrthoxOES +#define glPatchParameteri wrap_glPatchParameteri +#define glPatchParameteriEXT wrap_glPatchParameteriEXT +#define glPatchParameteriOES wrap_glPatchParameteriOES +#define glPathCommandsNV wrap_glPathCommandsNV +#define glPathCoordsNV wrap_glPathCoordsNV +#define glPathCoverDepthFuncNV wrap_glPathCoverDepthFuncNV +#define glPathDashArrayNV wrap_glPathDashArrayNV +#define glPathGlyphIndexArrayNV wrap_glPathGlyphIndexArrayNV +#define glPathGlyphIndexRangeNV wrap_glPathGlyphIndexRangeNV +#define glPathGlyphRangeNV wrap_glPathGlyphRangeNV +#define glPathGlyphsNV wrap_glPathGlyphsNV +#define glPathMemoryGlyphIndexArrayNV wrap_glPathMemoryGlyphIndexArrayNV +#define glPathParameterfNV wrap_glPathParameterfNV +#define glPathParameterfvNV wrap_glPathParameterfvNV +#define glPathParameteriNV wrap_glPathParameteriNV +#define glPathParameterivNV wrap_glPathParameterivNV +#define glPathStencilDepthOffsetNV wrap_glPathStencilDepthOffsetNV +#define glPathStencilFuncNV wrap_glPathStencilFuncNV +#define glPathStringNV wrap_glPathStringNV +#define glPathSubCommandsNV wrap_glPathSubCommandsNV +#define glPathSubCoordsNV wrap_glPathSubCoordsNV +#define glPauseTransformFeedback wrap_glPauseTransformFeedback +#define glPixelStorei wrap_glPixelStorei +#define glPointAlongPathNV wrap_glPointAlongPathNV +#define glPointParameterf wrap_glPointParameterf +#define glPointParameterfv wrap_glPointParameterfv +#define glPointParameterx wrap_glPointParameterx +#define glPointParameterxOES wrap_glPointParameterxOES +#define glPointParameterxv wrap_glPointParameterxv +#define glPointParameterxvOES wrap_glPointParameterxvOES +#define glPointSize wrap_glPointSize +#define glPointSizePointerOES wrap_glPointSizePointerOES +#define glPointSizex wrap_glPointSizex +#define glPointSizexOES wrap_glPointSizexOES +#define glPolygonModeNV wrap_glPolygonModeNV +#define glPolygonOffset wrap_glPolygonOffset +#define glPolygonOffsetx wrap_glPolygonOffsetx +#define glPolygonOffsetxOES wrap_glPolygonOffsetxOES +#define glPopDebugGroup wrap_glPopDebugGroup +#define glPopDebugGroupKHR wrap_glPopDebugGroupKHR +#define glPopGroupMarkerEXT wrap_glPopGroupMarkerEXT +#define glPopMatrix wrap_glPopMatrix +#define glPrimitiveBoundingBox wrap_glPrimitiveBoundingBox +#define glPrimitiveBoundingBoxEXT wrap_glPrimitiveBoundingBoxEXT +#define glPrimitiveBoundingBoxOES wrap_glPrimitiveBoundingBoxOES +#define glProgramBinary wrap_glProgramBinary +#define glProgramBinaryOES wrap_glProgramBinaryOES +#define glProgramParameteri wrap_glProgramParameteri +#define glProgramParameteriEXT wrap_glProgramParameteriEXT +#define glProgramPathFragmentInputGenNV wrap_glProgramPathFragmentInputGenNV +#define glProgramUniform1f wrap_glProgramUniform1f +#define glProgramUniform1fEXT wrap_glProgramUniform1fEXT +#define glProgramUniform1fv wrap_glProgramUniform1fv +#define glProgramUniform1fvEXT wrap_glProgramUniform1fvEXT +#define glProgramUniform1i wrap_glProgramUniform1i +#define glProgramUniform1iEXT wrap_glProgramUniform1iEXT +#define glProgramUniform1iv wrap_glProgramUniform1iv +#define glProgramUniform1ivEXT wrap_glProgramUniform1ivEXT +#define glProgramUniform1ui wrap_glProgramUniform1ui +#define glProgramUniform1uiEXT wrap_glProgramUniform1uiEXT +#define glProgramUniform1uiv wrap_glProgramUniform1uiv +#define glProgramUniform1uivEXT wrap_glProgramUniform1uivEXT +#define glProgramUniform2f wrap_glProgramUniform2f +#define glProgramUniform2fEXT wrap_glProgramUniform2fEXT +#define glProgramUniform2fv wrap_glProgramUniform2fv +#define glProgramUniform2fvEXT wrap_glProgramUniform2fvEXT +#define glProgramUniform2i wrap_glProgramUniform2i +#define glProgramUniform2iEXT wrap_glProgramUniform2iEXT +#define glProgramUniform2iv wrap_glProgramUniform2iv +#define glProgramUniform2ivEXT wrap_glProgramUniform2ivEXT +#define glProgramUniform2ui wrap_glProgramUniform2ui +#define glProgramUniform2uiEXT wrap_glProgramUniform2uiEXT +#define glProgramUniform2uiv wrap_glProgramUniform2uiv +#define glProgramUniform2uivEXT wrap_glProgramUniform2uivEXT +#define glProgramUniform3f wrap_glProgramUniform3f +#define glProgramUniform3fEXT wrap_glProgramUniform3fEXT +#define glProgramUniform3fv wrap_glProgramUniform3fv +#define glProgramUniform3fvEXT wrap_glProgramUniform3fvEXT +#define glProgramUniform3i wrap_glProgramUniform3i +#define glProgramUniform3iEXT wrap_glProgramUniform3iEXT +#define glProgramUniform3iv wrap_glProgramUniform3iv +#define glProgramUniform3ivEXT wrap_glProgramUniform3ivEXT +#define glProgramUniform3ui wrap_glProgramUniform3ui +#define glProgramUniform3uiEXT wrap_glProgramUniform3uiEXT +#define glProgramUniform3uiv wrap_glProgramUniform3uiv +#define glProgramUniform3uivEXT wrap_glProgramUniform3uivEXT +#define glProgramUniform4f wrap_glProgramUniform4f +#define glProgramUniform4fEXT wrap_glProgramUniform4fEXT +#define glProgramUniform4fv wrap_glProgramUniform4fv +#define glProgramUniform4fvEXT wrap_glProgramUniform4fvEXT +#define glProgramUniform4i wrap_glProgramUniform4i +#define glProgramUniform4iEXT wrap_glProgramUniform4iEXT +#define glProgramUniform4iv wrap_glProgramUniform4iv +#define glProgramUniform4ivEXT wrap_glProgramUniform4ivEXT +#define glProgramUniform4ui wrap_glProgramUniform4ui +#define glProgramUniform4uiEXT wrap_glProgramUniform4uiEXT +#define glProgramUniform4uiv wrap_glProgramUniform4uiv +#define glProgramUniform4uivEXT wrap_glProgramUniform4uivEXT +#define glProgramUniformHandleui64NV wrap_glProgramUniformHandleui64NV +#define glProgramUniformHandleui64vNV wrap_glProgramUniformHandleui64vNV +#define glProgramUniformMatrix2fv wrap_glProgramUniformMatrix2fv +#define glProgramUniformMatrix2fvEXT wrap_glProgramUniformMatrix2fvEXT +#define glProgramUniformMatrix2x3fv wrap_glProgramUniformMatrix2x3fv +#define glProgramUniformMatrix2x3fvEXT wrap_glProgramUniformMatrix2x3fvEXT +#define glProgramUniformMatrix2x4fv wrap_glProgramUniformMatrix2x4fv +#define glProgramUniformMatrix2x4fvEXT wrap_glProgramUniformMatrix2x4fvEXT +#define glProgramUniformMatrix3fv wrap_glProgramUniformMatrix3fv +#define glProgramUniformMatrix3fvEXT wrap_glProgramUniformMatrix3fvEXT +#define glProgramUniformMatrix3x2fv wrap_glProgramUniformMatrix3x2fv +#define glProgramUniformMatrix3x2fvEXT wrap_glProgramUniformMatrix3x2fvEXT +#define glProgramUniformMatrix3x4fv wrap_glProgramUniformMatrix3x4fv +#define glProgramUniformMatrix3x4fvEXT wrap_glProgramUniformMatrix3x4fvEXT +#define glProgramUniformMatrix4fv wrap_glProgramUniformMatrix4fv +#define glProgramUniformMatrix4fvEXT wrap_glProgramUniformMatrix4fvEXT +#define glProgramUniformMatrix4x2fv wrap_glProgramUniformMatrix4x2fv +#define glProgramUniformMatrix4x2fvEXT wrap_glProgramUniformMatrix4x2fvEXT +#define glProgramUniformMatrix4x3fv wrap_glProgramUniformMatrix4x3fv +#define glProgramUniformMatrix4x3fvEXT wrap_glProgramUniformMatrix4x3fvEXT +#define glPushDebugGroup wrap_glPushDebugGroup +#define glPushDebugGroupKHR wrap_glPushDebugGroupKHR +#define glPushGroupMarkerEXT wrap_glPushGroupMarkerEXT +#define glPushMatrix wrap_glPushMatrix +#define glQueryCounterEXT wrap_glQueryCounterEXT +#define glQueryMatrixxOES wrap_glQueryMatrixxOES +#define glRasterSamplesEXT wrap_glRasterSamplesEXT +#define glReadBuffer wrap_glReadBuffer +#define glReadBufferIndexedEXT wrap_glReadBufferIndexedEXT +#define glReadBufferNV wrap_glReadBufferNV +#define glReadPixels wrap_glReadPixels +#define glReadnPixels wrap_glReadnPixels +#define glReadnPixelsEXT wrap_glReadnPixelsEXT +#define glReadnPixelsKHR wrap_glReadnPixelsKHR +#define glReleaseShaderCompiler wrap_glReleaseShaderCompiler +#define glRenderbufferStorage wrap_glRenderbufferStorage +#define glRenderbufferStorageMultisample wrap_glRenderbufferStorageMultisample +#define glRenderbufferStorageMultisampleANGLE wrap_glRenderbufferStorageMultisampleANGLE +#define glRenderbufferStorageMultisampleAPPLE wrap_glRenderbufferStorageMultisampleAPPLE +#define glRenderbufferStorageMultisampleEXT wrap_glRenderbufferStorageMultisampleEXT +#define glRenderbufferStorageMultisampleIMG wrap_glRenderbufferStorageMultisampleIMG +#define glRenderbufferStorageMultisampleNV wrap_glRenderbufferStorageMultisampleNV +#define glRenderbufferStorageOES wrap_glRenderbufferStorageOES +#define glResolveDepthValuesNV wrap_glResolveDepthValuesNV +#define glResolveMultisampleFramebufferAPPLE wrap_glResolveMultisampleFramebufferAPPLE +#define glResumeTransformFeedback wrap_glResumeTransformFeedback +#define glRotatef wrap_glRotatef +#define glRotatex wrap_glRotatex +#define glRotatexOES wrap_glRotatexOES +#define glSampleCoverage wrap_glSampleCoverage +#define glSampleCoveragex wrap_glSampleCoveragex +#define glSampleCoveragexOES wrap_glSampleCoveragexOES +#define glSampleMaski wrap_glSampleMaski +#define glSamplerParameterIiv wrap_glSamplerParameterIiv +#define glSamplerParameterIivEXT wrap_glSamplerParameterIivEXT +#define glSamplerParameterIivOES wrap_glSamplerParameterIivOES +#define glSamplerParameterIuiv wrap_glSamplerParameterIuiv +#define glSamplerParameterIuivEXT wrap_glSamplerParameterIuivEXT +#define glSamplerParameterIuivOES wrap_glSamplerParameterIuivOES +#define glSamplerParameterf wrap_glSamplerParameterf +#define glSamplerParameterfv wrap_glSamplerParameterfv +#define glSamplerParameteri wrap_glSamplerParameteri +#define glSamplerParameteriv wrap_glSamplerParameteriv +#define glScalef wrap_glScalef +#define glScalex wrap_glScalex +#define glScalexOES wrap_glScalexOES +#define glScissor wrap_glScissor +#define glScissorArrayvNV wrap_glScissorArrayvNV +#define glScissorIndexedNV wrap_glScissorIndexedNV +#define glScissorIndexedvNV wrap_glScissorIndexedvNV +#define glSelectPerfMonitorCountersAMD wrap_glSelectPerfMonitorCountersAMD +#define glSetFenceNV wrap_glSetFenceNV +#define glShadeModel wrap_glShadeModel +#define glShaderBinary wrap_glShaderBinary +#define glShaderSource wrap_glShaderSource +#define glStartTilingQCOM wrap_glStartTilingQCOM +#define glStencilFillPathInstancedNV wrap_glStencilFillPathInstancedNV +#define glStencilFillPathNV wrap_glStencilFillPathNV +#define glStencilFunc wrap_glStencilFunc +#define glStencilFuncSeparate wrap_glStencilFuncSeparate +#define glStencilMask wrap_glStencilMask +#define glStencilMaskSeparate wrap_glStencilMaskSeparate +#define glStencilOp wrap_glStencilOp +#define glStencilOpSeparate wrap_glStencilOpSeparate +#define glStencilStrokePathInstancedNV wrap_glStencilStrokePathInstancedNV +#define glStencilStrokePathNV wrap_glStencilStrokePathNV +#define glStencilThenCoverFillPathInstancedNV wrap_glStencilThenCoverFillPathInstancedNV +#define glStencilThenCoverFillPathNV wrap_glStencilThenCoverFillPathNV +#define glStencilThenCoverStrokePathInstancedNV wrap_glStencilThenCoverStrokePathInstancedNV +#define glStencilThenCoverStrokePathNV wrap_glStencilThenCoverStrokePathNV +#define glSubpixelPrecisionBiasNV wrap_glSubpixelPrecisionBiasNV +#define glTestFenceNV wrap_glTestFenceNV +#define glTexBuffer wrap_glTexBuffer +#define glTexBufferEXT wrap_glTexBufferEXT +#define glTexBufferOES wrap_glTexBufferOES +#define glTexBufferRange wrap_glTexBufferRange +#define glTexBufferRangeEXT wrap_glTexBufferRangeEXT +#define glTexBufferRangeOES wrap_glTexBufferRangeOES +#define glTexCoordPointer wrap_glTexCoordPointer +#define glTexEnvf wrap_glTexEnvf +#define glTexEnvfv wrap_glTexEnvfv +#define glTexEnvi wrap_glTexEnvi +#define glTexEnviv wrap_glTexEnviv +#define glTexEnvx wrap_glTexEnvx +#define glTexEnvxOES wrap_glTexEnvxOES +#define glTexEnvxv wrap_glTexEnvxv +#define glTexEnvxvOES wrap_glTexEnvxvOES +#define glTexGenfOES wrap_glTexGenfOES +#define glTexGenfvOES wrap_glTexGenfvOES +#define glTexGeniOES wrap_glTexGeniOES +#define glTexGenivOES wrap_glTexGenivOES +#define glTexGenxOES wrap_glTexGenxOES +#define glTexGenxvOES wrap_glTexGenxvOES +#define glTexImage2D wrap_glTexImage2D +#define glTexImage3D wrap_glTexImage3D +#define glTexImage3DOES wrap_glTexImage3DOES +#define glTexPageCommitmentEXT wrap_glTexPageCommitmentEXT +#define glTexParameterIiv wrap_glTexParameterIiv +#define glTexParameterIivEXT wrap_glTexParameterIivEXT +#define glTexParameterIivOES wrap_glTexParameterIivOES +#define glTexParameterIuiv wrap_glTexParameterIuiv +#define glTexParameterIuivEXT wrap_glTexParameterIuivEXT +#define glTexParameterIuivOES wrap_glTexParameterIuivOES +#define glTexParameterf wrap_glTexParameterf +#define glTexParameterfv wrap_glTexParameterfv +#define glTexParameteri wrap_glTexParameteri +#define glTexParameteriv wrap_glTexParameteriv +#define glTexParameterx wrap_glTexParameterx +#define glTexParameterxOES wrap_glTexParameterxOES +#define glTexParameterxv wrap_glTexParameterxv +#define glTexParameterxvOES wrap_glTexParameterxvOES +#define glTexStorage1DEXT wrap_glTexStorage1DEXT +#define glTexStorage2D wrap_glTexStorage2D +#define glTexStorage2DEXT wrap_glTexStorage2DEXT +#define glTexStorage2DMultisample wrap_glTexStorage2DMultisample +#define glTexStorage3D wrap_glTexStorage3D +#define glTexStorage3DEXT wrap_glTexStorage3DEXT +#define glTexStorage3DMultisample wrap_glTexStorage3DMultisample +#define glTexStorage3DMultisampleOES wrap_glTexStorage3DMultisampleOES +#define glTexSubImage2D wrap_glTexSubImage2D +#define glTexSubImage3D wrap_glTexSubImage3D +#define glTexSubImage3DOES wrap_glTexSubImage3DOES +#define glTextureStorage1DEXT wrap_glTextureStorage1DEXT +#define glTextureStorage2DEXT wrap_glTextureStorage2DEXT +#define glTextureStorage3DEXT wrap_glTextureStorage3DEXT +#define glTextureViewEXT wrap_glTextureViewEXT +#define glTextureViewOES wrap_glTextureViewOES +#define glTransformFeedbackVaryings wrap_glTransformFeedbackVaryings +#define glTransformPathNV wrap_glTransformPathNV +#define glTranslatef wrap_glTranslatef +#define glTranslatex wrap_glTranslatex +#define glTranslatexOES wrap_glTranslatexOES +#define glUniform1f wrap_glUniform1f +#define glUniform1fv wrap_glUniform1fv +#define glUniform1i wrap_glUniform1i +#define glUniform1iv wrap_glUniform1iv +#define glUniform1ui wrap_glUniform1ui +#define glUniform1uiv wrap_glUniform1uiv +#define glUniform2f wrap_glUniform2f +#define glUniform2fv wrap_glUniform2fv +#define glUniform2i wrap_glUniform2i +#define glUniform2iv wrap_glUniform2iv +#define glUniform2ui wrap_glUniform2ui +#define glUniform2uiv wrap_glUniform2uiv +#define glUniform3f wrap_glUniform3f +#define glUniform3fv wrap_glUniform3fv +#define glUniform3i wrap_glUniform3i +#define glUniform3iv wrap_glUniform3iv +#define glUniform3ui wrap_glUniform3ui +#define glUniform3uiv wrap_glUniform3uiv +#define glUniform4f wrap_glUniform4f +#define glUniform4fv wrap_glUniform4fv +#define glUniform4i wrap_glUniform4i +#define glUniform4iv wrap_glUniform4iv +#define glUniform4ui wrap_glUniform4ui +#define glUniform4uiv wrap_glUniform4uiv +#define glUniformBlockBinding wrap_glUniformBlockBinding +#define glUniformHandleui64NV wrap_glUniformHandleui64NV +#define glUniformHandleui64vNV wrap_glUniformHandleui64vNV +#define glUniformMatrix2fv wrap_glUniformMatrix2fv +#define glUniformMatrix2x3fv wrap_glUniformMatrix2x3fv +#define glUniformMatrix2x3fvNV wrap_glUniformMatrix2x3fvNV +#define glUniformMatrix2x4fv wrap_glUniformMatrix2x4fv +#define glUniformMatrix2x4fvNV wrap_glUniformMatrix2x4fvNV +#define glUniformMatrix3fv wrap_glUniformMatrix3fv +#define glUniformMatrix3x2fv wrap_glUniformMatrix3x2fv +#define glUniformMatrix3x2fvNV wrap_glUniformMatrix3x2fvNV +#define glUniformMatrix3x4fv wrap_glUniformMatrix3x4fv +#define glUniformMatrix3x4fvNV wrap_glUniformMatrix3x4fvNV +#define glUniformMatrix4fv wrap_glUniformMatrix4fv +#define glUniformMatrix4x2fv wrap_glUniformMatrix4x2fv +#define glUniformMatrix4x2fvNV wrap_glUniformMatrix4x2fvNV +#define glUniformMatrix4x3fv wrap_glUniformMatrix4x3fv +#define glUniformMatrix4x3fvNV wrap_glUniformMatrix4x3fvNV +#define glUnmapBuffer wrap_glUnmapBuffer +#define glUnmapBufferOES wrap_glUnmapBufferOES +#define glUseProgram wrap_glUseProgram +#define glUseProgramStages wrap_glUseProgramStages +#define glUseProgramStagesEXT wrap_glUseProgramStagesEXT +#define glValidateProgram wrap_glValidateProgram +#define glValidateProgramPipeline wrap_glValidateProgramPipeline +#define glValidateProgramPipelineEXT wrap_glValidateProgramPipelineEXT +#define glVertexAttrib1f wrap_glVertexAttrib1f +#define glVertexAttrib1fv wrap_glVertexAttrib1fv +#define glVertexAttrib2f wrap_glVertexAttrib2f +#define glVertexAttrib2fv wrap_glVertexAttrib2fv +#define glVertexAttrib3f wrap_glVertexAttrib3f +#define glVertexAttrib3fv wrap_glVertexAttrib3fv +#define glVertexAttrib4f wrap_glVertexAttrib4f +#define glVertexAttrib4fv wrap_glVertexAttrib4fv +#define glVertexAttribBinding wrap_glVertexAttribBinding +#define glVertexAttribDivisor wrap_glVertexAttribDivisor +#define glVertexAttribDivisorANGLE wrap_glVertexAttribDivisorANGLE +#define glVertexAttribDivisorEXT wrap_glVertexAttribDivisorEXT +#define glVertexAttribDivisorNV wrap_glVertexAttribDivisorNV +#define glVertexAttribFormat wrap_glVertexAttribFormat +#define glVertexAttribI4i wrap_glVertexAttribI4i +#define glVertexAttribI4iv wrap_glVertexAttribI4iv +#define glVertexAttribI4ui wrap_glVertexAttribI4ui +#define glVertexAttribI4uiv wrap_glVertexAttribI4uiv +#define glVertexAttribIFormat wrap_glVertexAttribIFormat +#define glVertexAttribIPointer wrap_glVertexAttribIPointer +#define glVertexAttribPointer wrap_glVertexAttribPointer +#define glVertexBindingDivisor wrap_glVertexBindingDivisor +#define glVertexPointer wrap_glVertexPointer +#define glViewport wrap_glViewport +#define glViewportArrayvNV wrap_glViewportArrayvNV +#define glViewportIndexedfNV wrap_glViewportIndexedfNV +#define glViewportIndexedfvNV wrap_glViewportIndexedfvNV +#define glWaitSync wrap_glWaitSync +#define glWaitSyncAPPLE wrap_glWaitSyncAPPLE +#define glWeightPathsNV wrap_glWeightPathsNV +#define glWeightPointerOES wrap_glWeightPointerOES
\ No newline at end of file diff --git a/libs/hwui/debug/gles_stubs.in b/libs/hwui/debug/gles_stubs.in new file mode 100644 index 000000000000..4064a391a71d --- /dev/null +++ b/libs/hwui/debug/gles_stubs.in @@ -0,0 +1,1632 @@ +void API_ENTRY(glActiveTexture)(GLenum texture) { + CALL_GL_API(glActiveTexture, texture); +} +void API_ENTRY(glAttachShader)(GLuint program, GLuint shader) { + CALL_GL_API(glAttachShader, program, shader); +} +void API_ENTRY(glBindAttribLocation)(GLuint program, GLuint index, const GLchar *name) { + CALL_GL_API(glBindAttribLocation, program, index, name); +} +void API_ENTRY(glBindBuffer)(GLenum target, GLuint buffer) { + CALL_GL_API(glBindBuffer, target, buffer); +} +void API_ENTRY(glBindFramebuffer)(GLenum target, GLuint framebuffer) { + CALL_GL_API(glBindFramebuffer, target, framebuffer); +} +void API_ENTRY(glBindRenderbuffer)(GLenum target, GLuint renderbuffer) { + CALL_GL_API(glBindRenderbuffer, target, renderbuffer); +} +void API_ENTRY(glBindTexture)(GLenum target, GLuint texture) { + CALL_GL_API(glBindTexture, target, texture); +} +void API_ENTRY(glBlendColor)(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha) { + CALL_GL_API(glBlendColor, red, green, blue, alpha); +} +void API_ENTRY(glBlendEquation)(GLenum mode) { + CALL_GL_API(glBlendEquation, mode); +} +void API_ENTRY(glBlendEquationSeparate)(GLenum modeRGB, GLenum modeAlpha) { + CALL_GL_API(glBlendEquationSeparate, modeRGB, modeAlpha); +} +void API_ENTRY(glBlendFunc)(GLenum sfactor, GLenum dfactor) { + CALL_GL_API(glBlendFunc, sfactor, dfactor); +} +void API_ENTRY(glBlendFuncSeparate)(GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha) { + CALL_GL_API(glBlendFuncSeparate, sfactorRGB, dfactorRGB, sfactorAlpha, dfactorAlpha); +} +void API_ENTRY(glBufferData)(GLenum target, GLsizeiptr size, const void *data, GLenum usage) { + CALL_GL_API(glBufferData, target, size, data, usage); +} +void API_ENTRY(glBufferSubData)(GLenum target, GLintptr offset, GLsizeiptr size, const void *data) { + CALL_GL_API(glBufferSubData, target, offset, size, data); +} +GLenum API_ENTRY(glCheckFramebufferStatus)(GLenum target) { + CALL_GL_API_RETURN(glCheckFramebufferStatus, target); +} +void API_ENTRY(glClear)(GLbitfield mask) { + CALL_GL_API(glClear, mask); +} +void API_ENTRY(glClearColor)(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha) { + CALL_GL_API(glClearColor, red, green, blue, alpha); +} +void API_ENTRY(glClearDepthf)(GLfloat d) { + CALL_GL_API(glClearDepthf, d); +} +void API_ENTRY(glClearStencil)(GLint s) { + CALL_GL_API(glClearStencil, s); +} +void API_ENTRY(glColorMask)(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha) { + CALL_GL_API(glColorMask, red, green, blue, alpha); +} +void API_ENTRY(glCompileShader)(GLuint shader) { + CALL_GL_API(glCompileShader, shader); +} +void API_ENTRY(glCompressedTexImage2D)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *data) { + CALL_GL_API(glCompressedTexImage2D, target, level, internalformat, width, height, border, imageSize, data); +} +void API_ENTRY(glCompressedTexSubImage2D)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data) { + CALL_GL_API(glCompressedTexSubImage2D, target, level, xoffset, yoffset, width, height, format, imageSize, data); +} +void API_ENTRY(glCopyTexImage2D)(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border) { + CALL_GL_API(glCopyTexImage2D, target, level, internalformat, x, y, width, height, border); +} +void API_ENTRY(glCopyTexSubImage2D)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height) { + CALL_GL_API(glCopyTexSubImage2D, target, level, xoffset, yoffset, x, y, width, height); +} +GLuint API_ENTRY(glCreateProgram)(void) { + CALL_GL_API_RETURN(glCreateProgram); +} +GLuint API_ENTRY(glCreateShader)(GLenum type) { + CALL_GL_API_RETURN(glCreateShader, type); +} +void API_ENTRY(glCullFace)(GLenum mode) { + CALL_GL_API(glCullFace, mode); +} +void API_ENTRY(glDeleteBuffers)(GLsizei n, const GLuint *buffers) { + CALL_GL_API(glDeleteBuffers, n, buffers); +} +void API_ENTRY(glDeleteFramebuffers)(GLsizei n, const GLuint *framebuffers) { + CALL_GL_API(glDeleteFramebuffers, n, framebuffers); +} +void API_ENTRY(glDeleteProgram)(GLuint program) { + CALL_GL_API(glDeleteProgram, program); +} +void API_ENTRY(glDeleteRenderbuffers)(GLsizei n, const GLuint *renderbuffers) { + CALL_GL_API(glDeleteRenderbuffers, n, renderbuffers); +} +void API_ENTRY(glDeleteShader)(GLuint shader) { + CALL_GL_API(glDeleteShader, shader); +} +void API_ENTRY(glDeleteTextures)(GLsizei n, const GLuint *textures) { + CALL_GL_API(glDeleteTextures, n, textures); +} +void API_ENTRY(glDepthFunc)(GLenum func) { + CALL_GL_API(glDepthFunc, func); +} +void API_ENTRY(glDepthMask)(GLboolean flag) { + CALL_GL_API(glDepthMask, flag); +} +void API_ENTRY(glDepthRangef)(GLfloat n, GLfloat f) { + CALL_GL_API(glDepthRangef, n, f); +} +void API_ENTRY(glDetachShader)(GLuint program, GLuint shader) { + CALL_GL_API(glDetachShader, program, shader); +} +void API_ENTRY(glDisable)(GLenum cap) { + CALL_GL_API(glDisable, cap); +} +void API_ENTRY(glDisableVertexAttribArray)(GLuint index) { + CALL_GL_API(glDisableVertexAttribArray, index); +} +void API_ENTRY(glDrawArrays)(GLenum mode, GLint first, GLsizei count) { + CALL_GL_API(glDrawArrays, mode, first, count); +} +void API_ENTRY(glDrawElements)(GLenum mode, GLsizei count, GLenum type, const void *indices) { + CALL_GL_API(glDrawElements, mode, count, type, indices); +} +void API_ENTRY(glEnable)(GLenum cap) { + CALL_GL_API(glEnable, cap); +} +void API_ENTRY(glEnableVertexAttribArray)(GLuint index) { + CALL_GL_API(glEnableVertexAttribArray, index); +} +void API_ENTRY(glFinish)(void) { + CALL_GL_API(glFinish); +} +void API_ENTRY(glFlush)(void) { + CALL_GL_API(glFlush); +} +void API_ENTRY(glFramebufferRenderbuffer)(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer) { + CALL_GL_API(glFramebufferRenderbuffer, target, attachment, renderbuffertarget, renderbuffer); +} +void API_ENTRY(glFramebufferTexture2D)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level) { + CALL_GL_API(glFramebufferTexture2D, target, attachment, textarget, texture, level); +} +void API_ENTRY(glFrontFace)(GLenum mode) { + CALL_GL_API(glFrontFace, mode); +} +void API_ENTRY(glGenBuffers)(GLsizei n, GLuint *buffers) { + CALL_GL_API(glGenBuffers, n, buffers); +} +void API_ENTRY(glGenerateMipmap)(GLenum target) { + CALL_GL_API(glGenerateMipmap, target); +} +void API_ENTRY(glGenFramebuffers)(GLsizei n, GLuint *framebuffers) { + CALL_GL_API(glGenFramebuffers, n, framebuffers); +} +void API_ENTRY(glGenRenderbuffers)(GLsizei n, GLuint *renderbuffers) { + CALL_GL_API(glGenRenderbuffers, n, renderbuffers); +} +void API_ENTRY(glGenTextures)(GLsizei n, GLuint *textures) { + CALL_GL_API(glGenTextures, n, textures); +} +void API_ENTRY(glGetActiveAttrib)(GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name) { + CALL_GL_API(glGetActiveAttrib, program, index, bufSize, length, size, type, name); +} +void API_ENTRY(glGetActiveUniform)(GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name) { + CALL_GL_API(glGetActiveUniform, program, index, bufSize, length, size, type, name); +} +void API_ENTRY(glGetAttachedShaders)(GLuint program, GLsizei maxCount, GLsizei *count, GLuint *shaders) { + CALL_GL_API(glGetAttachedShaders, program, maxCount, count, shaders); +} +GLint API_ENTRY(glGetAttribLocation)(GLuint program, const GLchar *name) { + CALL_GL_API_RETURN(glGetAttribLocation, program, name); +} +void API_ENTRY(glGetBooleanv)(GLenum pname, GLboolean *data) { + CALL_GL_API(glGetBooleanv, pname, data); +} +void API_ENTRY(glGetBufferParameteriv)(GLenum target, GLenum pname, GLint *params) { + CALL_GL_API(glGetBufferParameteriv, target, pname, params); +} +GLenum API_ENTRY(glGetError)(void) { + CALL_GL_API_RETURN(glGetError); +} +void API_ENTRY(glGetFloatv)(GLenum pname, GLfloat *data) { + CALL_GL_API(glGetFloatv, pname, data); +} +void API_ENTRY(glGetFramebufferAttachmentParameteriv)(GLenum target, GLenum attachment, GLenum pname, GLint *params) { + CALL_GL_API(glGetFramebufferAttachmentParameteriv, target, attachment, pname, params); +} +void API_ENTRY(glGetIntegerv)(GLenum pname, GLint *data) { + CALL_GL_API(glGetIntegerv, pname, data); +} +void API_ENTRY(glGetProgramiv)(GLuint program, GLenum pname, GLint *params) { + CALL_GL_API(glGetProgramiv, program, pname, params); +} +void API_ENTRY(glGetProgramInfoLog)(GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog) { + CALL_GL_API(glGetProgramInfoLog, program, bufSize, length, infoLog); +} +void API_ENTRY(glGetRenderbufferParameteriv)(GLenum target, GLenum pname, GLint *params) { + CALL_GL_API(glGetRenderbufferParameteriv, target, pname, params); +} +void API_ENTRY(glGetShaderiv)(GLuint shader, GLenum pname, GLint *params) { + CALL_GL_API(glGetShaderiv, shader, pname, params); +} +void API_ENTRY(glGetShaderInfoLog)(GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog) { + CALL_GL_API(glGetShaderInfoLog, shader, bufSize, length, infoLog); +} +void API_ENTRY(glGetShaderPrecisionFormat)(GLenum shadertype, GLenum precisiontype, GLint *range, GLint *precision) { + CALL_GL_API(glGetShaderPrecisionFormat, shadertype, precisiontype, range, precision); +} +void API_ENTRY(glGetShaderSource)(GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *source) { + CALL_GL_API(glGetShaderSource, shader, bufSize, length, source); +} +const GLubyte * API_ENTRY(glGetString)(GLenum name) { + CALL_GL_API_RETURN(glGetString, name); +} +void API_ENTRY(glGetTexParameterfv)(GLenum target, GLenum pname, GLfloat *params) { + CALL_GL_API(glGetTexParameterfv, target, pname, params); +} +void API_ENTRY(glGetTexParameteriv)(GLenum target, GLenum pname, GLint *params) { + CALL_GL_API(glGetTexParameteriv, target, pname, params); +} +void API_ENTRY(glGetUniformfv)(GLuint program, GLint location, GLfloat *params) { + CALL_GL_API(glGetUniformfv, program, location, params); +} +void API_ENTRY(glGetUniformiv)(GLuint program, GLint location, GLint *params) { + CALL_GL_API(glGetUniformiv, program, location, params); +} +GLint API_ENTRY(glGetUniformLocation)(GLuint program, const GLchar *name) { + CALL_GL_API_RETURN(glGetUniformLocation, program, name); +} +void API_ENTRY(glGetVertexAttribfv)(GLuint index, GLenum pname, GLfloat *params) { + CALL_GL_API(glGetVertexAttribfv, index, pname, params); +} +void API_ENTRY(glGetVertexAttribiv)(GLuint index, GLenum pname, GLint *params) { + CALL_GL_API(glGetVertexAttribiv, index, pname, params); +} +void API_ENTRY(glGetVertexAttribPointerv)(GLuint index, GLenum pname, void **pointer) { + CALL_GL_API(glGetVertexAttribPointerv, index, pname, pointer); +} +void API_ENTRY(glHint)(GLenum target, GLenum mode) { + CALL_GL_API(glHint, target, mode); +} +GLboolean API_ENTRY(glIsBuffer)(GLuint buffer) { + CALL_GL_API_RETURN(glIsBuffer, buffer); +} +GLboolean API_ENTRY(glIsEnabled)(GLenum cap) { + CALL_GL_API_RETURN(glIsEnabled, cap); +} +GLboolean API_ENTRY(glIsFramebuffer)(GLuint framebuffer) { + CALL_GL_API_RETURN(glIsFramebuffer, framebuffer); +} +GLboolean API_ENTRY(glIsProgram)(GLuint program) { + CALL_GL_API_RETURN(glIsProgram, program); +} +GLboolean API_ENTRY(glIsRenderbuffer)(GLuint renderbuffer) { + CALL_GL_API_RETURN(glIsRenderbuffer, renderbuffer); +} +GLboolean API_ENTRY(glIsShader)(GLuint shader) { + CALL_GL_API_RETURN(glIsShader, shader); +} +GLboolean API_ENTRY(glIsTexture)(GLuint texture) { + CALL_GL_API_RETURN(glIsTexture, texture); +} +void API_ENTRY(glLineWidth)(GLfloat width) { + CALL_GL_API(glLineWidth, width); +} +void API_ENTRY(glLinkProgram)(GLuint program) { + CALL_GL_API(glLinkProgram, program); +} +void API_ENTRY(glPixelStorei)(GLenum pname, GLint param) { + CALL_GL_API(glPixelStorei, pname, param); +} +void API_ENTRY(glPolygonOffset)(GLfloat factor, GLfloat units) { + CALL_GL_API(glPolygonOffset, factor, units); +} +void API_ENTRY(glReadPixels)(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void *pixels) { + CALL_GL_API(glReadPixels, x, y, width, height, format, type, pixels); +} +void API_ENTRY(glReleaseShaderCompiler)(void) { + CALL_GL_API(glReleaseShaderCompiler); +} +void API_ENTRY(glRenderbufferStorage)(GLenum target, GLenum internalformat, GLsizei width, GLsizei height) { + CALL_GL_API(glRenderbufferStorage, target, internalformat, width, height); +} +void API_ENTRY(glSampleCoverage)(GLfloat value, GLboolean invert) { + CALL_GL_API(glSampleCoverage, value, invert); +} +void API_ENTRY(glScissor)(GLint x, GLint y, GLsizei width, GLsizei height) { + CALL_GL_API(glScissor, x, y, width, height); +} +void API_ENTRY(glShaderBinary)(GLsizei count, const GLuint *shaders, GLenum binaryformat, const void *binary, GLsizei length) { + CALL_GL_API(glShaderBinary, count, shaders, binaryformat, binary, length); +} +void API_ENTRY(glShaderSource)(GLuint shader, GLsizei count, const GLchar *const*string, const GLint *length) { + CALL_GL_API(glShaderSource, shader, count, string, length); +} +void API_ENTRY(glStencilFunc)(GLenum func, GLint ref, GLuint mask) { + CALL_GL_API(glStencilFunc, func, ref, mask); +} +void API_ENTRY(glStencilFuncSeparate)(GLenum face, GLenum func, GLint ref, GLuint mask) { + CALL_GL_API(glStencilFuncSeparate, face, func, ref, mask); +} +void API_ENTRY(glStencilMask)(GLuint mask) { + CALL_GL_API(glStencilMask, mask); +} +void API_ENTRY(glStencilMaskSeparate)(GLenum face, GLuint mask) { + CALL_GL_API(glStencilMaskSeparate, face, mask); +} +void API_ENTRY(glStencilOp)(GLenum fail, GLenum zfail, GLenum zpass) { + CALL_GL_API(glStencilOp, fail, zfail, zpass); +} +void API_ENTRY(glStencilOpSeparate)(GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass) { + CALL_GL_API(glStencilOpSeparate, face, sfail, dpfail, dppass); +} +void API_ENTRY(glTexImage2D)(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels) { + CALL_GL_API(glTexImage2D, target, level, internalformat, width, height, border, format, type, pixels); +} +void API_ENTRY(glTexParameterf)(GLenum target, GLenum pname, GLfloat param) { + CALL_GL_API(glTexParameterf, target, pname, param); +} +void API_ENTRY(glTexParameterfv)(GLenum target, GLenum pname, const GLfloat *params) { + CALL_GL_API(glTexParameterfv, target, pname, params); +} +void API_ENTRY(glTexParameteri)(GLenum target, GLenum pname, GLint param) { + CALL_GL_API(glTexParameteri, target, pname, param); +} +void API_ENTRY(glTexParameteriv)(GLenum target, GLenum pname, const GLint *params) { + CALL_GL_API(glTexParameteriv, target, pname, params); +} +void API_ENTRY(glTexSubImage2D)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels) { + CALL_GL_API(glTexSubImage2D, target, level, xoffset, yoffset, width, height, format, type, pixels); +} +void API_ENTRY(glUniform1f)(GLint location, GLfloat v0) { + CALL_GL_API(glUniform1f, location, v0); +} +void API_ENTRY(glUniform1fv)(GLint location, GLsizei count, const GLfloat *value) { + CALL_GL_API(glUniform1fv, location, count, value); +} +void API_ENTRY(glUniform1i)(GLint location, GLint v0) { + CALL_GL_API(glUniform1i, location, v0); +} +void API_ENTRY(glUniform1iv)(GLint location, GLsizei count, const GLint *value) { + CALL_GL_API(glUniform1iv, location, count, value); +} +void API_ENTRY(glUniform2f)(GLint location, GLfloat v0, GLfloat v1) { + CALL_GL_API(glUniform2f, location, v0, v1); +} +void API_ENTRY(glUniform2fv)(GLint location, GLsizei count, const GLfloat *value) { + CALL_GL_API(glUniform2fv, location, count, value); +} +void API_ENTRY(glUniform2i)(GLint location, GLint v0, GLint v1) { + CALL_GL_API(glUniform2i, location, v0, v1); +} +void API_ENTRY(glUniform2iv)(GLint location, GLsizei count, const GLint *value) { + CALL_GL_API(glUniform2iv, location, count, value); +} +void API_ENTRY(glUniform3f)(GLint location, GLfloat v0, GLfloat v1, GLfloat v2) { + CALL_GL_API(glUniform3f, location, v0, v1, v2); +} +void API_ENTRY(glUniform3fv)(GLint location, GLsizei count, const GLfloat *value) { + CALL_GL_API(glUniform3fv, location, count, value); +} +void API_ENTRY(glUniform3i)(GLint location, GLint v0, GLint v1, GLint v2) { + CALL_GL_API(glUniform3i, location, v0, v1, v2); +} +void API_ENTRY(glUniform3iv)(GLint location, GLsizei count, const GLint *value) { + CALL_GL_API(glUniform3iv, location, count, value); +} +void API_ENTRY(glUniform4f)(GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3) { + CALL_GL_API(glUniform4f, location, v0, v1, v2, v3); +} +void API_ENTRY(glUniform4fv)(GLint location, GLsizei count, const GLfloat *value) { + CALL_GL_API(glUniform4fv, location, count, value); +} +void API_ENTRY(glUniform4i)(GLint location, GLint v0, GLint v1, GLint v2, GLint v3) { + CALL_GL_API(glUniform4i, location, v0, v1, v2, v3); +} +void API_ENTRY(glUniform4iv)(GLint location, GLsizei count, const GLint *value) { + CALL_GL_API(glUniform4iv, location, count, value); +} +void API_ENTRY(glUniformMatrix2fv)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) { + CALL_GL_API(glUniformMatrix2fv, location, count, transpose, value); +} +void API_ENTRY(glUniformMatrix3fv)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) { + CALL_GL_API(glUniformMatrix3fv, location, count, transpose, value); +} +void API_ENTRY(glUniformMatrix4fv)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) { + CALL_GL_API(glUniformMatrix4fv, location, count, transpose, value); +} +void API_ENTRY(glUseProgram)(GLuint program) { + CALL_GL_API(glUseProgram, program); +} +void API_ENTRY(glValidateProgram)(GLuint program) { + CALL_GL_API(glValidateProgram, program); +} +void API_ENTRY(glVertexAttrib1f)(GLuint index, GLfloat x) { + CALL_GL_API(glVertexAttrib1f, index, x); +} +void API_ENTRY(glVertexAttrib1fv)(GLuint index, const GLfloat *v) { + CALL_GL_API(glVertexAttrib1fv, index, v); +} +void API_ENTRY(glVertexAttrib2f)(GLuint index, GLfloat x, GLfloat y) { + CALL_GL_API(glVertexAttrib2f, index, x, y); +} +void API_ENTRY(glVertexAttrib2fv)(GLuint index, const GLfloat *v) { + CALL_GL_API(glVertexAttrib2fv, index, v); +} +void API_ENTRY(glVertexAttrib3f)(GLuint index, GLfloat x, GLfloat y, GLfloat z) { + CALL_GL_API(glVertexAttrib3f, index, x, y, z); +} +void API_ENTRY(glVertexAttrib3fv)(GLuint index, const GLfloat *v) { + CALL_GL_API(glVertexAttrib3fv, index, v); +} +void API_ENTRY(glVertexAttrib4f)(GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w) { + CALL_GL_API(glVertexAttrib4f, index, x, y, z, w); +} +void API_ENTRY(glVertexAttrib4fv)(GLuint index, const GLfloat *v) { + CALL_GL_API(glVertexAttrib4fv, index, v); +} +void API_ENTRY(glVertexAttribPointer)(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer) { + CALL_GL_API(glVertexAttribPointer, index, size, type, normalized, stride, pointer); +} +void API_ENTRY(glViewport)(GLint x, GLint y, GLsizei width, GLsizei height) { + CALL_GL_API(glViewport, x, y, width, height); +} +void API_ENTRY(glReadBuffer)(GLenum src) { + CALL_GL_API(glReadBuffer, src); +} +void API_ENTRY(glDrawRangeElements)(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices) { + CALL_GL_API(glDrawRangeElements, mode, start, end, count, type, indices); +} +void API_ENTRY(glTexImage3D)(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels) { + CALL_GL_API(glTexImage3D, target, level, internalformat, width, height, depth, border, format, type, pixels); +} +void API_ENTRY(glTexSubImage3D)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels) { + CALL_GL_API(glTexSubImage3D, target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, pixels); +} +void API_ENTRY(glCopyTexSubImage3D)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height) { + CALL_GL_API(glCopyTexSubImage3D, target, level, xoffset, yoffset, zoffset, x, y, width, height); +} +void API_ENTRY(glCompressedTexImage3D)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *data) { + CALL_GL_API(glCompressedTexImage3D, target, level, internalformat, width, height, depth, border, imageSize, data); +} +void API_ENTRY(glCompressedTexSubImage3D)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *data) { + CALL_GL_API(glCompressedTexSubImage3D, target, level, xoffset, yoffset, zoffset, width, height, depth, format, imageSize, data); +} +void API_ENTRY(glGenQueries)(GLsizei n, GLuint *ids) { + CALL_GL_API(glGenQueries, n, ids); +} +void API_ENTRY(glDeleteQueries)(GLsizei n, const GLuint *ids) { + CALL_GL_API(glDeleteQueries, n, ids); +} +GLboolean API_ENTRY(glIsQuery)(GLuint id) { + CALL_GL_API_RETURN(glIsQuery, id); +} +void API_ENTRY(glBeginQuery)(GLenum target, GLuint id) { + CALL_GL_API(glBeginQuery, target, id); +} +void API_ENTRY(glEndQuery)(GLenum target) { + CALL_GL_API(glEndQuery, target); +} +void API_ENTRY(glGetQueryiv)(GLenum target, GLenum pname, GLint *params) { + CALL_GL_API(glGetQueryiv, target, pname, params); +} +void API_ENTRY(glGetQueryObjectuiv)(GLuint id, GLenum pname, GLuint *params) { + CALL_GL_API(glGetQueryObjectuiv, id, pname, params); +} +GLboolean API_ENTRY(glUnmapBuffer)(GLenum target) { + CALL_GL_API_RETURN(glUnmapBuffer, target); +} +void API_ENTRY(glGetBufferPointerv)(GLenum target, GLenum pname, void **params) { + CALL_GL_API(glGetBufferPointerv, target, pname, params); +} +void API_ENTRY(glDrawBuffers)(GLsizei n, const GLenum *bufs) { + CALL_GL_API(glDrawBuffers, n, bufs); +} +void API_ENTRY(glUniformMatrix2x3fv)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) { + CALL_GL_API(glUniformMatrix2x3fv, location, count, transpose, value); +} +void API_ENTRY(glUniformMatrix3x2fv)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) { + CALL_GL_API(glUniformMatrix3x2fv, location, count, transpose, value); +} +void API_ENTRY(glUniformMatrix2x4fv)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) { + CALL_GL_API(glUniformMatrix2x4fv, location, count, transpose, value); +} +void API_ENTRY(glUniformMatrix4x2fv)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) { + CALL_GL_API(glUniformMatrix4x2fv, location, count, transpose, value); +} +void API_ENTRY(glUniformMatrix3x4fv)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) { + CALL_GL_API(glUniformMatrix3x4fv, location, count, transpose, value); +} +void API_ENTRY(glUniformMatrix4x3fv)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) { + CALL_GL_API(glUniformMatrix4x3fv, location, count, transpose, value); +} +void API_ENTRY(glBlitFramebuffer)(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter) { + CALL_GL_API(glBlitFramebuffer, srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter); +} +void API_ENTRY(glRenderbufferStorageMultisample)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height) { + CALL_GL_API(glRenderbufferStorageMultisample, target, samples, internalformat, width, height); +} +void API_ENTRY(glFramebufferTextureLayer)(GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer) { + CALL_GL_API(glFramebufferTextureLayer, target, attachment, texture, level, layer); +} +void * API_ENTRY(glMapBufferRange)(GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access) { + CALL_GL_API_RETURN(glMapBufferRange, target, offset, length, access); +} +void API_ENTRY(glFlushMappedBufferRange)(GLenum target, GLintptr offset, GLsizeiptr length) { + CALL_GL_API(glFlushMappedBufferRange, target, offset, length); +} +void API_ENTRY(glBindVertexArray)(GLuint array) { + CALL_GL_API(glBindVertexArray, array); +} +void API_ENTRY(glDeleteVertexArrays)(GLsizei n, const GLuint *arrays) { + CALL_GL_API(glDeleteVertexArrays, n, arrays); +} +void API_ENTRY(glGenVertexArrays)(GLsizei n, GLuint *arrays) { + CALL_GL_API(glGenVertexArrays, n, arrays); +} +GLboolean API_ENTRY(glIsVertexArray)(GLuint array) { + CALL_GL_API_RETURN(glIsVertexArray, array); +} +void API_ENTRY(glGetIntegeri_v)(GLenum target, GLuint index, GLint *data) { + CALL_GL_API(glGetIntegeri_v, target, index, data); +} +void API_ENTRY(glBeginTransformFeedback)(GLenum primitiveMode) { + CALL_GL_API(glBeginTransformFeedback, primitiveMode); +} +void API_ENTRY(glEndTransformFeedback)(void) { + CALL_GL_API(glEndTransformFeedback); +} +void API_ENTRY(glBindBufferRange)(GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size) { + CALL_GL_API(glBindBufferRange, target, index, buffer, offset, size); +} +void API_ENTRY(glBindBufferBase)(GLenum target, GLuint index, GLuint buffer) { + CALL_GL_API(glBindBufferBase, target, index, buffer); +} +void API_ENTRY(glTransformFeedbackVaryings)(GLuint program, GLsizei count, const GLchar *const*varyings, GLenum bufferMode) { + CALL_GL_API(glTransformFeedbackVaryings, program, count, varyings, bufferMode); +} +void API_ENTRY(glGetTransformFeedbackVarying)(GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLsizei *size, GLenum *type, GLchar *name) { + CALL_GL_API(glGetTransformFeedbackVarying, program, index, bufSize, length, size, type, name); +} +void API_ENTRY(glVertexAttribIPointer)(GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer) { + CALL_GL_API(glVertexAttribIPointer, index, size, type, stride, pointer); +} +void API_ENTRY(glGetVertexAttribIiv)(GLuint index, GLenum pname, GLint *params) { + CALL_GL_API(glGetVertexAttribIiv, index, pname, params); +} +void API_ENTRY(glGetVertexAttribIuiv)(GLuint index, GLenum pname, GLuint *params) { + CALL_GL_API(glGetVertexAttribIuiv, index, pname, params); +} +void API_ENTRY(glVertexAttribI4i)(GLuint index, GLint x, GLint y, GLint z, GLint w) { + CALL_GL_API(glVertexAttribI4i, index, x, y, z, w); +} +void API_ENTRY(glVertexAttribI4ui)(GLuint index, GLuint x, GLuint y, GLuint z, GLuint w) { + CALL_GL_API(glVertexAttribI4ui, index, x, y, z, w); +} +void API_ENTRY(glVertexAttribI4iv)(GLuint index, const GLint *v) { + CALL_GL_API(glVertexAttribI4iv, index, v); +} +void API_ENTRY(glVertexAttribI4uiv)(GLuint index, const GLuint *v) { + CALL_GL_API(glVertexAttribI4uiv, index, v); +} +void API_ENTRY(glGetUniformuiv)(GLuint program, GLint location, GLuint *params) { + CALL_GL_API(glGetUniformuiv, program, location, params); +} +GLint API_ENTRY(glGetFragDataLocation)(GLuint program, const GLchar *name) { + CALL_GL_API_RETURN(glGetFragDataLocation, program, name); +} +void API_ENTRY(glUniform1ui)(GLint location, GLuint v0) { + CALL_GL_API(glUniform1ui, location, v0); +} +void API_ENTRY(glUniform2ui)(GLint location, GLuint v0, GLuint v1) { + CALL_GL_API(glUniform2ui, location, v0, v1); +} +void API_ENTRY(glUniform3ui)(GLint location, GLuint v0, GLuint v1, GLuint v2) { + CALL_GL_API(glUniform3ui, location, v0, v1, v2); +} +void API_ENTRY(glUniform4ui)(GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3) { + CALL_GL_API(glUniform4ui, location, v0, v1, v2, v3); +} +void API_ENTRY(glUniform1uiv)(GLint location, GLsizei count, const GLuint *value) { + CALL_GL_API(glUniform1uiv, location, count, value); +} +void API_ENTRY(glUniform2uiv)(GLint location, GLsizei count, const GLuint *value) { + CALL_GL_API(glUniform2uiv, location, count, value); +} +void API_ENTRY(glUniform3uiv)(GLint location, GLsizei count, const GLuint *value) { + CALL_GL_API(glUniform3uiv, location, count, value); +} +void API_ENTRY(glUniform4uiv)(GLint location, GLsizei count, const GLuint *value) { + CALL_GL_API(glUniform4uiv, location, count, value); +} +void API_ENTRY(glClearBufferiv)(GLenum buffer, GLint drawbuffer, const GLint *value) { + CALL_GL_API(glClearBufferiv, buffer, drawbuffer, value); +} +void API_ENTRY(glClearBufferuiv)(GLenum buffer, GLint drawbuffer, const GLuint *value) { + CALL_GL_API(glClearBufferuiv, buffer, drawbuffer, value); +} +void API_ENTRY(glClearBufferfv)(GLenum buffer, GLint drawbuffer, const GLfloat *value) { + CALL_GL_API(glClearBufferfv, buffer, drawbuffer, value); +} +void API_ENTRY(glClearBufferfi)(GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil) { + CALL_GL_API(glClearBufferfi, buffer, drawbuffer, depth, stencil); +} +const GLubyte * API_ENTRY(glGetStringi)(GLenum name, GLuint index) { + CALL_GL_API_RETURN(glGetStringi, name, index); +} +void API_ENTRY(glCopyBufferSubData)(GLenum readTarget, GLenum writeTarget, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size) { + CALL_GL_API(glCopyBufferSubData, readTarget, writeTarget, readOffset, writeOffset, size); +} +void API_ENTRY(glGetUniformIndices)(GLuint program, GLsizei uniformCount, const GLchar *const*uniformNames, GLuint *uniformIndices) { + CALL_GL_API(glGetUniformIndices, program, uniformCount, uniformNames, uniformIndices); +} +void API_ENTRY(glGetActiveUniformsiv)(GLuint program, GLsizei uniformCount, const GLuint *uniformIndices, GLenum pname, GLint *params) { + CALL_GL_API(glGetActiveUniformsiv, program, uniformCount, uniformIndices, pname, params); +} +GLuint API_ENTRY(glGetUniformBlockIndex)(GLuint program, const GLchar *uniformBlockName) { + CALL_GL_API_RETURN(glGetUniformBlockIndex, program, uniformBlockName); +} +void API_ENTRY(glGetActiveUniformBlockiv)(GLuint program, GLuint uniformBlockIndex, GLenum pname, GLint *params) { + CALL_GL_API(glGetActiveUniformBlockiv, program, uniformBlockIndex, pname, params); +} +void API_ENTRY(glGetActiveUniformBlockName)(GLuint program, GLuint uniformBlockIndex, GLsizei bufSize, GLsizei *length, GLchar *uniformBlockName) { + CALL_GL_API(glGetActiveUniformBlockName, program, uniformBlockIndex, bufSize, length, uniformBlockName); +} +void API_ENTRY(glUniformBlockBinding)(GLuint program, GLuint uniformBlockIndex, GLuint uniformBlockBinding) { + CALL_GL_API(glUniformBlockBinding, program, uniformBlockIndex, uniformBlockBinding); +} +void API_ENTRY(glDrawArraysInstanced)(GLenum mode, GLint first, GLsizei count, GLsizei instancecount) { + CALL_GL_API(glDrawArraysInstanced, mode, first, count, instancecount); +} +void API_ENTRY(glDrawElementsInstanced)(GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount) { + CALL_GL_API(glDrawElementsInstanced, mode, count, type, indices, instancecount); +} +GLsync API_ENTRY(glFenceSync)(GLenum condition, GLbitfield flags) { + CALL_GL_API_RETURN(glFenceSync, condition, flags); +} +GLboolean API_ENTRY(glIsSync)(GLsync sync) { + CALL_GL_API_RETURN(glIsSync, sync); +} +void API_ENTRY(glDeleteSync)(GLsync sync) { + CALL_GL_API(glDeleteSync, sync); +} +GLenum API_ENTRY(glClientWaitSync)(GLsync sync, GLbitfield flags, GLuint64 timeout) { + CALL_GL_API_RETURN(glClientWaitSync, sync, flags, timeout); +} +void API_ENTRY(glWaitSync)(GLsync sync, GLbitfield flags, GLuint64 timeout) { + CALL_GL_API(glWaitSync, sync, flags, timeout); +} +void API_ENTRY(glGetInteger64v)(GLenum pname, GLint64 *data) { + CALL_GL_API(glGetInteger64v, pname, data); +} +void API_ENTRY(glGetSynciv)(GLsync sync, GLenum pname, GLsizei bufSize, GLsizei *length, GLint *values) { + CALL_GL_API(glGetSynciv, sync, pname, bufSize, length, values); +} +void API_ENTRY(glGetInteger64i_v)(GLenum target, GLuint index, GLint64 *data) { + CALL_GL_API(glGetInteger64i_v, target, index, data); +} +void API_ENTRY(glGetBufferParameteri64v)(GLenum target, GLenum pname, GLint64 *params) { + CALL_GL_API(glGetBufferParameteri64v, target, pname, params); +} +void API_ENTRY(glGenSamplers)(GLsizei count, GLuint *samplers) { + CALL_GL_API(glGenSamplers, count, samplers); +} +void API_ENTRY(glDeleteSamplers)(GLsizei count, const GLuint *samplers) { + CALL_GL_API(glDeleteSamplers, count, samplers); +} +GLboolean API_ENTRY(glIsSampler)(GLuint sampler) { + CALL_GL_API_RETURN(glIsSampler, sampler); +} +void API_ENTRY(glBindSampler)(GLuint unit, GLuint sampler) { + CALL_GL_API(glBindSampler, unit, sampler); +} +void API_ENTRY(glSamplerParameteri)(GLuint sampler, GLenum pname, GLint param) { + CALL_GL_API(glSamplerParameteri, sampler, pname, param); +} +void API_ENTRY(glSamplerParameteriv)(GLuint sampler, GLenum pname, const GLint *param) { + CALL_GL_API(glSamplerParameteriv, sampler, pname, param); +} +void API_ENTRY(glSamplerParameterf)(GLuint sampler, GLenum pname, GLfloat param) { + CALL_GL_API(glSamplerParameterf, sampler, pname, param); +} +void API_ENTRY(glSamplerParameterfv)(GLuint sampler, GLenum pname, const GLfloat *param) { + CALL_GL_API(glSamplerParameterfv, sampler, pname, param); +} +void API_ENTRY(glGetSamplerParameteriv)(GLuint sampler, GLenum pname, GLint *params) { + CALL_GL_API(glGetSamplerParameteriv, sampler, pname, params); +} +void API_ENTRY(glGetSamplerParameterfv)(GLuint sampler, GLenum pname, GLfloat *params) { + CALL_GL_API(glGetSamplerParameterfv, sampler, pname, params); +} +void API_ENTRY(glVertexAttribDivisor)(GLuint index, GLuint divisor) { + CALL_GL_API(glVertexAttribDivisor, index, divisor); +} +void API_ENTRY(glBindTransformFeedback)(GLenum target, GLuint id) { + CALL_GL_API(glBindTransformFeedback, target, id); +} +void API_ENTRY(glDeleteTransformFeedbacks)(GLsizei n, const GLuint *ids) { + CALL_GL_API(glDeleteTransformFeedbacks, n, ids); +} +void API_ENTRY(glGenTransformFeedbacks)(GLsizei n, GLuint *ids) { + CALL_GL_API(glGenTransformFeedbacks, n, ids); +} +GLboolean API_ENTRY(glIsTransformFeedback)(GLuint id) { + CALL_GL_API_RETURN(glIsTransformFeedback, id); +} +void API_ENTRY(glPauseTransformFeedback)(void) { + CALL_GL_API(glPauseTransformFeedback); +} +void API_ENTRY(glResumeTransformFeedback)(void) { + CALL_GL_API(glResumeTransformFeedback); +} +void API_ENTRY(glGetProgramBinary)(GLuint program, GLsizei bufSize, GLsizei *length, GLenum *binaryFormat, void *binary) { + CALL_GL_API(glGetProgramBinary, program, bufSize, length, binaryFormat, binary); +} +void API_ENTRY(glProgramBinary)(GLuint program, GLenum binaryFormat, const void *binary, GLsizei length) { + CALL_GL_API(glProgramBinary, program, binaryFormat, binary, length); +} +void API_ENTRY(glProgramParameteri)(GLuint program, GLenum pname, GLint value) { + CALL_GL_API(glProgramParameteri, program, pname, value); +} +void API_ENTRY(glInvalidateFramebuffer)(GLenum target, GLsizei numAttachments, const GLenum *attachments) { + CALL_GL_API(glInvalidateFramebuffer, target, numAttachments, attachments); +} +void API_ENTRY(glInvalidateSubFramebuffer)(GLenum target, GLsizei numAttachments, const GLenum *attachments, GLint x, GLint y, GLsizei width, GLsizei height) { + CALL_GL_API(glInvalidateSubFramebuffer, target, numAttachments, attachments, x, y, width, height); +} +void API_ENTRY(glTexStorage2D)(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height) { + CALL_GL_API(glTexStorage2D, target, levels, internalformat, width, height); +} +void API_ENTRY(glTexStorage3D)(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth) { + CALL_GL_API(glTexStorage3D, target, levels, internalformat, width, height, depth); +} +void API_ENTRY(glGetInternalformativ)(GLenum target, GLenum internalformat, GLenum pname, GLsizei bufSize, GLint *params) { + CALL_GL_API(glGetInternalformativ, target, internalformat, pname, bufSize, params); +} +void API_ENTRY(glDispatchCompute)(GLuint num_groups_x, GLuint num_groups_y, GLuint num_groups_z) { + CALL_GL_API(glDispatchCompute, num_groups_x, num_groups_y, num_groups_z); +} +void API_ENTRY(glDispatchComputeIndirect)(GLintptr indirect) { + CALL_GL_API(glDispatchComputeIndirect, indirect); +} +void API_ENTRY(glDrawArraysIndirect)(GLenum mode, const void *indirect) { + CALL_GL_API(glDrawArraysIndirect, mode, indirect); +} +void API_ENTRY(glDrawElementsIndirect)(GLenum mode, GLenum type, const void *indirect) { + CALL_GL_API(glDrawElementsIndirect, mode, type, indirect); +} +void API_ENTRY(glFramebufferParameteri)(GLenum target, GLenum pname, GLint param) { + CALL_GL_API(glFramebufferParameteri, target, pname, param); +} +void API_ENTRY(glGetFramebufferParameteriv)(GLenum target, GLenum pname, GLint *params) { + CALL_GL_API(glGetFramebufferParameteriv, target, pname, params); +} +void API_ENTRY(glGetProgramInterfaceiv)(GLuint program, GLenum programInterface, GLenum pname, GLint *params) { + CALL_GL_API(glGetProgramInterfaceiv, program, programInterface, pname, params); +} +GLuint API_ENTRY(glGetProgramResourceIndex)(GLuint program, GLenum programInterface, const GLchar *name) { + CALL_GL_API_RETURN(glGetProgramResourceIndex, program, programInterface, name); +} +void API_ENTRY(glGetProgramResourceName)(GLuint program, GLenum programInterface, GLuint index, GLsizei bufSize, GLsizei *length, GLchar *name) { + CALL_GL_API(glGetProgramResourceName, program, programInterface, index, bufSize, length, name); +} +void API_ENTRY(glGetProgramResourceiv)(GLuint program, GLenum programInterface, GLuint index, GLsizei propCount, const GLenum *props, GLsizei bufSize, GLsizei *length, GLint *params) { + CALL_GL_API(glGetProgramResourceiv, program, programInterface, index, propCount, props, bufSize, length, params); +} +GLint API_ENTRY(glGetProgramResourceLocation)(GLuint program, GLenum programInterface, const GLchar *name) { + CALL_GL_API_RETURN(glGetProgramResourceLocation, program, programInterface, name); +} +void API_ENTRY(glUseProgramStages)(GLuint pipeline, GLbitfield stages, GLuint program) { + CALL_GL_API(glUseProgramStages, pipeline, stages, program); +} +void API_ENTRY(glActiveShaderProgram)(GLuint pipeline, GLuint program) { + CALL_GL_API(glActiveShaderProgram, pipeline, program); +} +GLuint API_ENTRY(glCreateShaderProgramv)(GLenum type, GLsizei count, const GLchar *const*strings) { + CALL_GL_API_RETURN(glCreateShaderProgramv, type, count, strings); +} +void API_ENTRY(glBindProgramPipeline)(GLuint pipeline) { + CALL_GL_API(glBindProgramPipeline, pipeline); +} +void API_ENTRY(glDeleteProgramPipelines)(GLsizei n, const GLuint *pipelines) { + CALL_GL_API(glDeleteProgramPipelines, n, pipelines); +} +void API_ENTRY(glGenProgramPipelines)(GLsizei n, GLuint *pipelines) { + CALL_GL_API(glGenProgramPipelines, n, pipelines); +} +GLboolean API_ENTRY(glIsProgramPipeline)(GLuint pipeline) { + CALL_GL_API_RETURN(glIsProgramPipeline, pipeline); +} +void API_ENTRY(glGetProgramPipelineiv)(GLuint pipeline, GLenum pname, GLint *params) { + CALL_GL_API(glGetProgramPipelineiv, pipeline, pname, params); +} +void API_ENTRY(glProgramUniform1i)(GLuint program, GLint location, GLint v0) { + CALL_GL_API(glProgramUniform1i, program, location, v0); +} +void API_ENTRY(glProgramUniform2i)(GLuint program, GLint location, GLint v0, GLint v1) { + CALL_GL_API(glProgramUniform2i, program, location, v0, v1); +} +void API_ENTRY(glProgramUniform3i)(GLuint program, GLint location, GLint v0, GLint v1, GLint v2) { + CALL_GL_API(glProgramUniform3i, program, location, v0, v1, v2); +} +void API_ENTRY(glProgramUniform4i)(GLuint program, GLint location, GLint v0, GLint v1, GLint v2, GLint v3) { + CALL_GL_API(glProgramUniform4i, program, location, v0, v1, v2, v3); +} +void API_ENTRY(glProgramUniform1ui)(GLuint program, GLint location, GLuint v0) { + CALL_GL_API(glProgramUniform1ui, program, location, v0); +} +void API_ENTRY(glProgramUniform2ui)(GLuint program, GLint location, GLuint v0, GLuint v1) { + CALL_GL_API(glProgramUniform2ui, program, location, v0, v1); +} +void API_ENTRY(glProgramUniform3ui)(GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2) { + CALL_GL_API(glProgramUniform3ui, program, location, v0, v1, v2); +} +void API_ENTRY(glProgramUniform4ui)(GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3) { + CALL_GL_API(glProgramUniform4ui, program, location, v0, v1, v2, v3); +} +void API_ENTRY(glProgramUniform1f)(GLuint program, GLint location, GLfloat v0) { + CALL_GL_API(glProgramUniform1f, program, location, v0); +} +void API_ENTRY(glProgramUniform2f)(GLuint program, GLint location, GLfloat v0, GLfloat v1) { + CALL_GL_API(glProgramUniform2f, program, location, v0, v1); +} +void API_ENTRY(glProgramUniform3f)(GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2) { + CALL_GL_API(glProgramUniform3f, program, location, v0, v1, v2); +} +void API_ENTRY(glProgramUniform4f)(GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3) { + CALL_GL_API(glProgramUniform4f, program, location, v0, v1, v2, v3); +} +void API_ENTRY(glProgramUniform1iv)(GLuint program, GLint location, GLsizei count, const GLint *value) { + CALL_GL_API(glProgramUniform1iv, program, location, count, value); +} +void API_ENTRY(glProgramUniform2iv)(GLuint program, GLint location, GLsizei count, const GLint *value) { + CALL_GL_API(glProgramUniform2iv, program, location, count, value); +} +void API_ENTRY(glProgramUniform3iv)(GLuint program, GLint location, GLsizei count, const GLint *value) { + CALL_GL_API(glProgramUniform3iv, program, location, count, value); +} +void API_ENTRY(glProgramUniform4iv)(GLuint program, GLint location, GLsizei count, const GLint *value) { + CALL_GL_API(glProgramUniform4iv, program, location, count, value); +} +void API_ENTRY(glProgramUniform1uiv)(GLuint program, GLint location, GLsizei count, const GLuint *value) { + CALL_GL_API(glProgramUniform1uiv, program, location, count, value); +} +void API_ENTRY(glProgramUniform2uiv)(GLuint program, GLint location, GLsizei count, const GLuint *value) { + CALL_GL_API(glProgramUniform2uiv, program, location, count, value); +} +void API_ENTRY(glProgramUniform3uiv)(GLuint program, GLint location, GLsizei count, const GLuint *value) { + CALL_GL_API(glProgramUniform3uiv, program, location, count, value); +} +void API_ENTRY(glProgramUniform4uiv)(GLuint program, GLint location, GLsizei count, const GLuint *value) { + CALL_GL_API(glProgramUniform4uiv, program, location, count, value); +} +void API_ENTRY(glProgramUniform1fv)(GLuint program, GLint location, GLsizei count, const GLfloat *value) { + CALL_GL_API(glProgramUniform1fv, program, location, count, value); +} +void API_ENTRY(glProgramUniform2fv)(GLuint program, GLint location, GLsizei count, const GLfloat *value) { + CALL_GL_API(glProgramUniform2fv, program, location, count, value); +} +void API_ENTRY(glProgramUniform3fv)(GLuint program, GLint location, GLsizei count, const GLfloat *value) { + CALL_GL_API(glProgramUniform3fv, program, location, count, value); +} +void API_ENTRY(glProgramUniform4fv)(GLuint program, GLint location, GLsizei count, const GLfloat *value) { + CALL_GL_API(glProgramUniform4fv, program, location, count, value); +} +void API_ENTRY(glProgramUniformMatrix2fv)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) { + CALL_GL_API(glProgramUniformMatrix2fv, program, location, count, transpose, value); +} +void API_ENTRY(glProgramUniformMatrix3fv)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) { + CALL_GL_API(glProgramUniformMatrix3fv, program, location, count, transpose, value); +} +void API_ENTRY(glProgramUniformMatrix4fv)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) { + CALL_GL_API(glProgramUniformMatrix4fv, program, location, count, transpose, value); +} +void API_ENTRY(glProgramUniformMatrix2x3fv)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) { + CALL_GL_API(glProgramUniformMatrix2x3fv, program, location, count, transpose, value); +} +void API_ENTRY(glProgramUniformMatrix3x2fv)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) { + CALL_GL_API(glProgramUniformMatrix3x2fv, program, location, count, transpose, value); +} +void API_ENTRY(glProgramUniformMatrix2x4fv)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) { + CALL_GL_API(glProgramUniformMatrix2x4fv, program, location, count, transpose, value); +} +void API_ENTRY(glProgramUniformMatrix4x2fv)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) { + CALL_GL_API(glProgramUniformMatrix4x2fv, program, location, count, transpose, value); +} +void API_ENTRY(glProgramUniformMatrix3x4fv)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) { + CALL_GL_API(glProgramUniformMatrix3x4fv, program, location, count, transpose, value); +} +void API_ENTRY(glProgramUniformMatrix4x3fv)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) { + CALL_GL_API(glProgramUniformMatrix4x3fv, program, location, count, transpose, value); +} +void API_ENTRY(glValidateProgramPipeline)(GLuint pipeline) { + CALL_GL_API(glValidateProgramPipeline, pipeline); +} +void API_ENTRY(glGetProgramPipelineInfoLog)(GLuint pipeline, GLsizei bufSize, GLsizei *length, GLchar *infoLog) { + CALL_GL_API(glGetProgramPipelineInfoLog, pipeline, bufSize, length, infoLog); +} +void API_ENTRY(glBindImageTexture)(GLuint unit, GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum access, GLenum format) { + CALL_GL_API(glBindImageTexture, unit, texture, level, layered, layer, access, format); +} +void API_ENTRY(glGetBooleani_v)(GLenum target, GLuint index, GLboolean *data) { + CALL_GL_API(glGetBooleani_v, target, index, data); +} +void API_ENTRY(glMemoryBarrier)(GLbitfield barriers) { + CALL_GL_API(glMemoryBarrier, barriers); +} +void API_ENTRY(glMemoryBarrierByRegion)(GLbitfield barriers) { + CALL_GL_API(glMemoryBarrierByRegion, barriers); +} +void API_ENTRY(glTexStorage2DMultisample)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations) { + CALL_GL_API(glTexStorage2DMultisample, target, samples, internalformat, width, height, fixedsamplelocations); +} +void API_ENTRY(glGetMultisamplefv)(GLenum pname, GLuint index, GLfloat *val) { + CALL_GL_API(glGetMultisamplefv, pname, index, val); +} +void API_ENTRY(glSampleMaski)(GLuint maskNumber, GLbitfield mask) { + CALL_GL_API(glSampleMaski, maskNumber, mask); +} +void API_ENTRY(glGetTexLevelParameteriv)(GLenum target, GLint level, GLenum pname, GLint *params) { + CALL_GL_API(glGetTexLevelParameteriv, target, level, pname, params); +} +void API_ENTRY(glGetTexLevelParameterfv)(GLenum target, GLint level, GLenum pname, GLfloat *params) { + CALL_GL_API(glGetTexLevelParameterfv, target, level, pname, params); +} +void API_ENTRY(glBindVertexBuffer)(GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride) { + CALL_GL_API(glBindVertexBuffer, bindingindex, buffer, offset, stride); +} +void API_ENTRY(glVertexAttribFormat)(GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset) { + CALL_GL_API(glVertexAttribFormat, attribindex, size, type, normalized, relativeoffset); +} +void API_ENTRY(glVertexAttribIFormat)(GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset) { + CALL_GL_API(glVertexAttribIFormat, attribindex, size, type, relativeoffset); +} +void API_ENTRY(glVertexAttribBinding)(GLuint attribindex, GLuint bindingindex) { + CALL_GL_API(glVertexAttribBinding, attribindex, bindingindex); +} +void API_ENTRY(glVertexBindingDivisor)(GLuint bindingindex, GLuint divisor) { + CALL_GL_API(glVertexBindingDivisor, bindingindex, divisor); +} +void API_ENTRY(glBlendBarrier)(void) { + CALL_GL_API(glBlendBarrier); +} +void API_ENTRY(glCopyImageSubData)(GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth) { + CALL_GL_API(glCopyImageSubData, srcName, srcTarget, srcLevel, srcX, srcY, srcZ, dstName, dstTarget, dstLevel, dstX, dstY, dstZ, srcWidth, srcHeight, srcDepth); +} +void API_ENTRY(glDebugMessageControl)(GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled) { + CALL_GL_API(glDebugMessageControl, source, type, severity, count, ids, enabled); +} +void API_ENTRY(glDebugMessageInsert)(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *buf) { + CALL_GL_API(glDebugMessageInsert, source, type, id, severity, length, buf); +} +void API_ENTRY(glDebugMessageCallback)(GLDEBUGPROC callback, const void *userParam) { + CALL_GL_API(glDebugMessageCallback, callback, userParam); +} +GLuint API_ENTRY(glGetDebugMessageLog)(GLuint count, GLsizei bufSize, GLenum *sources, GLenum *types, GLuint *ids, GLenum *severities, GLsizei *lengths, GLchar *messageLog) { + CALL_GL_API_RETURN(glGetDebugMessageLog, count, bufSize, sources, types, ids, severities, lengths, messageLog); +} +void API_ENTRY(glPushDebugGroup)(GLenum source, GLuint id, GLsizei length, const GLchar *message) { + CALL_GL_API(glPushDebugGroup, source, id, length, message); +} +void API_ENTRY(glPopDebugGroup)(void) { + CALL_GL_API(glPopDebugGroup); +} +void API_ENTRY(glObjectLabel)(GLenum identifier, GLuint name, GLsizei length, const GLchar *label) { + CALL_GL_API(glObjectLabel, identifier, name, length, label); +} +void API_ENTRY(glGetObjectLabel)(GLenum identifier, GLuint name, GLsizei bufSize, GLsizei *length, GLchar *label) { + CALL_GL_API(glGetObjectLabel, identifier, name, bufSize, length, label); +} +void API_ENTRY(glObjectPtrLabel)(const void *ptr, GLsizei length, const GLchar *label) { + CALL_GL_API(glObjectPtrLabel, ptr, length, label); +} +void API_ENTRY(glGetObjectPtrLabel)(const void *ptr, GLsizei bufSize, GLsizei *length, GLchar *label) { + CALL_GL_API(glGetObjectPtrLabel, ptr, bufSize, length, label); +} +void API_ENTRY(glGetPointerv)(GLenum pname, void **params) { + CALL_GL_API(glGetPointerv, pname, params); +} +void API_ENTRY(glEnablei)(GLenum target, GLuint index) { + CALL_GL_API(glEnablei, target, index); +} +void API_ENTRY(glDisablei)(GLenum target, GLuint index) { + CALL_GL_API(glDisablei, target, index); +} +void API_ENTRY(glBlendEquationi)(GLuint buf, GLenum mode) { + CALL_GL_API(glBlendEquationi, buf, mode); +} +void API_ENTRY(glBlendEquationSeparatei)(GLuint buf, GLenum modeRGB, GLenum modeAlpha) { + CALL_GL_API(glBlendEquationSeparatei, buf, modeRGB, modeAlpha); +} +void API_ENTRY(glBlendFunci)(GLuint buf, GLenum src, GLenum dst) { + CALL_GL_API(glBlendFunci, buf, src, dst); +} +void API_ENTRY(glBlendFuncSeparatei)(GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha) { + CALL_GL_API(glBlendFuncSeparatei, buf, srcRGB, dstRGB, srcAlpha, dstAlpha); +} +void API_ENTRY(glColorMaski)(GLuint index, GLboolean r, GLboolean g, GLboolean b, GLboolean a) { + CALL_GL_API(glColorMaski, index, r, g, b, a); +} +GLboolean API_ENTRY(glIsEnabledi)(GLenum target, GLuint index) { + CALL_GL_API_RETURN(glIsEnabledi, target, index); +} +void API_ENTRY(glDrawElementsBaseVertex)(GLenum mode, GLsizei count, GLenum type, const void *indices, GLint basevertex) { + CALL_GL_API(glDrawElementsBaseVertex, mode, count, type, indices, basevertex); +} +void API_ENTRY(glDrawRangeElementsBaseVertex)(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices, GLint basevertex) { + CALL_GL_API(glDrawRangeElementsBaseVertex, mode, start, end, count, type, indices, basevertex); +} +void API_ENTRY(glDrawElementsInstancedBaseVertex)(GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex) { + CALL_GL_API(glDrawElementsInstancedBaseVertex, mode, count, type, indices, instancecount, basevertex); +} +void API_ENTRY(glFramebufferTexture)(GLenum target, GLenum attachment, GLuint texture, GLint level) { + CALL_GL_API(glFramebufferTexture, target, attachment, texture, level); +} +void API_ENTRY(glPrimitiveBoundingBox)(GLfloat minX, GLfloat minY, GLfloat minZ, GLfloat minW, GLfloat maxX, GLfloat maxY, GLfloat maxZ, GLfloat maxW) { + CALL_GL_API(glPrimitiveBoundingBox, minX, minY, minZ, minW, maxX, maxY, maxZ, maxW); +} +GLenum API_ENTRY(glGetGraphicsResetStatus)(void) { + CALL_GL_API_RETURN(glGetGraphicsResetStatus); +} +void API_ENTRY(glReadnPixels)(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, void *data) { + CALL_GL_API(glReadnPixels, x, y, width, height, format, type, bufSize, data); +} +void API_ENTRY(glGetnUniformfv)(GLuint program, GLint location, GLsizei bufSize, GLfloat *params) { + CALL_GL_API(glGetnUniformfv, program, location, bufSize, params); +} +void API_ENTRY(glGetnUniformiv)(GLuint program, GLint location, GLsizei bufSize, GLint *params) { + CALL_GL_API(glGetnUniformiv, program, location, bufSize, params); +} +void API_ENTRY(glGetnUniformuiv)(GLuint program, GLint location, GLsizei bufSize, GLuint *params) { + CALL_GL_API(glGetnUniformuiv, program, location, bufSize, params); +} +void API_ENTRY(glMinSampleShading)(GLfloat value) { + CALL_GL_API(glMinSampleShading, value); +} +void API_ENTRY(glPatchParameteri)(GLenum pname, GLint value) { + CALL_GL_API(glPatchParameteri, pname, value); +} +void API_ENTRY(glTexParameterIiv)(GLenum target, GLenum pname, const GLint *params) { + CALL_GL_API(glTexParameterIiv, target, pname, params); +} +void API_ENTRY(glTexParameterIuiv)(GLenum target, GLenum pname, const GLuint *params) { + CALL_GL_API(glTexParameterIuiv, target, pname, params); +} +void API_ENTRY(glGetTexParameterIiv)(GLenum target, GLenum pname, GLint *params) { + CALL_GL_API(glGetTexParameterIiv, target, pname, params); +} +void API_ENTRY(glGetTexParameterIuiv)(GLenum target, GLenum pname, GLuint *params) { + CALL_GL_API(glGetTexParameterIuiv, target, pname, params); +} +void API_ENTRY(glSamplerParameterIiv)(GLuint sampler, GLenum pname, const GLint *param) { + CALL_GL_API(glSamplerParameterIiv, sampler, pname, param); +} +void API_ENTRY(glSamplerParameterIuiv)(GLuint sampler, GLenum pname, const GLuint *param) { + CALL_GL_API(glSamplerParameterIuiv, sampler, pname, param); +} +void API_ENTRY(glGetSamplerParameterIiv)(GLuint sampler, GLenum pname, GLint *params) { + CALL_GL_API(glGetSamplerParameterIiv, sampler, pname, params); +} +void API_ENTRY(glGetSamplerParameterIuiv)(GLuint sampler, GLenum pname, GLuint *params) { + CALL_GL_API(glGetSamplerParameterIuiv, sampler, pname, params); +} +void API_ENTRY(glTexBuffer)(GLenum target, GLenum internalformat, GLuint buffer) { + CALL_GL_API(glTexBuffer, target, internalformat, buffer); +} +void API_ENTRY(glTexBufferRange)(GLenum target, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size) { + CALL_GL_API(glTexBufferRange, target, internalformat, buffer, offset, size); +} +void API_ENTRY(glTexStorage3DMultisample)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations) { + CALL_GL_API(glTexStorage3DMultisample, target, samples, internalformat, width, height, depth, fixedsamplelocations); +} +void API_ENTRY(glBlendBarrierKHR)(void) { + CALL_GL_API(glBlendBarrierKHR); +} +void API_ENTRY(glDebugMessageControlKHR)(GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled) { + CALL_GL_API(glDebugMessageControlKHR, source, type, severity, count, ids, enabled); +} +void API_ENTRY(glDebugMessageInsertKHR)(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *buf) { + CALL_GL_API(glDebugMessageInsertKHR, source, type, id, severity, length, buf); +} +void API_ENTRY(glDebugMessageCallbackKHR)(GLDEBUGPROCKHR callback, const void *userParam) { + CALL_GL_API(glDebugMessageCallbackKHR, callback, userParam); +} +GLuint API_ENTRY(glGetDebugMessageLogKHR)(GLuint count, GLsizei bufSize, GLenum *sources, GLenum *types, GLuint *ids, GLenum *severities, GLsizei *lengths, GLchar *messageLog) { + CALL_GL_API_RETURN(glGetDebugMessageLogKHR, count, bufSize, sources, types, ids, severities, lengths, messageLog); +} +void API_ENTRY(glPushDebugGroupKHR)(GLenum source, GLuint id, GLsizei length, const GLchar *message) { + CALL_GL_API(glPushDebugGroupKHR, source, id, length, message); +} +void API_ENTRY(glPopDebugGroupKHR)(void) { + CALL_GL_API(glPopDebugGroupKHR); +} +void API_ENTRY(glObjectLabelKHR)(GLenum identifier, GLuint name, GLsizei length, const GLchar *label) { + CALL_GL_API(glObjectLabelKHR, identifier, name, length, label); +} +void API_ENTRY(glGetObjectLabelKHR)(GLenum identifier, GLuint name, GLsizei bufSize, GLsizei *length, GLchar *label) { + CALL_GL_API(glGetObjectLabelKHR, identifier, name, bufSize, length, label); +} +void API_ENTRY(glObjectPtrLabelKHR)(const void *ptr, GLsizei length, const GLchar *label) { + CALL_GL_API(glObjectPtrLabelKHR, ptr, length, label); +} +void API_ENTRY(glGetObjectPtrLabelKHR)(const void *ptr, GLsizei bufSize, GLsizei *length, GLchar *label) { + CALL_GL_API(glGetObjectPtrLabelKHR, ptr, bufSize, length, label); +} +void API_ENTRY(glGetPointervKHR)(GLenum pname, void **params) { + CALL_GL_API(glGetPointervKHR, pname, params); +} +GLenum API_ENTRY(glGetGraphicsResetStatusKHR)(void) { + CALL_GL_API_RETURN(glGetGraphicsResetStatusKHR); +} +void API_ENTRY(glReadnPixelsKHR)(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, void *data) { + CALL_GL_API(glReadnPixelsKHR, x, y, width, height, format, type, bufSize, data); +} +void API_ENTRY(glGetnUniformfvKHR)(GLuint program, GLint location, GLsizei bufSize, GLfloat *params) { + CALL_GL_API(glGetnUniformfvKHR, program, location, bufSize, params); +} +void API_ENTRY(glGetnUniformivKHR)(GLuint program, GLint location, GLsizei bufSize, GLint *params) { + CALL_GL_API(glGetnUniformivKHR, program, location, bufSize, params); +} +void API_ENTRY(glGetnUniformuivKHR)(GLuint program, GLint location, GLsizei bufSize, GLuint *params) { + CALL_GL_API(glGetnUniformuivKHR, program, location, bufSize, params); +} +void API_ENTRY(glEGLImageTargetTexture2DOES)(GLenum target, GLeglImageOES image) { + CALL_GL_API(glEGLImageTargetTexture2DOES, target, image); +} +void API_ENTRY(glEGLImageTargetRenderbufferStorageOES)(GLenum target, GLeglImageOES image) { + CALL_GL_API(glEGLImageTargetRenderbufferStorageOES, target, image); +} +void API_ENTRY(glCopyImageSubDataOES)(GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth) { + CALL_GL_API(glCopyImageSubDataOES, srcName, srcTarget, srcLevel, srcX, srcY, srcZ, dstName, dstTarget, dstLevel, dstX, dstY, dstZ, srcWidth, srcHeight, srcDepth); +} +void API_ENTRY(glEnableiOES)(GLenum target, GLuint index) { + CALL_GL_API(glEnableiOES, target, index); +} +void API_ENTRY(glDisableiOES)(GLenum target, GLuint index) { + CALL_GL_API(glDisableiOES, target, index); +} +void API_ENTRY(glBlendEquationiOES)(GLuint buf, GLenum mode) { + CALL_GL_API(glBlendEquationiOES, buf, mode); +} +void API_ENTRY(glBlendEquationSeparateiOES)(GLuint buf, GLenum modeRGB, GLenum modeAlpha) { + CALL_GL_API(glBlendEquationSeparateiOES, buf, modeRGB, modeAlpha); +} +void API_ENTRY(glBlendFunciOES)(GLuint buf, GLenum src, GLenum dst) { + CALL_GL_API(glBlendFunciOES, buf, src, dst); +} +void API_ENTRY(glBlendFuncSeparateiOES)(GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha) { + CALL_GL_API(glBlendFuncSeparateiOES, buf, srcRGB, dstRGB, srcAlpha, dstAlpha); +} +void API_ENTRY(glColorMaskiOES)(GLuint index, GLboolean r, GLboolean g, GLboolean b, GLboolean a) { + CALL_GL_API(glColorMaskiOES, index, r, g, b, a); +} +GLboolean API_ENTRY(glIsEnablediOES)(GLenum target, GLuint index) { + CALL_GL_API_RETURN(glIsEnablediOES, target, index); +} +void API_ENTRY(glDrawElementsBaseVertexOES)(GLenum mode, GLsizei count, GLenum type, const void *indices, GLint basevertex) { + CALL_GL_API(glDrawElementsBaseVertexOES, mode, count, type, indices, basevertex); +} +void API_ENTRY(glDrawRangeElementsBaseVertexOES)(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices, GLint basevertex) { + CALL_GL_API(glDrawRangeElementsBaseVertexOES, mode, start, end, count, type, indices, basevertex); +} +void API_ENTRY(glDrawElementsInstancedBaseVertexOES)(GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex) { + CALL_GL_API(glDrawElementsInstancedBaseVertexOES, mode, count, type, indices, instancecount, basevertex); +} +void API_ENTRY(glMultiDrawElementsBaseVertexOES)(GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei primcount, const GLint *basevertex) { + CALL_GL_API(glMultiDrawElementsBaseVertexOES, mode, count, type, indices, primcount, basevertex); +} +void API_ENTRY(glFramebufferTextureOES)(GLenum target, GLenum attachment, GLuint texture, GLint level) { + CALL_GL_API(glFramebufferTextureOES, target, attachment, texture, level); +} +void API_ENTRY(glGetProgramBinaryOES)(GLuint program, GLsizei bufSize, GLsizei *length, GLenum *binaryFormat, void *binary) { + CALL_GL_API(glGetProgramBinaryOES, program, bufSize, length, binaryFormat, binary); +} +void API_ENTRY(glProgramBinaryOES)(GLuint program, GLenum binaryFormat, const void *binary, GLint length) { + CALL_GL_API(glProgramBinaryOES, program, binaryFormat, binary, length); +} +void * API_ENTRY(glMapBufferOES)(GLenum target, GLenum access) { + CALL_GL_API_RETURN(glMapBufferOES, target, access); +} +GLboolean API_ENTRY(glUnmapBufferOES)(GLenum target) { + CALL_GL_API_RETURN(glUnmapBufferOES, target); +} +void API_ENTRY(glGetBufferPointervOES)(GLenum target, GLenum pname, void **params) { + CALL_GL_API(glGetBufferPointervOES, target, pname, params); +} +void API_ENTRY(glPrimitiveBoundingBoxOES)(GLfloat minX, GLfloat minY, GLfloat minZ, GLfloat minW, GLfloat maxX, GLfloat maxY, GLfloat maxZ, GLfloat maxW) { + CALL_GL_API(glPrimitiveBoundingBoxOES, minX, minY, minZ, minW, maxX, maxY, maxZ, maxW); +} +void API_ENTRY(glMinSampleShadingOES)(GLfloat value) { + CALL_GL_API(glMinSampleShadingOES, value); +} +void API_ENTRY(glPatchParameteriOES)(GLenum pname, GLint value) { + CALL_GL_API(glPatchParameteriOES, pname, value); +} +void API_ENTRY(glTexImage3DOES)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels) { + CALL_GL_API(glTexImage3DOES, target, level, internalformat, width, height, depth, border, format, type, pixels); +} +void API_ENTRY(glTexSubImage3DOES)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels) { + CALL_GL_API(glTexSubImage3DOES, target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, pixels); +} +void API_ENTRY(glCopyTexSubImage3DOES)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height) { + CALL_GL_API(glCopyTexSubImage3DOES, target, level, xoffset, yoffset, zoffset, x, y, width, height); +} +void API_ENTRY(glCompressedTexImage3DOES)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *data) { + CALL_GL_API(glCompressedTexImage3DOES, target, level, internalformat, width, height, depth, border, imageSize, data); +} +void API_ENTRY(glCompressedTexSubImage3DOES)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *data) { + CALL_GL_API(glCompressedTexSubImage3DOES, target, level, xoffset, yoffset, zoffset, width, height, depth, format, imageSize, data); +} +void API_ENTRY(glFramebufferTexture3DOES)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset) { + CALL_GL_API(glFramebufferTexture3DOES, target, attachment, textarget, texture, level, zoffset); +} +void API_ENTRY(glTexParameterIivOES)(GLenum target, GLenum pname, const GLint *params) { + CALL_GL_API(glTexParameterIivOES, target, pname, params); +} +void API_ENTRY(glTexParameterIuivOES)(GLenum target, GLenum pname, const GLuint *params) { + CALL_GL_API(glTexParameterIuivOES, target, pname, params); +} +void API_ENTRY(glGetTexParameterIivOES)(GLenum target, GLenum pname, GLint *params) { + CALL_GL_API(glGetTexParameterIivOES, target, pname, params); +} +void API_ENTRY(glGetTexParameterIuivOES)(GLenum target, GLenum pname, GLuint *params) { + CALL_GL_API(glGetTexParameterIuivOES, target, pname, params); +} +void API_ENTRY(glSamplerParameterIivOES)(GLuint sampler, GLenum pname, const GLint *param) { + CALL_GL_API(glSamplerParameterIivOES, sampler, pname, param); +} +void API_ENTRY(glSamplerParameterIuivOES)(GLuint sampler, GLenum pname, const GLuint *param) { + CALL_GL_API(glSamplerParameterIuivOES, sampler, pname, param); +} +void API_ENTRY(glGetSamplerParameterIivOES)(GLuint sampler, GLenum pname, GLint *params) { + CALL_GL_API(glGetSamplerParameterIivOES, sampler, pname, params); +} +void API_ENTRY(glGetSamplerParameterIuivOES)(GLuint sampler, GLenum pname, GLuint *params) { + CALL_GL_API(glGetSamplerParameterIuivOES, sampler, pname, params); +} +void API_ENTRY(glTexBufferOES)(GLenum target, GLenum internalformat, GLuint buffer) { + CALL_GL_API(glTexBufferOES, target, internalformat, buffer); +} +void API_ENTRY(glTexBufferRangeOES)(GLenum target, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size) { + CALL_GL_API(glTexBufferRangeOES, target, internalformat, buffer, offset, size); +} +void API_ENTRY(glTexStorage3DMultisampleOES)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations) { + CALL_GL_API(glTexStorage3DMultisampleOES, target, samples, internalformat, width, height, depth, fixedsamplelocations); +} +void API_ENTRY(glTextureViewOES)(GLuint texture, GLenum target, GLuint origtexture, GLenum internalformat, GLuint minlevel, GLuint numlevels, GLuint minlayer, GLuint numlayers) { + CALL_GL_API(glTextureViewOES, texture, target, origtexture, internalformat, minlevel, numlevels, minlayer, numlayers); +} +void API_ENTRY(glBindVertexArrayOES)(GLuint array) { + CALL_GL_API(glBindVertexArrayOES, array); +} +void API_ENTRY(glDeleteVertexArraysOES)(GLsizei n, const GLuint *arrays) { + CALL_GL_API(glDeleteVertexArraysOES, n, arrays); +} +void API_ENTRY(glGenVertexArraysOES)(GLsizei n, GLuint *arrays) { + CALL_GL_API(glGenVertexArraysOES, n, arrays); +} +GLboolean API_ENTRY(glIsVertexArrayOES)(GLuint array) { + CALL_GL_API_RETURN(glIsVertexArrayOES, array); +} +void API_ENTRY(glDrawArraysInstancedBaseInstanceEXT)(GLenum mode, GLint first, GLsizei count, GLsizei instancecount, GLuint baseinstance) { + CALL_GL_API(glDrawArraysInstancedBaseInstanceEXT, mode, first, count, instancecount, baseinstance); +} +void API_ENTRY(glDrawElementsInstancedBaseInstanceEXT)(GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLuint baseinstance) { + CALL_GL_API(glDrawElementsInstancedBaseInstanceEXT, mode, count, type, indices, instancecount, baseinstance); +} +void API_ENTRY(glDrawElementsInstancedBaseVertexBaseInstanceEXT)(GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex, GLuint baseinstance) { + CALL_GL_API(glDrawElementsInstancedBaseVertexBaseInstanceEXT, mode, count, type, indices, instancecount, basevertex, baseinstance); +} +void API_ENTRY(glBindFragDataLocationIndexedEXT)(GLuint program, GLuint colorNumber, GLuint index, const GLchar *name) { + CALL_GL_API(glBindFragDataLocationIndexedEXT, program, colorNumber, index, name); +} +void API_ENTRY(glBindFragDataLocationEXT)(GLuint program, GLuint color, const GLchar *name) { + CALL_GL_API(glBindFragDataLocationEXT, program, color, name); +} +GLint API_ENTRY(glGetProgramResourceLocationIndexEXT)(GLuint program, GLenum programInterface, const GLchar *name) { + CALL_GL_API_RETURN(glGetProgramResourceLocationIndexEXT, program, programInterface, name); +} +GLint API_ENTRY(glGetFragDataIndexEXT)(GLuint program, const GLchar *name) { + CALL_GL_API_RETURN(glGetFragDataIndexEXT, program, name); +} +void API_ENTRY(glBufferStorageEXT)(GLenum target, GLsizeiptr size, const void *data, GLbitfield flags) { + CALL_GL_API(glBufferStorageEXT, target, size, data, flags); +} +void API_ENTRY(glCopyImageSubDataEXT)(GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth) { + CALL_GL_API(glCopyImageSubDataEXT, srcName, srcTarget, srcLevel, srcX, srcY, srcZ, dstName, dstTarget, dstLevel, dstX, dstY, dstZ, srcWidth, srcHeight, srcDepth); +} +void API_ENTRY(glLabelObjectEXT)(GLenum type, GLuint object, GLsizei length, const GLchar *label) { + CALL_GL_API(glLabelObjectEXT, type, object, length, label); +} +void API_ENTRY(glGetObjectLabelEXT)(GLenum type, GLuint object, GLsizei bufSize, GLsizei *length, GLchar *label) { + CALL_GL_API(glGetObjectLabelEXT, type, object, bufSize, length, label); +} +void API_ENTRY(glInsertEventMarkerEXT)(GLsizei length, const GLchar *marker) { + CALL_GL_API(glInsertEventMarkerEXT, length, marker); +} +void API_ENTRY(glPushGroupMarkerEXT)(GLsizei length, const GLchar *marker) { + CALL_GL_API(glPushGroupMarkerEXT, length, marker); +} +void API_ENTRY(glPopGroupMarkerEXT)(void) { + CALL_GL_API(glPopGroupMarkerEXT); +} +void API_ENTRY(glDiscardFramebufferEXT)(GLenum target, GLsizei numAttachments, const GLenum *attachments) { + CALL_GL_API(glDiscardFramebufferEXT, target, numAttachments, attachments); +} +void API_ENTRY(glGenQueriesEXT)(GLsizei n, GLuint *ids) { + CALL_GL_API(glGenQueriesEXT, n, ids); +} +void API_ENTRY(glDeleteQueriesEXT)(GLsizei n, const GLuint *ids) { + CALL_GL_API(glDeleteQueriesEXT, n, ids); +} +GLboolean API_ENTRY(glIsQueryEXT)(GLuint id) { + CALL_GL_API_RETURN(glIsQueryEXT, id); +} +void API_ENTRY(glBeginQueryEXT)(GLenum target, GLuint id) { + CALL_GL_API(glBeginQueryEXT, target, id); +} +void API_ENTRY(glEndQueryEXT)(GLenum target) { + CALL_GL_API(glEndQueryEXT, target); +} +void API_ENTRY(glQueryCounterEXT)(GLuint id, GLenum target) { + CALL_GL_API(glQueryCounterEXT, id, target); +} +void API_ENTRY(glGetQueryivEXT)(GLenum target, GLenum pname, GLint *params) { + CALL_GL_API(glGetQueryivEXT, target, pname, params); +} +void API_ENTRY(glGetQueryObjectivEXT)(GLuint id, GLenum pname, GLint *params) { + CALL_GL_API(glGetQueryObjectivEXT, id, pname, params); +} +void API_ENTRY(glGetQueryObjectuivEXT)(GLuint id, GLenum pname, GLuint *params) { + CALL_GL_API(glGetQueryObjectuivEXT, id, pname, params); +} +void API_ENTRY(glGetQueryObjecti64vEXT)(GLuint id, GLenum pname, GLint64 *params) { + CALL_GL_API(glGetQueryObjecti64vEXT, id, pname, params); +} +void API_ENTRY(glGetQueryObjectui64vEXT)(GLuint id, GLenum pname, GLuint64 *params) { + CALL_GL_API(glGetQueryObjectui64vEXT, id, pname, params); +} +void API_ENTRY(glDrawBuffersEXT)(GLsizei n, const GLenum *bufs) { + CALL_GL_API(glDrawBuffersEXT, n, bufs); +} +void API_ENTRY(glEnableiEXT)(GLenum target, GLuint index) { + CALL_GL_API(glEnableiEXT, target, index); +} +void API_ENTRY(glDisableiEXT)(GLenum target, GLuint index) { + CALL_GL_API(glDisableiEXT, target, index); +} +void API_ENTRY(glBlendEquationiEXT)(GLuint buf, GLenum mode) { + CALL_GL_API(glBlendEquationiEXT, buf, mode); +} +void API_ENTRY(glBlendEquationSeparateiEXT)(GLuint buf, GLenum modeRGB, GLenum modeAlpha) { + CALL_GL_API(glBlendEquationSeparateiEXT, buf, modeRGB, modeAlpha); +} +void API_ENTRY(glBlendFunciEXT)(GLuint buf, GLenum src, GLenum dst) { + CALL_GL_API(glBlendFunciEXT, buf, src, dst); +} +void API_ENTRY(glBlendFuncSeparateiEXT)(GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha) { + CALL_GL_API(glBlendFuncSeparateiEXT, buf, srcRGB, dstRGB, srcAlpha, dstAlpha); +} +void API_ENTRY(glColorMaskiEXT)(GLuint index, GLboolean r, GLboolean g, GLboolean b, GLboolean a) { + CALL_GL_API(glColorMaskiEXT, index, r, g, b, a); +} +GLboolean API_ENTRY(glIsEnablediEXT)(GLenum target, GLuint index) { + CALL_GL_API_RETURN(glIsEnablediEXT, target, index); +} +void API_ENTRY(glDrawElementsBaseVertexEXT)(GLenum mode, GLsizei count, GLenum type, const void *indices, GLint basevertex) { + CALL_GL_API(glDrawElementsBaseVertexEXT, mode, count, type, indices, basevertex); +} +void API_ENTRY(glDrawRangeElementsBaseVertexEXT)(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices, GLint basevertex) { + CALL_GL_API(glDrawRangeElementsBaseVertexEXT, mode, start, end, count, type, indices, basevertex); +} +void API_ENTRY(glDrawElementsInstancedBaseVertexEXT)(GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex) { + CALL_GL_API(glDrawElementsInstancedBaseVertexEXT, mode, count, type, indices, instancecount, basevertex); +} +void API_ENTRY(glMultiDrawElementsBaseVertexEXT)(GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei primcount, const GLint *basevertex) { + CALL_GL_API(glMultiDrawElementsBaseVertexEXT, mode, count, type, indices, primcount, basevertex); +} +void API_ENTRY(glDrawArraysInstancedEXT)(GLenum mode, GLint start, GLsizei count, GLsizei primcount) { + CALL_GL_API(glDrawArraysInstancedEXT, mode, start, count, primcount); +} +void API_ENTRY(glDrawElementsInstancedEXT)(GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei primcount) { + CALL_GL_API(glDrawElementsInstancedEXT, mode, count, type, indices, primcount); +} +void API_ENTRY(glFramebufferTextureEXT)(GLenum target, GLenum attachment, GLuint texture, GLint level) { + CALL_GL_API(glFramebufferTextureEXT, target, attachment, texture, level); +} +void API_ENTRY(glVertexAttribDivisorEXT)(GLuint index, GLuint divisor) { + CALL_GL_API(glVertexAttribDivisorEXT, index, divisor); +} +void * API_ENTRY(glMapBufferRangeEXT)(GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access) { + CALL_GL_API_RETURN(glMapBufferRangeEXT, target, offset, length, access); +} +void API_ENTRY(glFlushMappedBufferRangeEXT)(GLenum target, GLintptr offset, GLsizeiptr length) { + CALL_GL_API(glFlushMappedBufferRangeEXT, target, offset, length); +} +void API_ENTRY(glMultiDrawArraysEXT)(GLenum mode, const GLint *first, const GLsizei *count, GLsizei primcount) { + CALL_GL_API(glMultiDrawArraysEXT, mode, first, count, primcount); +} +void API_ENTRY(glMultiDrawElementsEXT)(GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei primcount) { + CALL_GL_API(glMultiDrawElementsEXT, mode, count, type, indices, primcount); +} +void API_ENTRY(glMultiDrawArraysIndirectEXT)(GLenum mode, const void *indirect, GLsizei drawcount, GLsizei stride) { + CALL_GL_API(glMultiDrawArraysIndirectEXT, mode, indirect, drawcount, stride); +} +void API_ENTRY(glMultiDrawElementsIndirectEXT)(GLenum mode, GLenum type, const void *indirect, GLsizei drawcount, GLsizei stride) { + CALL_GL_API(glMultiDrawElementsIndirectEXT, mode, type, indirect, drawcount, stride); +} +void API_ENTRY(glRenderbufferStorageMultisampleEXT)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height) { + CALL_GL_API(glRenderbufferStorageMultisampleEXT, target, samples, internalformat, width, height); +} +void API_ENTRY(glFramebufferTexture2DMultisampleEXT)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLsizei samples) { + CALL_GL_API(glFramebufferTexture2DMultisampleEXT, target, attachment, textarget, texture, level, samples); +} +void API_ENTRY(glReadBufferIndexedEXT)(GLenum src, GLint index) { + CALL_GL_API(glReadBufferIndexedEXT, src, index); +} +void API_ENTRY(glDrawBuffersIndexedEXT)(GLint n, const GLenum *location, const GLint *indices) { + CALL_GL_API(glDrawBuffersIndexedEXT, n, location, indices); +} +void API_ENTRY(glGetIntegeri_vEXT)(GLenum target, GLuint index, GLint *data) { + CALL_GL_API(glGetIntegeri_vEXT, target, index, data); +} +void API_ENTRY(glPrimitiveBoundingBoxEXT)(GLfloat minX, GLfloat minY, GLfloat minZ, GLfloat minW, GLfloat maxX, GLfloat maxY, GLfloat maxZ, GLfloat maxW) { + CALL_GL_API(glPrimitiveBoundingBoxEXT, minX, minY, minZ, minW, maxX, maxY, maxZ, maxW); +} +void API_ENTRY(glRasterSamplesEXT)(GLuint samples, GLboolean fixedsamplelocations) { + CALL_GL_API(glRasterSamplesEXT, samples, fixedsamplelocations); +} +GLenum API_ENTRY(glGetGraphicsResetStatusEXT)(void) { + CALL_GL_API_RETURN(glGetGraphicsResetStatusEXT); +} +void API_ENTRY(glReadnPixelsEXT)(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, void *data) { + CALL_GL_API(glReadnPixelsEXT, x, y, width, height, format, type, bufSize, data); +} +void API_ENTRY(glGetnUniformfvEXT)(GLuint program, GLint location, GLsizei bufSize, GLfloat *params) { + CALL_GL_API(glGetnUniformfvEXT, program, location, bufSize, params); +} +void API_ENTRY(glGetnUniformivEXT)(GLuint program, GLint location, GLsizei bufSize, GLint *params) { + CALL_GL_API(glGetnUniformivEXT, program, location, bufSize, params); +} +void API_ENTRY(glActiveShaderProgramEXT)(GLuint pipeline, GLuint program) { + CALL_GL_API(glActiveShaderProgramEXT, pipeline, program); +} +void API_ENTRY(glBindProgramPipelineEXT)(GLuint pipeline) { + CALL_GL_API(glBindProgramPipelineEXT, pipeline); +} +GLuint API_ENTRY(glCreateShaderProgramvEXT)(GLenum type, GLsizei count, const GLchar **strings) { + CALL_GL_API_RETURN(glCreateShaderProgramvEXT, type, count, strings); +} +void API_ENTRY(glDeleteProgramPipelinesEXT)(GLsizei n, const GLuint *pipelines) { + CALL_GL_API(glDeleteProgramPipelinesEXT, n, pipelines); +} +void API_ENTRY(glGenProgramPipelinesEXT)(GLsizei n, GLuint *pipelines) { + CALL_GL_API(glGenProgramPipelinesEXT, n, pipelines); +} +void API_ENTRY(glGetProgramPipelineInfoLogEXT)(GLuint pipeline, GLsizei bufSize, GLsizei *length, GLchar *infoLog) { + CALL_GL_API(glGetProgramPipelineInfoLogEXT, pipeline, bufSize, length, infoLog); +} +void API_ENTRY(glGetProgramPipelineivEXT)(GLuint pipeline, GLenum pname, GLint *params) { + CALL_GL_API(glGetProgramPipelineivEXT, pipeline, pname, params); +} +GLboolean API_ENTRY(glIsProgramPipelineEXT)(GLuint pipeline) { + CALL_GL_API_RETURN(glIsProgramPipelineEXT, pipeline); +} +void API_ENTRY(glProgramParameteriEXT)(GLuint program, GLenum pname, GLint value) { + CALL_GL_API(glProgramParameteriEXT, program, pname, value); +} +void API_ENTRY(glProgramUniform1fEXT)(GLuint program, GLint location, GLfloat v0) { + CALL_GL_API(glProgramUniform1fEXT, program, location, v0); +} +void API_ENTRY(glProgramUniform1fvEXT)(GLuint program, GLint location, GLsizei count, const GLfloat *value) { + CALL_GL_API(glProgramUniform1fvEXT, program, location, count, value); +} +void API_ENTRY(glProgramUniform1iEXT)(GLuint program, GLint location, GLint v0) { + CALL_GL_API(glProgramUniform1iEXT, program, location, v0); +} +void API_ENTRY(glProgramUniform1ivEXT)(GLuint program, GLint location, GLsizei count, const GLint *value) { + CALL_GL_API(glProgramUniform1ivEXT, program, location, count, value); +} +void API_ENTRY(glProgramUniform2fEXT)(GLuint program, GLint location, GLfloat v0, GLfloat v1) { + CALL_GL_API(glProgramUniform2fEXT, program, location, v0, v1); +} +void API_ENTRY(glProgramUniform2fvEXT)(GLuint program, GLint location, GLsizei count, const GLfloat *value) { + CALL_GL_API(glProgramUniform2fvEXT, program, location, count, value); +} +void API_ENTRY(glProgramUniform2iEXT)(GLuint program, GLint location, GLint v0, GLint v1) { + CALL_GL_API(glProgramUniform2iEXT, program, location, v0, v1); +} +void API_ENTRY(glProgramUniform2ivEXT)(GLuint program, GLint location, GLsizei count, const GLint *value) { + CALL_GL_API(glProgramUniform2ivEXT, program, location, count, value); +} +void API_ENTRY(glProgramUniform3fEXT)(GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2) { + CALL_GL_API(glProgramUniform3fEXT, program, location, v0, v1, v2); +} +void API_ENTRY(glProgramUniform3fvEXT)(GLuint program, GLint location, GLsizei count, const GLfloat *value) { + CALL_GL_API(glProgramUniform3fvEXT, program, location, count, value); +} +void API_ENTRY(glProgramUniform3iEXT)(GLuint program, GLint location, GLint v0, GLint v1, GLint v2) { + CALL_GL_API(glProgramUniform3iEXT, program, location, v0, v1, v2); +} +void API_ENTRY(glProgramUniform3ivEXT)(GLuint program, GLint location, GLsizei count, const GLint *value) { + CALL_GL_API(glProgramUniform3ivEXT, program, location, count, value); +} +void API_ENTRY(glProgramUniform4fEXT)(GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3) { + CALL_GL_API(glProgramUniform4fEXT, program, location, v0, v1, v2, v3); +} +void API_ENTRY(glProgramUniform4fvEXT)(GLuint program, GLint location, GLsizei count, const GLfloat *value) { + CALL_GL_API(glProgramUniform4fvEXT, program, location, count, value); +} +void API_ENTRY(glProgramUniform4iEXT)(GLuint program, GLint location, GLint v0, GLint v1, GLint v2, GLint v3) { + CALL_GL_API(glProgramUniform4iEXT, program, location, v0, v1, v2, v3); +} +void API_ENTRY(glProgramUniform4ivEXT)(GLuint program, GLint location, GLsizei count, const GLint *value) { + CALL_GL_API(glProgramUniform4ivEXT, program, location, count, value); +} +void API_ENTRY(glProgramUniformMatrix2fvEXT)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) { + CALL_GL_API(glProgramUniformMatrix2fvEXT, program, location, count, transpose, value); +} +void API_ENTRY(glProgramUniformMatrix3fvEXT)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) { + CALL_GL_API(glProgramUniformMatrix3fvEXT, program, location, count, transpose, value); +} +void API_ENTRY(glProgramUniformMatrix4fvEXT)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) { + CALL_GL_API(glProgramUniformMatrix4fvEXT, program, location, count, transpose, value); +} +void API_ENTRY(glUseProgramStagesEXT)(GLuint pipeline, GLbitfield stages, GLuint program) { + CALL_GL_API(glUseProgramStagesEXT, pipeline, stages, program); +} +void API_ENTRY(glValidateProgramPipelineEXT)(GLuint pipeline) { + CALL_GL_API(glValidateProgramPipelineEXT, pipeline); +} +void API_ENTRY(glProgramUniform1uiEXT)(GLuint program, GLint location, GLuint v0) { + CALL_GL_API(glProgramUniform1uiEXT, program, location, v0); +} +void API_ENTRY(glProgramUniform2uiEXT)(GLuint program, GLint location, GLuint v0, GLuint v1) { + CALL_GL_API(glProgramUniform2uiEXT, program, location, v0, v1); +} +void API_ENTRY(glProgramUniform3uiEXT)(GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2) { + CALL_GL_API(glProgramUniform3uiEXT, program, location, v0, v1, v2); +} +void API_ENTRY(glProgramUniform4uiEXT)(GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3) { + CALL_GL_API(glProgramUniform4uiEXT, program, location, v0, v1, v2, v3); +} +void API_ENTRY(glProgramUniform1uivEXT)(GLuint program, GLint location, GLsizei count, const GLuint *value) { + CALL_GL_API(glProgramUniform1uivEXT, program, location, count, value); +} +void API_ENTRY(glProgramUniform2uivEXT)(GLuint program, GLint location, GLsizei count, const GLuint *value) { + CALL_GL_API(glProgramUniform2uivEXT, program, location, count, value); +} +void API_ENTRY(glProgramUniform3uivEXT)(GLuint program, GLint location, GLsizei count, const GLuint *value) { + CALL_GL_API(glProgramUniform3uivEXT, program, location, count, value); +} +void API_ENTRY(glProgramUniform4uivEXT)(GLuint program, GLint location, GLsizei count, const GLuint *value) { + CALL_GL_API(glProgramUniform4uivEXT, program, location, count, value); +} +void API_ENTRY(glProgramUniformMatrix2x3fvEXT)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) { + CALL_GL_API(glProgramUniformMatrix2x3fvEXT, program, location, count, transpose, value); +} +void API_ENTRY(glProgramUniformMatrix3x2fvEXT)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) { + CALL_GL_API(glProgramUniformMatrix3x2fvEXT, program, location, count, transpose, value); +} +void API_ENTRY(glProgramUniformMatrix2x4fvEXT)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) { + CALL_GL_API(glProgramUniformMatrix2x4fvEXT, program, location, count, transpose, value); +} +void API_ENTRY(glProgramUniformMatrix4x2fvEXT)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) { + CALL_GL_API(glProgramUniformMatrix4x2fvEXT, program, location, count, transpose, value); +} +void API_ENTRY(glProgramUniformMatrix3x4fvEXT)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) { + CALL_GL_API(glProgramUniformMatrix3x4fvEXT, program, location, count, transpose, value); +} +void API_ENTRY(glProgramUniformMatrix4x3fvEXT)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) { + CALL_GL_API(glProgramUniformMatrix4x3fvEXT, program, location, count, transpose, value); +} +void API_ENTRY(glTexPageCommitmentEXT)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLboolean commit) { + CALL_GL_API(glTexPageCommitmentEXT, target, level, xoffset, yoffset, zoffset, width, height, depth, commit); +} +void API_ENTRY(glPatchParameteriEXT)(GLenum pname, GLint value) { + CALL_GL_API(glPatchParameteriEXT, pname, value); +} +void API_ENTRY(glTexParameterIivEXT)(GLenum target, GLenum pname, const GLint *params) { + CALL_GL_API(glTexParameterIivEXT, target, pname, params); +} +void API_ENTRY(glTexParameterIuivEXT)(GLenum target, GLenum pname, const GLuint *params) { + CALL_GL_API(glTexParameterIuivEXT, target, pname, params); +} +void API_ENTRY(glGetTexParameterIivEXT)(GLenum target, GLenum pname, GLint *params) { + CALL_GL_API(glGetTexParameterIivEXT, target, pname, params); +} +void API_ENTRY(glGetTexParameterIuivEXT)(GLenum target, GLenum pname, GLuint *params) { + CALL_GL_API(glGetTexParameterIuivEXT, target, pname, params); +} +void API_ENTRY(glSamplerParameterIivEXT)(GLuint sampler, GLenum pname, const GLint *param) { + CALL_GL_API(glSamplerParameterIivEXT, sampler, pname, param); +} +void API_ENTRY(glSamplerParameterIuivEXT)(GLuint sampler, GLenum pname, const GLuint *param) { + CALL_GL_API(glSamplerParameterIuivEXT, sampler, pname, param); +} +void API_ENTRY(glGetSamplerParameterIivEXT)(GLuint sampler, GLenum pname, GLint *params) { + CALL_GL_API(glGetSamplerParameterIivEXT, sampler, pname, params); +} +void API_ENTRY(glGetSamplerParameterIuivEXT)(GLuint sampler, GLenum pname, GLuint *params) { + CALL_GL_API(glGetSamplerParameterIuivEXT, sampler, pname, params); +} +void API_ENTRY(glTexBufferEXT)(GLenum target, GLenum internalformat, GLuint buffer) { + CALL_GL_API(glTexBufferEXT, target, internalformat, buffer); +} +void API_ENTRY(glTexBufferRangeEXT)(GLenum target, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size) { + CALL_GL_API(glTexBufferRangeEXT, target, internalformat, buffer, offset, size); +} +void API_ENTRY(glTexStorage1DEXT)(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width) { + CALL_GL_API(glTexStorage1DEXT, target, levels, internalformat, width); +} +void API_ENTRY(glTexStorage2DEXT)(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height) { + CALL_GL_API(glTexStorage2DEXT, target, levels, internalformat, width, height); +} +void API_ENTRY(glTexStorage3DEXT)(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth) { + CALL_GL_API(glTexStorage3DEXT, target, levels, internalformat, width, height, depth); +} +void API_ENTRY(glTextureStorage1DEXT)(GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width) { + CALL_GL_API(glTextureStorage1DEXT, texture, target, levels, internalformat, width); +} +void API_ENTRY(glTextureStorage2DEXT)(GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height) { + CALL_GL_API(glTextureStorage2DEXT, texture, target, levels, internalformat, width, height); +} +void API_ENTRY(glTextureStorage3DEXT)(GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth) { + CALL_GL_API(glTextureStorage3DEXT, texture, target, levels, internalformat, width, height, depth); +} +void API_ENTRY(glTextureViewEXT)(GLuint texture, GLenum target, GLuint origtexture, GLenum internalformat, GLuint minlevel, GLuint numlevels, GLuint minlayer, GLuint numlayers) { + CALL_GL_API(glTextureViewEXT, texture, target, origtexture, internalformat, minlevel, numlevels, minlayer, numlayers); +}
\ No newline at end of file diff --git a/libs/hwui/debug/unwrap_gles.h b/libs/hwui/debug/gles_undefine.h index 7716a735a63b..e43829d98e38 100644 --- a/libs/hwui/debug/unwrap_gles.h +++ b/libs/hwui/debug/gles_undefine.h @@ -14,9 +14,6 @@ * limitations under the License. */ -#ifdef HWUI_GLES_WRAP_ENABLED -#undef HWUI_GLES_WRAP_ENABLED - #undef glActiveShaderProgram #undef glActiveShaderProgramEXT #undef glActiveTexture @@ -914,5 +911,3 @@ #undef glWaitSyncAPPLE #undef glWeightPathsNV #undef glWeightPointerOES - -#endif // HWUI_GLES_WRAP_ENABLED diff --git a/libs/hwui/debug/nullegl.cpp b/libs/hwui/debug/nullegl.cpp index b6cc2f247627..1ce180dd7543 100644 --- a/libs/hwui/debug/nullegl.cpp +++ b/libs/hwui/debug/nullegl.cpp @@ -68,6 +68,9 @@ EGLBoolean eglTerminate(EGLDisplay dpy) { } const char * eglQueryString(EGLDisplay dpy, EGLint name) { + if (name == EGL_EXTENSIONS) { + return "EGL_KHR_swap_buffers_with_damage"; + } return ""; } @@ -148,6 +151,10 @@ EGLBoolean eglSwapBuffers(EGLDisplay dpy, EGLSurface surface) { return EGL_TRUE; } +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)); } diff --git a/libs/hwui/debug/nullgles.cpp b/libs/hwui/debug/nullgles.cpp deleted file mode 100644 index 8689f9814f7b..000000000000 --- a/libs/hwui/debug/nullgles.cpp +++ /dev/null @@ -1,284 +0,0 @@ -/* - * Copyright(C) 2015 The Android Open 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 "unwrap_gles.h" - -#include <GLES3/gl3.h> -#include <GLES2/gl2ext.h> - -#include <stdlib.h> -#include <string.h> - -struct { - GLboolean scissorEnabled; -} gState; - -void glGenCommon(GLsizei n, GLuint *buffers) { - static GLuint nextId = 0; - int i; - for(i = 0; i < n; i++) { - buffers[i] = ++nextId; - } -} - -void glGenBuffers(GLsizei n, GLuint *buffers) { - glGenCommon(n, buffers); -} - -void glGenFramebuffers(GLsizei n, GLuint *framebuffers) { - glGenCommon(n, framebuffers); -} - -void glGenRenderbuffers(GLsizei n, GLuint *renderbuffers) { - glGenCommon(n, renderbuffers); -} - -void glGenTextures(GLsizei n, GLuint *textures) { - glGenCommon(n, textures); -} - -GLuint glCreateProgram(void) { - static GLuint nextProgram = 0; - return ++nextProgram; -} - -GLuint glCreateShader(GLenum type) { - static GLuint nextShader = 0; - return ++nextShader; -} - -void 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; - } -} - -void glGetProgramInfoLog(GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog) { - *length = snprintf(infoLog, bufSize, "success"); - if (*length >= bufSize) { - *length = bufSize - 1; - } -} - -void glGetShaderiv(GLuint shader, GLenum pname, GLint *params) { - switch (pname) { - case GL_COMPILE_STATUS: - case GL_DELETE_STATUS: - *params = GL_TRUE; - } -} - -void glGetShaderInfoLog(GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog) { - *length = snprintf(infoLog, bufSize, "success"); - if (*length >= bufSize) { - *length = bufSize - 1; - } -} - -void setBooleanState(GLenum cap, GLboolean value) { - switch (cap) { - case GL_SCISSOR_TEST: - gState.scissorEnabled = value; - break; - } -} - -void glEnable(GLenum cap) { - setBooleanState(cap, GL_TRUE); -} - -void glDisable(GLenum cap) { - setBooleanState(cap, GL_FALSE); -} - -GLboolean glIsEnabled(GLenum cap) { - switch (cap) { - case GL_SCISSOR_TEST: - return gState.scissorEnabled; - default: - return GL_FALSE; - } -} - -void 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; - } -} - -GLenum glCheckFramebufferStatus(GLenum target) { - switch (target) { - case GL_FRAMEBUFFER: - return GL_FRAMEBUFFER_COMPLETE; - default: - return 0; // error case - } -} - -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 ""; - } -} - -const GLubyte* glGetString(GLenum name) { - return (GLubyte*) getString(name); -} - -void glActiveTexture(GLenum texture) {} -void glAttachShader(GLuint program, GLuint shader) {} -void glBindAttribLocation(GLuint program, GLuint index, const GLchar *name) {} -void glBindBuffer(GLenum target, GLuint buffer) {} -void glBindFramebuffer(GLenum target, GLuint framebuffer) {} -void glBindRenderbuffer(GLenum target, GLuint renderbuffer) {} -void glBindTexture(GLenum target, GLuint texture) {} -void glBlendColor(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha) {} -void glBlendEquation(GLenum mode) {} -void glBlendEquationSeparate(GLenum modeRGB, GLenum modeAlpha) {} -void glBlendFunc(GLenum sfactor, GLenum dfactor) {} -void glBlendFuncSeparate(GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha) {} -void glBufferData(GLenum target, GLsizeiptr size, const void *data, GLenum usage) {} -void glBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, const void *data) {} -void glClear(GLbitfield mask) {} -void glClearColor(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha) {} -void glClearDepthf(GLfloat d) {} -void glClearStencil(GLint s) {} -void glColorMask(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha) {} -void glCompileShader(GLuint shader) {} -void glCompressedTexImage2D(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *data) {} -void glCompressedTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data) {} -void glCopyTexImage2D(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border) {} -void glCopyTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height) {} -void glCullFace(GLenum mode) {} -void glDeleteBuffers(GLsizei n, const GLuint *buffers) {} -void glDeleteFramebuffers(GLsizei n, const GLuint *framebuffers) {} -void glDeleteProgram(GLuint program) {} -void glDeleteRenderbuffers(GLsizei n, const GLuint *renderbuffers) {} -void glDeleteShader(GLuint shader) {} -void glDeleteTextures(GLsizei n, const GLuint *textures) {} -void glDepthFunc(GLenum func) {} -void glDepthMask(GLboolean flag) {} -void glDepthRangef(GLfloat n, GLfloat f) {} -void glDetachShader(GLuint program, GLuint shader) {} -void glDisableVertexAttribArray(GLuint index) {} -void glDrawArrays(GLenum mode, GLint first, GLsizei count) {} -void glDrawElements(GLenum mode, GLsizei count, GLenum type, const void *indices) {} -void glEnableVertexAttribArray(GLuint index) {} -void glFinish(void) {} -void glFlush(void) {} -void glFramebufferRenderbuffer(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer) {} -void glFramebufferTexture2D(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level) {} -void glFrontFace(GLenum mode) {} -void glGenerateMipmap(GLenum target) {} -GLint glGetAttribLocation(GLuint program, const GLchar *name) { return 1; } -GLenum glGetError(void) { return GL_NO_ERROR; } -GLint glGetUniformLocation(GLuint program, const GLchar *name) { return 2; } -void glHint(GLenum target, GLenum mode) {} -void glLineWidth(GLfloat width) {} -void glLinkProgram(GLuint program) {} -void glPixelStorei(GLenum pname, GLint param) {} -void glPolygonOffset(GLfloat factor, GLfloat units) {} -void glReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void *pixels) {} -void glReleaseShaderCompiler(void) {} -void glRenderbufferStorage(GLenum target, GLenum internalformat, GLsizei width, GLsizei height) {} -void glSampleCoverage(GLfloat value, GLboolean invert) {} -void glScissor(GLint x, GLint y, GLsizei width, GLsizei height) {} -void glShaderBinary(GLsizei count, const GLuint *shaders, GLenum binaryformat, const void *binary, GLsizei length) {} -void glShaderSource(GLuint shader, GLsizei count, const GLchar *const*string, const GLint *length) {} -void glStencilFunc(GLenum func, GLint ref, GLuint mask) {} -void glStencilFuncSeparate(GLenum face, GLenum func, GLint ref, GLuint mask) {} -void glStencilMask(GLuint mask) {} -void glStencilMaskSeparate(GLenum face, GLuint mask) {} -void glStencilOp(GLenum fail, GLenum zfail, GLenum zpass) {} -void glStencilOpSeparate(GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass) {} -void glTexImage2D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels) {} -void glTexParameterf(GLenum target, GLenum pname, GLfloat param) {} -void glTexParameterfv(GLenum target, GLenum pname, const GLfloat *params) {} -void glTexParameteri(GLenum target, GLenum pname, GLint param) {} -void glTexParameteriv(GLenum target, GLenum pname, const GLint *params) {} -void glTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels) {} -void glUniform1f(GLint location, GLfloat v0) {} -void glUniform1fv(GLint location, GLsizei count, const GLfloat *value) {} -void glUniform1i(GLint location, GLint v0) {} -void glUniform1iv(GLint location, GLsizei count, const GLint *value) {} -void glUniform2f(GLint location, GLfloat v0, GLfloat v1) {} -void glUniform2fv(GLint location, GLsizei count, const GLfloat *value) {} -void glUniform2i(GLint location, GLint v0, GLint v1) {} -void glUniform2iv(GLint location, GLsizei count, const GLint *value) {} -void glUniform3f(GLint location, GLfloat v0, GLfloat v1, GLfloat v2) {} -void glUniform3fv(GLint location, GLsizei count, const GLfloat *value) {} -void glUniform3i(GLint location, GLint v0, GLint v1, GLint v2) {} -void glUniform3iv(GLint location, GLsizei count, const GLint *value) {} -void glUniform4f(GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3) {} -void glUniform4fv(GLint location, GLsizei count, const GLfloat *value) {} -void glUniform4i(GLint location, GLint v0, GLint v1, GLint v2, GLint v3) {} -void glUniform4iv(GLint location, GLsizei count, const GLint *value) {} -void glUniformMatrix2fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) {} -void glUniformMatrix3fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) {} -void glUniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) {} -void glUseProgram(GLuint program) {} -void glValidateProgram(GLuint program) {} -void glVertexAttrib1f(GLuint index, GLfloat x) {} -void glVertexAttrib1fv(GLuint index, const GLfloat *v) {} -void glVertexAttrib2f(GLuint index, GLfloat x, GLfloat y) {} -void glVertexAttrib2fv(GLuint index, const GLfloat *v) {} -void glVertexAttrib3f(GLuint index, GLfloat x, GLfloat y, GLfloat z) {} -void glVertexAttrib3fv(GLuint index, const GLfloat *v) {} -void glVertexAttrib4f(GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w) {} -void glVertexAttrib4fv(GLuint index, const GLfloat *v) {} -void glVertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer) {} -void glViewport(GLint x, GLint y, GLsizei width, GLsizei height) {} - - -// gles2 ext -void glInsertEventMarkerEXT(GLsizei length, const GLchar *marker) {} -void glPushGroupMarkerEXT(GLsizei length, const GLchar *marker) {} -void glPopGroupMarkerEXT(void) {} -void glDiscardFramebufferEXT(GLenum target, GLsizei numAttachments, const GLenum *attachments) {} -void glEGLImageTargetTexture2DOES(GLenum target, GLeglImageOES image) {} - -// GLES3 -void* glMapBufferRange(GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access) { - return 0; -} - -GLboolean glUnmapBuffer(GLenum target) { - return GL_FALSE; -} diff --git a/libs/hwui/debug/wrap_gles.cpp b/libs/hwui/debug/wrap_gles.cpp index c4f2e3537fe8..8dc946e5667b 100644 --- a/libs/hwui/debug/wrap_gles.cpp +++ b/libs/hwui/debug/wrap_gles.cpp @@ -14,80 +14,20 @@ * limitations under the License. */ -#include "unwrap_gles.h" +#include "GlesDriver.h" -#include <EGL/egl.h> -#include <EGL/eglext.h> -#include <GLES/gl.h> -#include <GLES/glext.h> -#include <GLES2/gl2.h> -#include <GLES2/gl2ext.h> -#include <GLES3/gl3.h> -#include <GLES3/gl31.h> -#include <GLES3/gl32.h> +using namespace android::uirenderer::debug; -#include <cutils/log.h> +#undef API_ENTRY +#undef CALL_GL_API +#undef CALL_GL_API_RETURN -void assertNoGlErrors(const char* apicall) { - GLenum status = GL_NO_ERROR; - GLenum lastError = GL_NO_ERROR; - const char* lastErrorName = nullptr; - while ((status = 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"; - } - } - LOG_ALWAYS_FATAL_IF(lastError != GL_NO_ERROR, - "%s error! %s (0x%x)", apicall, lastErrorName, lastError); -} +#define API_ENTRY(x) x +#define CALL_GL_API(api, ...) GlesDriver::get()->api##_(__VA_ARGS__) +#define CALL_GL_API_RETURN(api, ...) return GlesDriver::get()->api##_(__VA_ARGS__) -#define API_ENTRY(x) wrap_##x -#define CALL_GL_API(x, ...) x(__VA_ARGS__); assertNoGlErrors(#x) -#define CALL_GL_API_RETURN(x, ...) auto ret = x(__VA_ARGS__);\ - assertNoGlErrors(#x);\ - return ret +#include "gles_stubs.in" -extern "C" { -#include <gl2_api.in> -#include <gl2ext_api.in> - -// libGLESv2 handles these specially, so they are not in gl2_api.in - -void API_ENTRY(glGetBooleanv)(GLenum pname, GLboolean *data) { - CALL_GL_API(glGetBooleanv, pname, data); -} -void API_ENTRY(glGetFloatv)(GLenum pname, GLfloat *data) { - CALL_GL_API(glGetFloatv, pname, data); -} -void API_ENTRY(glGetIntegerv)(GLenum pname, GLint *data) { - CALL_GL_API(glGetIntegerv, pname, data); -} -const GLubyte * API_ENTRY(glGetString)(GLenum name) { - CALL_GL_API_RETURN(glGetString, name); -} -const GLubyte * API_ENTRY(glGetStringi)(GLenum name, GLuint index) { - CALL_GL_API_RETURN(glGetStringi, name, index); -} -void API_ENTRY(glGetInteger64v)(GLenum pname, GLint64 *data) { - CALL_GL_API(glGetInteger64v, pname, data); -} -} +#undef API_ENTRY +#undef CALL_GL_API +#undef CALL_GL_API_RETURN diff --git a/libs/hwui/debug/wrap_gles.h b/libs/hwui/debug/wrap_gles.h index 4a3537442e73..27a29aa0a6b2 100644 --- a/libs/hwui/debug/wrap_gles.h +++ b/libs/hwui/debug/wrap_gles.h @@ -14,905 +14,24 @@ * limitations under the License. */ -#ifndef HWUI_GLES_WRAP_ENABLED +// #include'ing this file is bad, bad things should be compile errors +#ifdef HWUI_GLES_WRAP_ENABLED +#error wrap_gles.h should only be used as an auto-included header, don't directly #include it +#endif #define HWUI_GLES_WRAP_ENABLED -#define glActiveShaderProgram wrap_glActiveShaderProgram -#define glActiveShaderProgramEXT wrap_glActiveShaderProgramEXT -#define glActiveTexture wrap_glActiveTexture -#define glAlphaFunc wrap_glAlphaFunc -#define glAlphaFuncQCOM wrap_glAlphaFuncQCOM -#define glAlphaFuncx wrap_glAlphaFuncx -#define glAlphaFuncxOES wrap_glAlphaFuncxOES -#define glApplyFramebufferAttachmentCMAAINTEL wrap_glApplyFramebufferAttachmentCMAAINTEL -#define glAttachShader wrap_glAttachShader -#define glBeginConditionalRenderNV wrap_glBeginConditionalRenderNV -#define glBeginPerfMonitorAMD wrap_glBeginPerfMonitorAMD -#define glBeginPerfQueryINTEL wrap_glBeginPerfQueryINTEL -#define glBeginQuery wrap_glBeginQuery -#define glBeginQueryEXT wrap_glBeginQueryEXT -#define glBeginTransformFeedback wrap_glBeginTransformFeedback -#define glBindAttribLocation wrap_glBindAttribLocation -#define glBindBuffer wrap_glBindBuffer -#define glBindBufferBase wrap_glBindBufferBase -#define glBindBufferRange wrap_glBindBufferRange -#define glBindFragDataLocationEXT wrap_glBindFragDataLocationEXT -#define glBindFragDataLocationIndexedEXT wrap_glBindFragDataLocationIndexedEXT -#define glBindFramebuffer wrap_glBindFramebuffer -#define glBindFramebufferOES wrap_glBindFramebufferOES -#define glBindImageTexture wrap_glBindImageTexture -#define glBindProgramPipeline wrap_glBindProgramPipeline -#define glBindProgramPipelineEXT wrap_glBindProgramPipelineEXT -#define glBindRenderbuffer wrap_glBindRenderbuffer -#define glBindRenderbufferOES wrap_glBindRenderbufferOES -#define glBindSampler wrap_glBindSampler -#define glBindTexture wrap_glBindTexture -#define glBindTransformFeedback wrap_glBindTransformFeedback -#define glBindVertexArray wrap_glBindVertexArray -#define glBindVertexArrayOES wrap_glBindVertexArrayOES -#define glBindVertexBuffer wrap_glBindVertexBuffer -#define glBlendBarrier wrap_glBlendBarrier -#define glBlendBarrierKHR wrap_glBlendBarrierKHR -#define glBlendBarrierNV wrap_glBlendBarrierNV -#define glBlendColor wrap_glBlendColor -#define glBlendEquation wrap_glBlendEquation -#define glBlendEquationOES wrap_glBlendEquationOES -#define glBlendEquationSeparate wrap_glBlendEquationSeparate -#define glBlendEquationSeparateOES wrap_glBlendEquationSeparateOES -#define glBlendEquationSeparatei wrap_glBlendEquationSeparatei -#define glBlendEquationSeparateiEXT wrap_glBlendEquationSeparateiEXT -#define glBlendEquationSeparateiOES wrap_glBlendEquationSeparateiOES -#define glBlendEquationi wrap_glBlendEquationi -#define glBlendEquationiEXT wrap_glBlendEquationiEXT -#define glBlendEquationiOES wrap_glBlendEquationiOES -#define glBlendFunc wrap_glBlendFunc -#define glBlendFuncSeparate wrap_glBlendFuncSeparate -#define glBlendFuncSeparateOES wrap_glBlendFuncSeparateOES -#define glBlendFuncSeparatei wrap_glBlendFuncSeparatei -#define glBlendFuncSeparateiEXT wrap_glBlendFuncSeparateiEXT -#define glBlendFuncSeparateiOES wrap_glBlendFuncSeparateiOES -#define glBlendFunci wrap_glBlendFunci -#define glBlendFunciEXT wrap_glBlendFunciEXT -#define glBlendFunciOES wrap_glBlendFunciOES -#define glBlendParameteriNV wrap_glBlendParameteriNV -#define glBlitFramebuffer wrap_glBlitFramebuffer -#define glBlitFramebufferANGLE wrap_glBlitFramebufferANGLE -#define glBlitFramebufferNV wrap_glBlitFramebufferNV -#define glBufferData wrap_glBufferData -#define glBufferStorageEXT wrap_glBufferStorageEXT -#define glBufferSubData wrap_glBufferSubData -#define glCheckFramebufferStatus wrap_glCheckFramebufferStatus -#define glCheckFramebufferStatusOES wrap_glCheckFramebufferStatusOES -#define glClear wrap_glClear -#define glClearBufferfi wrap_glClearBufferfi -#define glClearBufferfv wrap_glClearBufferfv -#define glClearBufferiv wrap_glClearBufferiv -#define glClearBufferuiv wrap_glClearBufferuiv -#define glClearColor wrap_glClearColor -#define glClearColorx wrap_glClearColorx -#define glClearColorxOES wrap_glClearColorxOES -#define glClearDepthf wrap_glClearDepthf -#define glClearDepthfOES wrap_glClearDepthfOES -#define glClearDepthx wrap_glClearDepthx -#define glClearDepthxOES wrap_glClearDepthxOES -#define glClearStencil wrap_glClearStencil -#define glClientActiveTexture wrap_glClientActiveTexture -#define glClientWaitSync wrap_glClientWaitSync -#define glClientWaitSyncAPPLE wrap_glClientWaitSyncAPPLE -#define glClipPlanef wrap_glClipPlanef -#define glClipPlanefIMG wrap_glClipPlanefIMG -#define glClipPlanefOES wrap_glClipPlanefOES -#define glClipPlanex wrap_glClipPlanex -#define glClipPlanexIMG wrap_glClipPlanexIMG -#define glClipPlanexOES wrap_glClipPlanexOES -#define glColor4f wrap_glColor4f -#define glColor4ub wrap_glColor4ub -#define glColor4x wrap_glColor4x -#define glColor4xOES wrap_glColor4xOES -#define glColorMask wrap_glColorMask -#define glColorMaski wrap_glColorMaski -#define glColorMaskiEXT wrap_glColorMaskiEXT -#define glColorMaskiOES wrap_glColorMaskiOES -#define glColorPointer wrap_glColorPointer -#define glCompileShader wrap_glCompileShader -#define glCompressedTexImage2D wrap_glCompressedTexImage2D -#define glCompressedTexImage3D wrap_glCompressedTexImage3D -#define glCompressedTexImage3DOES wrap_glCompressedTexImage3DOES -#define glCompressedTexSubImage2D wrap_glCompressedTexSubImage2D -#define glCompressedTexSubImage3D wrap_glCompressedTexSubImage3D -#define glCompressedTexSubImage3DOES wrap_glCompressedTexSubImage3DOES -#define glCopyBufferSubData wrap_glCopyBufferSubData -#define glCopyBufferSubDataNV wrap_glCopyBufferSubDataNV -#define glCopyImageSubData wrap_glCopyImageSubData -#define glCopyImageSubDataEXT wrap_glCopyImageSubDataEXT -#define glCopyImageSubDataOES wrap_glCopyImageSubDataOES -#define glCopyPathNV wrap_glCopyPathNV -#define glCopyTexImage2D wrap_glCopyTexImage2D -#define glCopyTexSubImage2D wrap_glCopyTexSubImage2D -#define glCopyTexSubImage3D wrap_glCopyTexSubImage3D -#define glCopyTexSubImage3DOES wrap_glCopyTexSubImage3DOES -#define glCopyTextureLevelsAPPLE wrap_glCopyTextureLevelsAPPLE -#define glCoverFillPathInstancedNV wrap_glCoverFillPathInstancedNV -#define glCoverFillPathNV wrap_glCoverFillPathNV -#define glCoverStrokePathInstancedNV wrap_glCoverStrokePathInstancedNV -#define glCoverStrokePathNV wrap_glCoverStrokePathNV -#define glCoverageMaskNV wrap_glCoverageMaskNV -#define glCoverageModulationNV wrap_glCoverageModulationNV -#define glCoverageModulationTableNV wrap_glCoverageModulationTableNV -#define glCoverageOperationNV wrap_glCoverageOperationNV -#define glCreatePerfQueryINTEL wrap_glCreatePerfQueryINTEL -#define glCreateProgram wrap_glCreateProgram -#define glCreateShader wrap_glCreateShader -#define glCreateShaderProgramv wrap_glCreateShaderProgramv -#define glCreateShaderProgramvEXT wrap_glCreateShaderProgramvEXT -#define glCullFace wrap_glCullFace -#define glCurrentPaletteMatrixOES wrap_glCurrentPaletteMatrixOES -#define glDebugMessageCallback wrap_glDebugMessageCallback -#define glDebugMessageCallbackKHR wrap_glDebugMessageCallbackKHR -#define glDebugMessageControl wrap_glDebugMessageControl -#define glDebugMessageControlKHR wrap_glDebugMessageControlKHR -#define glDebugMessageInsert wrap_glDebugMessageInsert -#define glDebugMessageInsertKHR wrap_glDebugMessageInsertKHR -#define glDeleteBuffers wrap_glDeleteBuffers -#define glDeleteFencesNV wrap_glDeleteFencesNV -#define glDeleteFramebuffers wrap_glDeleteFramebuffers -#define glDeleteFramebuffersOES wrap_glDeleteFramebuffersOES -#define glDeletePathsNV wrap_glDeletePathsNV -#define glDeletePerfMonitorsAMD wrap_glDeletePerfMonitorsAMD -#define glDeletePerfQueryINTEL wrap_glDeletePerfQueryINTEL -#define glDeleteProgram wrap_glDeleteProgram -#define glDeleteProgramPipelines wrap_glDeleteProgramPipelines -#define glDeleteProgramPipelinesEXT wrap_glDeleteProgramPipelinesEXT -#define glDeleteQueries wrap_glDeleteQueries -#define glDeleteQueriesEXT wrap_glDeleteQueriesEXT -#define glDeleteRenderbuffers wrap_glDeleteRenderbuffers -#define glDeleteRenderbuffersOES wrap_glDeleteRenderbuffersOES -#define glDeleteSamplers wrap_glDeleteSamplers -#define glDeleteShader wrap_glDeleteShader -#define glDeleteSync wrap_glDeleteSync -#define glDeleteSyncAPPLE wrap_glDeleteSyncAPPLE -#define glDeleteTextures wrap_glDeleteTextures -#define glDeleteTransformFeedbacks wrap_glDeleteTransformFeedbacks -#define glDeleteVertexArrays wrap_glDeleteVertexArrays -#define glDeleteVertexArraysOES wrap_glDeleteVertexArraysOES -#define glDepthFunc wrap_glDepthFunc -#define glDepthMask wrap_glDepthMask -#define glDepthRangeArrayfvNV wrap_glDepthRangeArrayfvNV -#define glDepthRangeIndexedfNV wrap_glDepthRangeIndexedfNV -#define glDepthRangef wrap_glDepthRangef -#define glDepthRangefOES wrap_glDepthRangefOES -#define glDepthRangex wrap_glDepthRangex -#define glDepthRangexOES wrap_glDepthRangexOES -#define glDetachShader wrap_glDetachShader -#define glDisable wrap_glDisable -#define glDisableClientState wrap_glDisableClientState -#define glDisableDriverControlQCOM wrap_glDisableDriverControlQCOM -#define glDisableVertexAttribArray wrap_glDisableVertexAttribArray -#define glDisablei wrap_glDisablei -#define glDisableiEXT wrap_glDisableiEXT -#define glDisableiNV wrap_glDisableiNV -#define glDisableiOES wrap_glDisableiOES -#define glDiscardFramebufferEXT wrap_glDiscardFramebufferEXT -#define glDispatchCompute wrap_glDispatchCompute -#define glDispatchComputeIndirect wrap_glDispatchComputeIndirect -#define glDrawArrays wrap_glDrawArrays -#define glDrawArraysIndirect wrap_glDrawArraysIndirect -#define glDrawArraysInstanced wrap_glDrawArraysInstanced -#define glDrawArraysInstancedANGLE wrap_glDrawArraysInstancedANGLE -#define glDrawArraysInstancedBaseInstanceEXT wrap_glDrawArraysInstancedBaseInstanceEXT -#define glDrawArraysInstancedEXT wrap_glDrawArraysInstancedEXT -#define glDrawArraysInstancedNV wrap_glDrawArraysInstancedNV -#define glDrawBuffers wrap_glDrawBuffers -#define glDrawBuffersEXT wrap_glDrawBuffersEXT -#define glDrawBuffersIndexedEXT wrap_glDrawBuffersIndexedEXT -#define glDrawBuffersNV wrap_glDrawBuffersNV -#define glDrawElements wrap_glDrawElements -#define glDrawElementsBaseVertex wrap_glDrawElementsBaseVertex -#define glDrawElementsBaseVertexEXT wrap_glDrawElementsBaseVertexEXT -#define glDrawElementsBaseVertexOES wrap_glDrawElementsBaseVertexOES -#define glDrawElementsIndirect wrap_glDrawElementsIndirect -#define glDrawElementsInstanced wrap_glDrawElementsInstanced -#define glDrawElementsInstancedANGLE wrap_glDrawElementsInstancedANGLE -#define glDrawElementsInstancedBaseInstanceEXT wrap_glDrawElementsInstancedBaseInstanceEXT -#define glDrawElementsInstancedBaseVertex wrap_glDrawElementsInstancedBaseVertex -#define glDrawElementsInstancedBaseVertexBaseInstanceEXT wrap_glDrawElementsInstancedBaseVertexBaseInstanceEXT -#define glDrawElementsInstancedBaseVertexEXT wrap_glDrawElementsInstancedBaseVertexEXT -#define glDrawElementsInstancedBaseVertexOES wrap_glDrawElementsInstancedBaseVertexOES -#define glDrawElementsInstancedEXT wrap_glDrawElementsInstancedEXT -#define glDrawElementsInstancedNV wrap_glDrawElementsInstancedNV -#define glDrawRangeElements wrap_glDrawRangeElements -#define glDrawRangeElementsBaseVertex wrap_glDrawRangeElementsBaseVertex -#define glDrawRangeElementsBaseVertexEXT wrap_glDrawRangeElementsBaseVertexEXT -#define glDrawRangeElementsBaseVertexOES wrap_glDrawRangeElementsBaseVertexOES -#define glDrawTexfOES wrap_glDrawTexfOES -#define glDrawTexfvOES wrap_glDrawTexfvOES -#define glDrawTexiOES wrap_glDrawTexiOES -#define glDrawTexivOES wrap_glDrawTexivOES -#define glDrawTexsOES wrap_glDrawTexsOES -#define glDrawTexsvOES wrap_glDrawTexsvOES -#define glDrawTexxOES wrap_glDrawTexxOES -#define glDrawTexxvOES wrap_glDrawTexxvOES -#define glEGLImageTargetRenderbufferStorageOES wrap_glEGLImageTargetRenderbufferStorageOES -#define glEGLImageTargetTexture2DOES wrap_glEGLImageTargetTexture2DOES -#define glEnable wrap_glEnable -#define glEnableClientState wrap_glEnableClientState -#define glEnableDriverControlQCOM wrap_glEnableDriverControlQCOM -#define glEnableVertexAttribArray wrap_glEnableVertexAttribArray -#define glEnablei wrap_glEnablei -#define glEnableiEXT wrap_glEnableiEXT -#define glEnableiNV wrap_glEnableiNV -#define glEnableiOES wrap_glEnableiOES -#define glEndConditionalRenderNV wrap_glEndConditionalRenderNV -#define glEndPerfMonitorAMD wrap_glEndPerfMonitorAMD -#define glEndPerfQueryINTEL wrap_glEndPerfQueryINTEL -#define glEndQuery wrap_glEndQuery -#define glEndQueryEXT wrap_glEndQueryEXT -#define glEndTilingQCOM wrap_glEndTilingQCOM -#define glEndTransformFeedback wrap_glEndTransformFeedback -#define glExtGetBufferPointervQCOM wrap_glExtGetBufferPointervQCOM -#define glExtGetBuffersQCOM wrap_glExtGetBuffersQCOM -#define glExtGetFramebuffersQCOM wrap_glExtGetFramebuffersQCOM -#define glExtGetProgramBinarySourceQCOM wrap_glExtGetProgramBinarySourceQCOM -#define glExtGetProgramsQCOM wrap_glExtGetProgramsQCOM -#define glExtGetRenderbuffersQCOM wrap_glExtGetRenderbuffersQCOM -#define glExtGetShadersQCOM wrap_glExtGetShadersQCOM -#define glExtGetTexLevelParameterivQCOM wrap_glExtGetTexLevelParameterivQCOM -#define glExtGetTexSubImageQCOM wrap_glExtGetTexSubImageQCOM -#define glExtGetTexturesQCOM wrap_glExtGetTexturesQCOM -#define glExtIsProgramBinaryQCOM wrap_glExtIsProgramBinaryQCOM -#define glExtTexObjectStateOverrideiQCOM wrap_glExtTexObjectStateOverrideiQCOM -#define glFenceSync wrap_glFenceSync -#define glFenceSyncAPPLE wrap_glFenceSyncAPPLE -#define glFinish wrap_glFinish -#define glFinishFenceNV wrap_glFinishFenceNV -#define glFlush wrap_glFlush -#define glFlushMappedBufferRange wrap_glFlushMappedBufferRange -#define glFlushMappedBufferRangeEXT wrap_glFlushMappedBufferRangeEXT -#define glFogf wrap_glFogf -#define glFogfv wrap_glFogfv -#define glFogx wrap_glFogx -#define glFogxOES wrap_glFogxOES -#define glFogxv wrap_glFogxv -#define glFogxvOES wrap_glFogxvOES -#define glFragmentCoverageColorNV wrap_glFragmentCoverageColorNV -#define glFramebufferParameteri wrap_glFramebufferParameteri -#define glFramebufferRenderbuffer wrap_glFramebufferRenderbuffer -#define glFramebufferRenderbufferOES wrap_glFramebufferRenderbufferOES -#define glFramebufferSampleLocationsfvNV wrap_glFramebufferSampleLocationsfvNV -#define glFramebufferTexture wrap_glFramebufferTexture -#define glFramebufferTexture2D wrap_glFramebufferTexture2D -#define glFramebufferTexture2DMultisampleEXT wrap_glFramebufferTexture2DMultisampleEXT -#define glFramebufferTexture2DMultisampleIMG wrap_glFramebufferTexture2DMultisampleIMG -#define glFramebufferTexture2DOES wrap_glFramebufferTexture2DOES -#define glFramebufferTexture3DOES wrap_glFramebufferTexture3DOES -#define glFramebufferTextureEXT wrap_glFramebufferTextureEXT -#define glFramebufferTextureLayer wrap_glFramebufferTextureLayer -#define glFramebufferTextureMultisampleMultiviewOVR wrap_glFramebufferTextureMultisampleMultiviewOVR -#define glFramebufferTextureMultiviewOVR wrap_glFramebufferTextureMultiviewOVR -#define glFramebufferTextureOES wrap_glFramebufferTextureOES -#define glFrontFace wrap_glFrontFace -#define glFrustumf wrap_glFrustumf -#define glFrustumfOES wrap_glFrustumfOES -#define glFrustumx wrap_glFrustumx -#define glFrustumxOES wrap_glFrustumxOES -#define glGenBuffers wrap_glGenBuffers -#define glGenFencesNV wrap_glGenFencesNV -#define glGenFramebuffers wrap_glGenFramebuffers -#define glGenFramebuffersOES wrap_glGenFramebuffersOES -#define glGenPathsNV wrap_glGenPathsNV -#define glGenPerfMonitorsAMD wrap_glGenPerfMonitorsAMD -#define glGenProgramPipelines wrap_glGenProgramPipelines -#define glGenProgramPipelinesEXT wrap_glGenProgramPipelinesEXT -#define glGenQueries wrap_glGenQueries -#define glGenQueriesEXT wrap_glGenQueriesEXT -#define glGenRenderbuffers wrap_glGenRenderbuffers -#define glGenRenderbuffersOES wrap_glGenRenderbuffersOES -#define glGenSamplers wrap_glGenSamplers -#define glGenTextures wrap_glGenTextures -#define glGenTransformFeedbacks wrap_glGenTransformFeedbacks -#define glGenVertexArrays wrap_glGenVertexArrays -#define glGenVertexArraysOES wrap_glGenVertexArraysOES -#define glGenerateMipmap wrap_glGenerateMipmap -#define glGenerateMipmapOES wrap_glGenerateMipmapOES -#define glGetActiveAttrib wrap_glGetActiveAttrib -#define glGetActiveUniform wrap_glGetActiveUniform -#define glGetActiveUniformBlockName wrap_glGetActiveUniformBlockName -#define glGetActiveUniformBlockiv wrap_glGetActiveUniformBlockiv -#define glGetActiveUniformsiv wrap_glGetActiveUniformsiv -#define glGetAttachedShaders wrap_glGetAttachedShaders -#define glGetAttribLocation wrap_glGetAttribLocation -#define glGetBooleani_v wrap_glGetBooleani_v -#define glGetBooleanv wrap_glGetBooleanv -#define glGetBufferParameteri64v wrap_glGetBufferParameteri64v -#define glGetBufferParameteriv wrap_glGetBufferParameteriv -#define glGetBufferPointerv wrap_glGetBufferPointerv -#define glGetBufferPointervOES wrap_glGetBufferPointervOES -#define glGetClipPlanef wrap_glGetClipPlanef -#define glGetClipPlanefOES wrap_glGetClipPlanefOES -#define glGetClipPlanex wrap_glGetClipPlanex -#define glGetClipPlanexOES wrap_glGetClipPlanexOES -#define glGetCoverageModulationTableNV wrap_glGetCoverageModulationTableNV -#define glGetDebugMessageLog wrap_glGetDebugMessageLog -#define glGetDebugMessageLogKHR wrap_glGetDebugMessageLogKHR -#define glGetDriverControlStringQCOM wrap_glGetDriverControlStringQCOM -#define glGetDriverControlsQCOM wrap_glGetDriverControlsQCOM -#define glGetError wrap_glGetError -#define glGetFenceivNV wrap_glGetFenceivNV -#define glGetFirstPerfQueryIdINTEL wrap_glGetFirstPerfQueryIdINTEL -#define glGetFixedv wrap_glGetFixedv -#define glGetFixedvOES wrap_glGetFixedvOES -#define glGetFloati_vNV wrap_glGetFloati_vNV -#define glGetFloatv wrap_glGetFloatv -#define glGetFragDataIndexEXT wrap_glGetFragDataIndexEXT -#define glGetFragDataLocation wrap_glGetFragDataLocation -#define glGetFramebufferAttachmentParameteriv wrap_glGetFramebufferAttachmentParameteriv -#define glGetFramebufferAttachmentParameterivOES wrap_glGetFramebufferAttachmentParameterivOES -#define glGetFramebufferParameteriv wrap_glGetFramebufferParameteriv -#define glGetGraphicsResetStatus wrap_glGetGraphicsResetStatus -#define glGetGraphicsResetStatusEXT wrap_glGetGraphicsResetStatusEXT -#define glGetGraphicsResetStatusKHR wrap_glGetGraphicsResetStatusKHR -#define glGetImageHandleNV wrap_glGetImageHandleNV -#define glGetInteger64i_v wrap_glGetInteger64i_v -#define glGetInteger64v wrap_glGetInteger64v -#define glGetInteger64vAPPLE wrap_glGetInteger64vAPPLE -#define glGetIntegeri_v wrap_glGetIntegeri_v -#define glGetIntegeri_vEXT wrap_glGetIntegeri_vEXT -#define glGetIntegerv wrap_glGetIntegerv -#define glGetInternalformatSampleivNV wrap_glGetInternalformatSampleivNV -#define glGetInternalformativ wrap_glGetInternalformativ -#define glGetLightfv wrap_glGetLightfv -#define glGetLightxv wrap_glGetLightxv -#define glGetLightxvOES wrap_glGetLightxvOES -#define glGetMaterialfv wrap_glGetMaterialfv -#define glGetMaterialxv wrap_glGetMaterialxv -#define glGetMaterialxvOES wrap_glGetMaterialxvOES -#define glGetMultisamplefv wrap_glGetMultisamplefv -#define glGetNextPerfQueryIdINTEL wrap_glGetNextPerfQueryIdINTEL -#define glGetObjectLabel wrap_glGetObjectLabel -#define glGetObjectLabelEXT wrap_glGetObjectLabelEXT -#define glGetObjectLabelKHR wrap_glGetObjectLabelKHR -#define glGetObjectPtrLabel wrap_glGetObjectPtrLabel -#define glGetObjectPtrLabelKHR wrap_glGetObjectPtrLabelKHR -#define glGetPathCommandsNV wrap_glGetPathCommandsNV -#define glGetPathCoordsNV wrap_glGetPathCoordsNV -#define glGetPathDashArrayNV wrap_glGetPathDashArrayNV -#define glGetPathLengthNV wrap_glGetPathLengthNV -#define glGetPathMetricRangeNV wrap_glGetPathMetricRangeNV -#define glGetPathMetricsNV wrap_glGetPathMetricsNV -#define glGetPathParameterfvNV wrap_glGetPathParameterfvNV -#define glGetPathParameterivNV wrap_glGetPathParameterivNV -#define glGetPathSpacingNV wrap_glGetPathSpacingNV -#define glGetPerfCounterInfoINTEL wrap_glGetPerfCounterInfoINTEL -#define glGetPerfMonitorCounterDataAMD wrap_glGetPerfMonitorCounterDataAMD -#define glGetPerfMonitorCounterInfoAMD wrap_glGetPerfMonitorCounterInfoAMD -#define glGetPerfMonitorCounterStringAMD wrap_glGetPerfMonitorCounterStringAMD -#define glGetPerfMonitorCountersAMD wrap_glGetPerfMonitorCountersAMD -#define glGetPerfMonitorGroupStringAMD wrap_glGetPerfMonitorGroupStringAMD -#define glGetPerfMonitorGroupsAMD wrap_glGetPerfMonitorGroupsAMD -#define glGetPerfQueryDataINTEL wrap_glGetPerfQueryDataINTEL -#define glGetPerfQueryIdByNameINTEL wrap_glGetPerfQueryIdByNameINTEL -#define glGetPerfQueryInfoINTEL wrap_glGetPerfQueryInfoINTEL -#define glGetPointerv wrap_glGetPointerv -#define glGetPointervKHR wrap_glGetPointervKHR -#define glGetProgramBinary wrap_glGetProgramBinary -#define glGetProgramBinaryOES wrap_glGetProgramBinaryOES -#define glGetProgramInfoLog wrap_glGetProgramInfoLog -#define glGetProgramInterfaceiv wrap_glGetProgramInterfaceiv -#define glGetProgramPipelineInfoLog wrap_glGetProgramPipelineInfoLog -#define glGetProgramPipelineInfoLogEXT wrap_glGetProgramPipelineInfoLogEXT -#define glGetProgramPipelineiv wrap_glGetProgramPipelineiv -#define glGetProgramPipelineivEXT wrap_glGetProgramPipelineivEXT -#define glGetProgramResourceIndex wrap_glGetProgramResourceIndex -#define glGetProgramResourceLocation wrap_glGetProgramResourceLocation -#define glGetProgramResourceLocationIndexEXT wrap_glGetProgramResourceLocationIndexEXT -#define glGetProgramResourceName wrap_glGetProgramResourceName -#define glGetProgramResourcefvNV wrap_glGetProgramResourcefvNV -#define glGetProgramResourceiv wrap_glGetProgramResourceiv -#define glGetProgramiv wrap_glGetProgramiv -#define glGetQueryObjecti64vEXT wrap_glGetQueryObjecti64vEXT -#define glGetQueryObjectivEXT wrap_glGetQueryObjectivEXT -#define glGetQueryObjectui64vEXT wrap_glGetQueryObjectui64vEXT -#define glGetQueryObjectuiv wrap_glGetQueryObjectuiv -#define glGetQueryObjectuivEXT wrap_glGetQueryObjectuivEXT -#define glGetQueryiv wrap_glGetQueryiv -#define glGetQueryivEXT wrap_glGetQueryivEXT -#define glGetRenderbufferParameteriv wrap_glGetRenderbufferParameteriv -#define glGetRenderbufferParameterivOES wrap_glGetRenderbufferParameterivOES -#define glGetSamplerParameterIiv wrap_glGetSamplerParameterIiv -#define glGetSamplerParameterIivEXT wrap_glGetSamplerParameterIivEXT -#define glGetSamplerParameterIivOES wrap_glGetSamplerParameterIivOES -#define glGetSamplerParameterIuiv wrap_glGetSamplerParameterIuiv -#define glGetSamplerParameterIuivEXT wrap_glGetSamplerParameterIuivEXT -#define glGetSamplerParameterIuivOES wrap_glGetSamplerParameterIuivOES -#define glGetSamplerParameterfv wrap_glGetSamplerParameterfv -#define glGetSamplerParameteriv wrap_glGetSamplerParameteriv -#define glGetShaderInfoLog wrap_glGetShaderInfoLog -#define glGetShaderPrecisionFormat wrap_glGetShaderPrecisionFormat -#define glGetShaderSource wrap_glGetShaderSource -#define glGetShaderiv wrap_glGetShaderiv -#define glGetString wrap_glGetString -#define glGetStringi wrap_glGetStringi -#define glGetSynciv wrap_glGetSynciv -#define glGetSyncivAPPLE wrap_glGetSyncivAPPLE -#define glGetTexEnvfv wrap_glGetTexEnvfv -#define glGetTexEnviv wrap_glGetTexEnviv -#define glGetTexEnvxv wrap_glGetTexEnvxv -#define glGetTexEnvxvOES wrap_glGetTexEnvxvOES -#define glGetTexGenfvOES wrap_glGetTexGenfvOES -#define glGetTexGenivOES wrap_glGetTexGenivOES -#define glGetTexGenxvOES wrap_glGetTexGenxvOES -#define glGetTexLevelParameterfv wrap_glGetTexLevelParameterfv -#define glGetTexLevelParameteriv wrap_glGetTexLevelParameteriv -#define glGetTexParameterIiv wrap_glGetTexParameterIiv -#define glGetTexParameterIivEXT wrap_glGetTexParameterIivEXT -#define glGetTexParameterIivOES wrap_glGetTexParameterIivOES -#define glGetTexParameterIuiv wrap_glGetTexParameterIuiv -#define glGetTexParameterIuivEXT wrap_glGetTexParameterIuivEXT -#define glGetTexParameterIuivOES wrap_glGetTexParameterIuivOES -#define glGetTexParameterfv wrap_glGetTexParameterfv -#define glGetTexParameteriv wrap_glGetTexParameteriv -#define glGetTexParameterxv wrap_glGetTexParameterxv -#define glGetTexParameterxvOES wrap_glGetTexParameterxvOES -#define glGetTextureHandleNV wrap_glGetTextureHandleNV -#define glGetTextureSamplerHandleNV wrap_glGetTextureSamplerHandleNV -#define glGetTransformFeedbackVarying wrap_glGetTransformFeedbackVarying -#define glGetTranslatedShaderSourceANGLE wrap_glGetTranslatedShaderSourceANGLE -#define glGetUniformBlockIndex wrap_glGetUniformBlockIndex -#define glGetUniformIndices wrap_glGetUniformIndices -#define glGetUniformLocation wrap_glGetUniformLocation -#define glGetUniformfv wrap_glGetUniformfv -#define glGetUniformiv wrap_glGetUniformiv -#define glGetUniformuiv wrap_glGetUniformuiv -#define glGetVertexAttribIiv wrap_glGetVertexAttribIiv -#define glGetVertexAttribIuiv wrap_glGetVertexAttribIuiv -#define glGetVertexAttribPointerv wrap_glGetVertexAttribPointerv -#define glGetVertexAttribfv wrap_glGetVertexAttribfv -#define glGetVertexAttribiv wrap_glGetVertexAttribiv -#define glGetnUniformfv wrap_glGetnUniformfv -#define glGetnUniformfvEXT wrap_glGetnUniformfvEXT -#define glGetnUniformfvKHR wrap_glGetnUniformfvKHR -#define glGetnUniformiv wrap_glGetnUniformiv -#define glGetnUniformivEXT wrap_glGetnUniformivEXT -#define glGetnUniformivKHR wrap_glGetnUniformivKHR -#define glGetnUniformuiv wrap_glGetnUniformuiv -#define glGetnUniformuivKHR wrap_glGetnUniformuivKHR -#define glHint wrap_glHint -#define glInsertEventMarkerEXT wrap_glInsertEventMarkerEXT -#define glInterpolatePathsNV wrap_glInterpolatePathsNV -#define glInvalidateFramebuffer wrap_glInvalidateFramebuffer -#define glInvalidateSubFramebuffer wrap_glInvalidateSubFramebuffer -#define glIsBuffer wrap_glIsBuffer -#define glIsEnabled wrap_glIsEnabled -#define glIsEnabledi wrap_glIsEnabledi -#define glIsEnablediEXT wrap_glIsEnablediEXT -#define glIsEnablediNV wrap_glIsEnablediNV -#define glIsEnablediOES wrap_glIsEnablediOES -#define glIsFenceNV wrap_glIsFenceNV -#define glIsFramebuffer wrap_glIsFramebuffer -#define glIsFramebufferOES wrap_glIsFramebufferOES -#define glIsImageHandleResidentNV wrap_glIsImageHandleResidentNV -#define glIsPathNV wrap_glIsPathNV -#define glIsPointInFillPathNV wrap_glIsPointInFillPathNV -#define glIsPointInStrokePathNV wrap_glIsPointInStrokePathNV -#define glIsProgram wrap_glIsProgram -#define glIsProgramPipeline wrap_glIsProgramPipeline -#define glIsProgramPipelineEXT wrap_glIsProgramPipelineEXT -#define glIsQuery wrap_glIsQuery -#define glIsQueryEXT wrap_glIsQueryEXT -#define glIsRenderbuffer wrap_glIsRenderbuffer -#define glIsRenderbufferOES wrap_glIsRenderbufferOES -#define glIsSampler wrap_glIsSampler -#define glIsShader wrap_glIsShader -#define glIsSync wrap_glIsSync -#define glIsSyncAPPLE wrap_glIsSyncAPPLE -#define glIsTexture wrap_glIsTexture -#define glIsTextureHandleResidentNV wrap_glIsTextureHandleResidentNV -#define glIsTransformFeedback wrap_glIsTransformFeedback -#define glIsVertexArray wrap_glIsVertexArray -#define glIsVertexArrayOES wrap_glIsVertexArrayOES -#define glLabelObjectEXT wrap_glLabelObjectEXT -#define glLightModelf wrap_glLightModelf -#define glLightModelfv wrap_glLightModelfv -#define glLightModelx wrap_glLightModelx -#define glLightModelxOES wrap_glLightModelxOES -#define glLightModelxv wrap_glLightModelxv -#define glLightModelxvOES wrap_glLightModelxvOES -#define glLightf wrap_glLightf -#define glLightfv wrap_glLightfv -#define glLightx wrap_glLightx -#define glLightxOES wrap_glLightxOES -#define glLightxv wrap_glLightxv -#define glLightxvOES wrap_glLightxvOES -#define glLineWidth wrap_glLineWidth -#define glLineWidthx wrap_glLineWidthx -#define glLineWidthxOES wrap_glLineWidthxOES -#define glLinkProgram wrap_glLinkProgram -#define glLoadIdentity wrap_glLoadIdentity -#define glLoadMatrixf wrap_glLoadMatrixf -#define glLoadMatrixx wrap_glLoadMatrixx -#define glLoadMatrixxOES wrap_glLoadMatrixxOES -#define glLoadPaletteFromModelViewMatrixOES wrap_glLoadPaletteFromModelViewMatrixOES -#define glLogicOp wrap_glLogicOp -#define glMakeImageHandleNonResidentNV wrap_glMakeImageHandleNonResidentNV -#define glMakeImageHandleResidentNV wrap_glMakeImageHandleResidentNV -#define glMakeTextureHandleNonResidentNV wrap_glMakeTextureHandleNonResidentNV -#define glMakeTextureHandleResidentNV wrap_glMakeTextureHandleResidentNV -#define glMapBufferOES wrap_glMapBufferOES -#define glMapBufferRange wrap_glMapBufferRange -#define glMapBufferRangeEXT wrap_glMapBufferRangeEXT -#define glMaterialf wrap_glMaterialf -#define glMaterialfv wrap_glMaterialfv -#define glMaterialx wrap_glMaterialx -#define glMaterialxOES wrap_glMaterialxOES -#define glMaterialxv wrap_glMaterialxv -#define glMaterialxvOES wrap_glMaterialxvOES -#define glMatrixIndexPointerOES wrap_glMatrixIndexPointerOES -#define glMatrixLoad3x2fNV wrap_glMatrixLoad3x2fNV -#define glMatrixLoad3x3fNV wrap_glMatrixLoad3x3fNV -#define glMatrixLoadTranspose3x3fNV wrap_glMatrixLoadTranspose3x3fNV -#define glMatrixMode wrap_glMatrixMode -#define glMatrixMult3x2fNV wrap_glMatrixMult3x2fNV -#define glMatrixMult3x3fNV wrap_glMatrixMult3x3fNV -#define glMatrixMultTranspose3x3fNV wrap_glMatrixMultTranspose3x3fNV -#define glMemoryBarrier wrap_glMemoryBarrier -#define glMemoryBarrierByRegion wrap_glMemoryBarrierByRegion -#define glMinSampleShading wrap_glMinSampleShading -#define glMinSampleShadingOES wrap_glMinSampleShadingOES -#define glMultMatrixf wrap_glMultMatrixf -#define glMultMatrixx wrap_glMultMatrixx -#define glMultMatrixxOES wrap_glMultMatrixxOES -#define glMultiDrawArraysEXT wrap_glMultiDrawArraysEXT -#define glMultiDrawArraysIndirectEXT wrap_glMultiDrawArraysIndirectEXT -#define glMultiDrawElementsBaseVertexEXT wrap_glMultiDrawElementsBaseVertexEXT -#define glMultiDrawElementsBaseVertexOES wrap_glMultiDrawElementsBaseVertexOES -#define glMultiDrawElementsEXT wrap_glMultiDrawElementsEXT -#define glMultiDrawElementsIndirectEXT wrap_glMultiDrawElementsIndirectEXT -#define glMultiTexCoord4f wrap_glMultiTexCoord4f -#define glMultiTexCoord4x wrap_glMultiTexCoord4x -#define glMultiTexCoord4xOES wrap_glMultiTexCoord4xOES -#define glNamedFramebufferSampleLocationsfvNV wrap_glNamedFramebufferSampleLocationsfvNV -#define glNormal3f wrap_glNormal3f -#define glNormal3x wrap_glNormal3x -#define glNormal3xOES wrap_glNormal3xOES -#define glNormalPointer wrap_glNormalPointer -#define glObjectLabel wrap_glObjectLabel -#define glObjectLabelKHR wrap_glObjectLabelKHR -#define glObjectPtrLabel wrap_glObjectPtrLabel -#define glObjectPtrLabelKHR wrap_glObjectPtrLabelKHR -#define glOrthof wrap_glOrthof -#define glOrthofOES wrap_glOrthofOES -#define glOrthox wrap_glOrthox -#define glOrthoxOES wrap_glOrthoxOES -#define glPatchParameteri wrap_glPatchParameteri -#define glPatchParameteriEXT wrap_glPatchParameteriEXT -#define glPatchParameteriOES wrap_glPatchParameteriOES -#define glPathCommandsNV wrap_glPathCommandsNV -#define glPathCoordsNV wrap_glPathCoordsNV -#define glPathCoverDepthFuncNV wrap_glPathCoverDepthFuncNV -#define glPathDashArrayNV wrap_glPathDashArrayNV -#define glPathGlyphIndexArrayNV wrap_glPathGlyphIndexArrayNV -#define glPathGlyphIndexRangeNV wrap_glPathGlyphIndexRangeNV -#define glPathGlyphRangeNV wrap_glPathGlyphRangeNV -#define glPathGlyphsNV wrap_glPathGlyphsNV -#define glPathMemoryGlyphIndexArrayNV wrap_glPathMemoryGlyphIndexArrayNV -#define glPathParameterfNV wrap_glPathParameterfNV -#define glPathParameterfvNV wrap_glPathParameterfvNV -#define glPathParameteriNV wrap_glPathParameteriNV -#define glPathParameterivNV wrap_glPathParameterivNV -#define glPathStencilDepthOffsetNV wrap_glPathStencilDepthOffsetNV -#define glPathStencilFuncNV wrap_glPathStencilFuncNV -#define glPathStringNV wrap_glPathStringNV -#define glPathSubCommandsNV wrap_glPathSubCommandsNV -#define glPathSubCoordsNV wrap_glPathSubCoordsNV -#define glPauseTransformFeedback wrap_glPauseTransformFeedback -#define glPixelStorei wrap_glPixelStorei -#define glPointAlongPathNV wrap_glPointAlongPathNV -#define glPointParameterf wrap_glPointParameterf -#define glPointParameterfv wrap_glPointParameterfv -#define glPointParameterx wrap_glPointParameterx -#define glPointParameterxOES wrap_glPointParameterxOES -#define glPointParameterxv wrap_glPointParameterxv -#define glPointParameterxvOES wrap_glPointParameterxvOES -#define glPointSize wrap_glPointSize -#define glPointSizePointerOES wrap_glPointSizePointerOES -#define glPointSizex wrap_glPointSizex -#define glPointSizexOES wrap_glPointSizexOES -#define glPolygonModeNV wrap_glPolygonModeNV -#define glPolygonOffset wrap_glPolygonOffset -#define glPolygonOffsetx wrap_glPolygonOffsetx -#define glPolygonOffsetxOES wrap_glPolygonOffsetxOES -#define glPopDebugGroup wrap_glPopDebugGroup -#define glPopDebugGroupKHR wrap_glPopDebugGroupKHR -#define glPopGroupMarkerEXT wrap_glPopGroupMarkerEXT -#define glPopMatrix wrap_glPopMatrix -#define glPrimitiveBoundingBox wrap_glPrimitiveBoundingBox -#define glPrimitiveBoundingBoxEXT wrap_glPrimitiveBoundingBoxEXT -#define glPrimitiveBoundingBoxOES wrap_glPrimitiveBoundingBoxOES -#define glProgramBinary wrap_glProgramBinary -#define glProgramBinaryOES wrap_glProgramBinaryOES -#define glProgramParameteri wrap_glProgramParameteri -#define glProgramParameteriEXT wrap_glProgramParameteriEXT -#define glProgramPathFragmentInputGenNV wrap_glProgramPathFragmentInputGenNV -#define glProgramUniform1f wrap_glProgramUniform1f -#define glProgramUniform1fEXT wrap_glProgramUniform1fEXT -#define glProgramUniform1fv wrap_glProgramUniform1fv -#define glProgramUniform1fvEXT wrap_glProgramUniform1fvEXT -#define glProgramUniform1i wrap_glProgramUniform1i -#define glProgramUniform1iEXT wrap_glProgramUniform1iEXT -#define glProgramUniform1iv wrap_glProgramUniform1iv -#define glProgramUniform1ivEXT wrap_glProgramUniform1ivEXT -#define glProgramUniform1ui wrap_glProgramUniform1ui -#define glProgramUniform1uiEXT wrap_glProgramUniform1uiEXT -#define glProgramUniform1uiv wrap_glProgramUniform1uiv -#define glProgramUniform1uivEXT wrap_glProgramUniform1uivEXT -#define glProgramUniform2f wrap_glProgramUniform2f -#define glProgramUniform2fEXT wrap_glProgramUniform2fEXT -#define glProgramUniform2fv wrap_glProgramUniform2fv -#define glProgramUniform2fvEXT wrap_glProgramUniform2fvEXT -#define glProgramUniform2i wrap_glProgramUniform2i -#define glProgramUniform2iEXT wrap_glProgramUniform2iEXT -#define glProgramUniform2iv wrap_glProgramUniform2iv -#define glProgramUniform2ivEXT wrap_glProgramUniform2ivEXT -#define glProgramUniform2ui wrap_glProgramUniform2ui -#define glProgramUniform2uiEXT wrap_glProgramUniform2uiEXT -#define glProgramUniform2uiv wrap_glProgramUniform2uiv -#define glProgramUniform2uivEXT wrap_glProgramUniform2uivEXT -#define glProgramUniform3f wrap_glProgramUniform3f -#define glProgramUniform3fEXT wrap_glProgramUniform3fEXT -#define glProgramUniform3fv wrap_glProgramUniform3fv -#define glProgramUniform3fvEXT wrap_glProgramUniform3fvEXT -#define glProgramUniform3i wrap_glProgramUniform3i -#define glProgramUniform3iEXT wrap_glProgramUniform3iEXT -#define glProgramUniform3iv wrap_glProgramUniform3iv -#define glProgramUniform3ivEXT wrap_glProgramUniform3ivEXT -#define glProgramUniform3ui wrap_glProgramUniform3ui -#define glProgramUniform3uiEXT wrap_glProgramUniform3uiEXT -#define glProgramUniform3uiv wrap_glProgramUniform3uiv -#define glProgramUniform3uivEXT wrap_glProgramUniform3uivEXT -#define glProgramUniform4f wrap_glProgramUniform4f -#define glProgramUniform4fEXT wrap_glProgramUniform4fEXT -#define glProgramUniform4fv wrap_glProgramUniform4fv -#define glProgramUniform4fvEXT wrap_glProgramUniform4fvEXT -#define glProgramUniform4i wrap_glProgramUniform4i -#define glProgramUniform4iEXT wrap_glProgramUniform4iEXT -#define glProgramUniform4iv wrap_glProgramUniform4iv -#define glProgramUniform4ivEXT wrap_glProgramUniform4ivEXT -#define glProgramUniform4ui wrap_glProgramUniform4ui -#define glProgramUniform4uiEXT wrap_glProgramUniform4uiEXT -#define glProgramUniform4uiv wrap_glProgramUniform4uiv -#define glProgramUniform4uivEXT wrap_glProgramUniform4uivEXT -#define glProgramUniformHandleui64NV wrap_glProgramUniformHandleui64NV -#define glProgramUniformHandleui64vNV wrap_glProgramUniformHandleui64vNV -#define glProgramUniformMatrix2fv wrap_glProgramUniformMatrix2fv -#define glProgramUniformMatrix2fvEXT wrap_glProgramUniformMatrix2fvEXT -#define glProgramUniformMatrix2x3fv wrap_glProgramUniformMatrix2x3fv -#define glProgramUniformMatrix2x3fvEXT wrap_glProgramUniformMatrix2x3fvEXT -#define glProgramUniformMatrix2x4fv wrap_glProgramUniformMatrix2x4fv -#define glProgramUniformMatrix2x4fvEXT wrap_glProgramUniformMatrix2x4fvEXT -#define glProgramUniformMatrix3fv wrap_glProgramUniformMatrix3fv -#define glProgramUniformMatrix3fvEXT wrap_glProgramUniformMatrix3fvEXT -#define glProgramUniformMatrix3x2fv wrap_glProgramUniformMatrix3x2fv -#define glProgramUniformMatrix3x2fvEXT wrap_glProgramUniformMatrix3x2fvEXT -#define glProgramUniformMatrix3x4fv wrap_glProgramUniformMatrix3x4fv -#define glProgramUniformMatrix3x4fvEXT wrap_glProgramUniformMatrix3x4fvEXT -#define glProgramUniformMatrix4fv wrap_glProgramUniformMatrix4fv -#define glProgramUniformMatrix4fvEXT wrap_glProgramUniformMatrix4fvEXT -#define glProgramUniformMatrix4x2fv wrap_glProgramUniformMatrix4x2fv -#define glProgramUniformMatrix4x2fvEXT wrap_glProgramUniformMatrix4x2fvEXT -#define glProgramUniformMatrix4x3fv wrap_glProgramUniformMatrix4x3fv -#define glProgramUniformMatrix4x3fvEXT wrap_glProgramUniformMatrix4x3fvEXT -#define glPushDebugGroup wrap_glPushDebugGroup -#define glPushDebugGroupKHR wrap_glPushDebugGroupKHR -#define glPushGroupMarkerEXT wrap_glPushGroupMarkerEXT -#define glPushMatrix wrap_glPushMatrix -#define glQueryCounterEXT wrap_glQueryCounterEXT -#define glQueryMatrixxOES wrap_glQueryMatrixxOES -#define glRasterSamplesEXT wrap_glRasterSamplesEXT -#define glReadBuffer wrap_glReadBuffer -#define glReadBufferIndexedEXT wrap_glReadBufferIndexedEXT -#define glReadBufferNV wrap_glReadBufferNV -#define glReadPixels wrap_glReadPixels -#define glReadnPixels wrap_glReadnPixels -#define glReadnPixelsEXT wrap_glReadnPixelsEXT -#define glReadnPixelsKHR wrap_glReadnPixelsKHR -#define glReleaseShaderCompiler wrap_glReleaseShaderCompiler -#define glRenderbufferStorage wrap_glRenderbufferStorage -#define glRenderbufferStorageMultisample wrap_glRenderbufferStorageMultisample -#define glRenderbufferStorageMultisampleANGLE wrap_glRenderbufferStorageMultisampleANGLE -#define glRenderbufferStorageMultisampleAPPLE wrap_glRenderbufferStorageMultisampleAPPLE -#define glRenderbufferStorageMultisampleEXT wrap_glRenderbufferStorageMultisampleEXT -#define glRenderbufferStorageMultisampleIMG wrap_glRenderbufferStorageMultisampleIMG -#define glRenderbufferStorageMultisampleNV wrap_glRenderbufferStorageMultisampleNV -#define glRenderbufferStorageOES wrap_glRenderbufferStorageOES -#define glResolveDepthValuesNV wrap_glResolveDepthValuesNV -#define glResolveMultisampleFramebufferAPPLE wrap_glResolveMultisampleFramebufferAPPLE -#define glResumeTransformFeedback wrap_glResumeTransformFeedback -#define glRotatef wrap_glRotatef -#define glRotatex wrap_glRotatex -#define glRotatexOES wrap_glRotatexOES -#define glSampleCoverage wrap_glSampleCoverage -#define glSampleCoveragex wrap_glSampleCoveragex -#define glSampleCoveragexOES wrap_glSampleCoveragexOES -#define glSampleMaski wrap_glSampleMaski -#define glSamplerParameterIiv wrap_glSamplerParameterIiv -#define glSamplerParameterIivEXT wrap_glSamplerParameterIivEXT -#define glSamplerParameterIivOES wrap_glSamplerParameterIivOES -#define glSamplerParameterIuiv wrap_glSamplerParameterIuiv -#define glSamplerParameterIuivEXT wrap_glSamplerParameterIuivEXT -#define glSamplerParameterIuivOES wrap_glSamplerParameterIuivOES -#define glSamplerParameterf wrap_glSamplerParameterf -#define glSamplerParameterfv wrap_glSamplerParameterfv -#define glSamplerParameteri wrap_glSamplerParameteri -#define glSamplerParameteriv wrap_glSamplerParameteriv -#define glScalef wrap_glScalef -#define glScalex wrap_glScalex -#define glScalexOES wrap_glScalexOES -#define glScissor wrap_glScissor -#define glScissorArrayvNV wrap_glScissorArrayvNV -#define glScissorIndexedNV wrap_glScissorIndexedNV -#define glScissorIndexedvNV wrap_glScissorIndexedvNV -#define glSelectPerfMonitorCountersAMD wrap_glSelectPerfMonitorCountersAMD -#define glSetFenceNV wrap_glSetFenceNV -#define glShadeModel wrap_glShadeModel -#define glShaderBinary wrap_glShaderBinary -#define glShaderSource wrap_glShaderSource -#define glStartTilingQCOM wrap_glStartTilingQCOM -#define glStencilFillPathInstancedNV wrap_glStencilFillPathInstancedNV -#define glStencilFillPathNV wrap_glStencilFillPathNV -#define glStencilFunc wrap_glStencilFunc -#define glStencilFuncSeparate wrap_glStencilFuncSeparate -#define glStencilMask wrap_glStencilMask -#define glStencilMaskSeparate wrap_glStencilMaskSeparate -#define glStencilOp wrap_glStencilOp -#define glStencilOpSeparate wrap_glStencilOpSeparate -#define glStencilStrokePathInstancedNV wrap_glStencilStrokePathInstancedNV -#define glStencilStrokePathNV wrap_glStencilStrokePathNV -#define glStencilThenCoverFillPathInstancedNV wrap_glStencilThenCoverFillPathInstancedNV -#define glStencilThenCoverFillPathNV wrap_glStencilThenCoverFillPathNV -#define glStencilThenCoverStrokePathInstancedNV wrap_glStencilThenCoverStrokePathInstancedNV -#define glStencilThenCoverStrokePathNV wrap_glStencilThenCoverStrokePathNV -#define glSubpixelPrecisionBiasNV wrap_glSubpixelPrecisionBiasNV -#define glTestFenceNV wrap_glTestFenceNV -#define glTexBuffer wrap_glTexBuffer -#define glTexBufferEXT wrap_glTexBufferEXT -#define glTexBufferOES wrap_glTexBufferOES -#define glTexBufferRange wrap_glTexBufferRange -#define glTexBufferRangeEXT wrap_glTexBufferRangeEXT -#define glTexBufferRangeOES wrap_glTexBufferRangeOES -#define glTexCoordPointer wrap_glTexCoordPointer -#define glTexEnvf wrap_glTexEnvf -#define glTexEnvfv wrap_glTexEnvfv -#define glTexEnvi wrap_glTexEnvi -#define glTexEnviv wrap_glTexEnviv -#define glTexEnvx wrap_glTexEnvx -#define glTexEnvxOES wrap_glTexEnvxOES -#define glTexEnvxv wrap_glTexEnvxv -#define glTexEnvxvOES wrap_glTexEnvxvOES -#define glTexGenfOES wrap_glTexGenfOES -#define glTexGenfvOES wrap_glTexGenfvOES -#define glTexGeniOES wrap_glTexGeniOES -#define glTexGenivOES wrap_glTexGenivOES -#define glTexGenxOES wrap_glTexGenxOES -#define glTexGenxvOES wrap_glTexGenxvOES -#define glTexImage2D wrap_glTexImage2D -#define glTexImage3D wrap_glTexImage3D -#define glTexImage3DOES wrap_glTexImage3DOES -#define glTexPageCommitmentEXT wrap_glTexPageCommitmentEXT -#define glTexParameterIiv wrap_glTexParameterIiv -#define glTexParameterIivEXT wrap_glTexParameterIivEXT -#define glTexParameterIivOES wrap_glTexParameterIivOES -#define glTexParameterIuiv wrap_glTexParameterIuiv -#define glTexParameterIuivEXT wrap_glTexParameterIuivEXT -#define glTexParameterIuivOES wrap_glTexParameterIuivOES -#define glTexParameterf wrap_glTexParameterf -#define glTexParameterfv wrap_glTexParameterfv -#define glTexParameteri wrap_glTexParameteri -#define glTexParameteriv wrap_glTexParameteriv -#define glTexParameterx wrap_glTexParameterx -#define glTexParameterxOES wrap_glTexParameterxOES -#define glTexParameterxv wrap_glTexParameterxv -#define glTexParameterxvOES wrap_glTexParameterxvOES -#define glTexStorage1DEXT wrap_glTexStorage1DEXT -#define glTexStorage2D wrap_glTexStorage2D -#define glTexStorage2DEXT wrap_glTexStorage2DEXT -#define glTexStorage2DMultisample wrap_glTexStorage2DMultisample -#define glTexStorage3D wrap_glTexStorage3D -#define glTexStorage3DEXT wrap_glTexStorage3DEXT -#define glTexStorage3DMultisample wrap_glTexStorage3DMultisample -#define glTexStorage3DMultisampleOES wrap_glTexStorage3DMultisampleOES -#define glTexSubImage2D wrap_glTexSubImage2D -#define glTexSubImage3D wrap_glTexSubImage3D -#define glTexSubImage3DOES wrap_glTexSubImage3DOES -#define glTextureStorage1DEXT wrap_glTextureStorage1DEXT -#define glTextureStorage2DEXT wrap_glTextureStorage2DEXT -#define glTextureStorage3DEXT wrap_glTextureStorage3DEXT -#define glTextureViewEXT wrap_glTextureViewEXT -#define glTextureViewOES wrap_glTextureViewOES -#define glTransformFeedbackVaryings wrap_glTransformFeedbackVaryings -#define glTransformPathNV wrap_glTransformPathNV -#define glTranslatef wrap_glTranslatef -#define glTranslatex wrap_glTranslatex -#define glTranslatexOES wrap_glTranslatexOES -#define glUniform1f wrap_glUniform1f -#define glUniform1fv wrap_glUniform1fv -#define glUniform1i wrap_glUniform1i -#define glUniform1iv wrap_glUniform1iv -#define glUniform1ui wrap_glUniform1ui -#define glUniform1uiv wrap_glUniform1uiv -#define glUniform2f wrap_glUniform2f -#define glUniform2fv wrap_glUniform2fv -#define glUniform2i wrap_glUniform2i -#define glUniform2iv wrap_glUniform2iv -#define glUniform2ui wrap_glUniform2ui -#define glUniform2uiv wrap_glUniform2uiv -#define glUniform3f wrap_glUniform3f -#define glUniform3fv wrap_glUniform3fv -#define glUniform3i wrap_glUniform3i -#define glUniform3iv wrap_glUniform3iv -#define glUniform3ui wrap_glUniform3ui -#define glUniform3uiv wrap_glUniform3uiv -#define glUniform4f wrap_glUniform4f -#define glUniform4fv wrap_glUniform4fv -#define glUniform4i wrap_glUniform4i -#define glUniform4iv wrap_glUniform4iv -#define glUniform4ui wrap_glUniform4ui -#define glUniform4uiv wrap_glUniform4uiv -#define glUniformBlockBinding wrap_glUniformBlockBinding -#define glUniformHandleui64NV wrap_glUniformHandleui64NV -#define glUniformHandleui64vNV wrap_glUniformHandleui64vNV -#define glUniformMatrix2fv wrap_glUniformMatrix2fv -#define glUniformMatrix2x3fv wrap_glUniformMatrix2x3fv -#define glUniformMatrix2x3fvNV wrap_glUniformMatrix2x3fvNV -#define glUniformMatrix2x4fv wrap_glUniformMatrix2x4fv -#define glUniformMatrix2x4fvNV wrap_glUniformMatrix2x4fvNV -#define glUniformMatrix3fv wrap_glUniformMatrix3fv -#define glUniformMatrix3x2fv wrap_glUniformMatrix3x2fv -#define glUniformMatrix3x2fvNV wrap_glUniformMatrix3x2fvNV -#define glUniformMatrix3x4fv wrap_glUniformMatrix3x4fv -#define glUniformMatrix3x4fvNV wrap_glUniformMatrix3x4fvNV -#define glUniformMatrix4fv wrap_glUniformMatrix4fv -#define glUniformMatrix4x2fv wrap_glUniformMatrix4x2fv -#define glUniformMatrix4x2fvNV wrap_glUniformMatrix4x2fvNV -#define glUniformMatrix4x3fv wrap_glUniformMatrix4x3fv -#define glUniformMatrix4x3fvNV wrap_glUniformMatrix4x3fvNV -#define glUnmapBuffer wrap_glUnmapBuffer -#define glUnmapBufferOES wrap_glUnmapBufferOES -#define glUseProgram wrap_glUseProgram -#define glUseProgramStages wrap_glUseProgramStages -#define glUseProgramStagesEXT wrap_glUseProgramStagesEXT -#define glValidateProgram wrap_glValidateProgram -#define glValidateProgramPipeline wrap_glValidateProgramPipeline -#define glValidateProgramPipelineEXT wrap_glValidateProgramPipelineEXT -#define glVertexAttrib1f wrap_glVertexAttrib1f -#define glVertexAttrib1fv wrap_glVertexAttrib1fv -#define glVertexAttrib2f wrap_glVertexAttrib2f -#define glVertexAttrib2fv wrap_glVertexAttrib2fv -#define glVertexAttrib3f wrap_glVertexAttrib3f -#define glVertexAttrib3fv wrap_glVertexAttrib3fv -#define glVertexAttrib4f wrap_glVertexAttrib4f -#define glVertexAttrib4fv wrap_glVertexAttrib4fv -#define glVertexAttribBinding wrap_glVertexAttribBinding -#define glVertexAttribDivisor wrap_glVertexAttribDivisor -#define glVertexAttribDivisorANGLE wrap_glVertexAttribDivisorANGLE -#define glVertexAttribDivisorEXT wrap_glVertexAttribDivisorEXT -#define glVertexAttribDivisorNV wrap_glVertexAttribDivisorNV -#define glVertexAttribFormat wrap_glVertexAttribFormat -#define glVertexAttribI4i wrap_glVertexAttribI4i -#define glVertexAttribI4iv wrap_glVertexAttribI4iv -#define glVertexAttribI4ui wrap_glVertexAttribI4ui -#define glVertexAttribI4uiv wrap_glVertexAttribI4uiv -#define glVertexAttribIFormat wrap_glVertexAttribIFormat -#define glVertexAttribIPointer wrap_glVertexAttribIPointer -#define glVertexAttribPointer wrap_glVertexAttribPointer -#define glVertexBindingDivisor wrap_glVertexBindingDivisor -#define glVertexPointer wrap_glVertexPointer -#define glViewport wrap_glViewport -#define glViewportArrayvNV wrap_glViewportArrayvNV -#define glViewportIndexedfNV wrap_glViewportIndexedfNV -#define glViewportIndexedfvNV wrap_glViewportIndexedfvNV -#define glWaitSync wrap_glWaitSync -#define glWaitSyncAPPLE wrap_glWaitSyncAPPLE -#define glWeightPathsNV wrap_glWeightPathsNV -#define glWeightPointerOES wrap_glWeightPointerOES +#include <GLES/gl.h> +#include <GLES/glext.h> +#include <GLES2/gl2.h> +#include <GLES2/gl2ext.h> +#include <GLES3/gl3.h> +#include <GLES3/gl31.h> +#include <GLES3/gl32.h> -#endif // HWUI_GLES_WRAP_ENABLED +// Generate stubs that route all the calls to our function table +#include "gles_redefine.h" + +#define GL_ENTRY(ret, api, ...) ret api(__VA_ARGS__); + +#include "gles_decls.in" +#undef GL_ENTRY diff --git a/libs/hwui/font/CacheTexture.cpp b/libs/hwui/font/CacheTexture.cpp index 49e9f65582ae..e2844ad0649a 100644 --- a/libs/hwui/font/CacheTexture.cpp +++ b/libs/hwui/font/CacheTexture.cpp @@ -180,7 +180,12 @@ void CacheTexture::allocatePixelBuffer() { mPixelBuffer = PixelBuffer::create(mFormat, getWidth(), getHeight()); } - mTexture.resize(mWidth, mHeight, mFormat); + GLint internalFormat = mFormat; + if (mFormat == GL_RGBA) { + internalFormat = mCaches.rgbaInternalFormat(); + } + + mTexture.resize(mWidth, mHeight, internalFormat, mFormat); mTexture.setFilter(getLinearFiltering() ? GL_LINEAR : GL_NEAREST); mTexture.setWrap(GL_CLAMP_TO_EDGE); } diff --git a/libs/hwui/font/CachedGlyphInfo.h b/libs/hwui/font/CachedGlyphInfo.h index 0642d59d9b16..073d59bdea3f 100644 --- a/libs/hwui/font/CachedGlyphInfo.h +++ b/libs/hwui/font/CachedGlyphInfo.h @@ -17,8 +17,6 @@ #ifndef ANDROID_HWUI_CACHED_GLYPH_INFO_H #define ANDROID_HWUI_CACHED_GLYPH_INFO_H -#include <SkFixed.h> - namespace android { namespace uirenderer { @@ -41,14 +39,14 @@ struct CachedGlyphInfo { float mBitmapMaxV; // Minimize how much we call freetype uint32_t mGlyphIndex; - uint32_t mAdvanceX; - uint32_t mAdvanceY; + float mAdvanceX; + float mAdvanceY; // Values below contain a glyph's origin in the bitmap int32_t mBitmapLeft; int32_t mBitmapTop; - // Auto-kerning - SkFixed mLsbDelta; - SkFixed mRsbDelta; + // Auto-kerning; represents a 2.6 fixed-point value with range [-1, 1]. + int8_t mLsbDelta; + int8_t mRsbDelta; CacheTexture* mCacheTexture; }; diff --git a/libs/hwui/font/Font.cpp b/libs/hwui/font/Font.cpp index a95454a4c010..24d497cb1860 100644 --- a/libs/hwui/font/Font.cpp +++ b/libs/hwui/font/Font.cpp @@ -304,7 +304,7 @@ void Font::render(const SkPaint* paint, const glyph_t* glyphs, int numGlyphs, } int glyphsCount = 0; - SkFixed prevRsbDelta = 0; + int prevRsbDelta = 0; float penX = 0.0f; @@ -332,14 +332,14 @@ void Font::render(const SkPaint* paint, const glyph_t* glyphs, int numGlyphs, } CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph); - penX += SkFixedToFloat(AUTO_KERN(prevRsbDelta, cachedGlyph->mLsbDelta)); + penX += AUTO_KERN(prevRsbDelta, cachedGlyph->mLsbDelta); prevRsbDelta = cachedGlyph->mRsbDelta; if (cachedGlyph->mIsValid && cachedGlyph->mCacheTexture) { drawCachedGlyph(cachedGlyph, penX, hOffset, vOffset, measure, &position, &tangent); } - penX += SkFixedToFloat(cachedGlyph->mAdvanceX); + penX += cachedGlyph->mAdvanceX; glyphsCount++; } diff --git a/libs/hwui/font/Font.h b/libs/hwui/font/Font.h index 288f73361bbc..504dabb5bcc0 100644 --- a/libs/hwui/font/Font.h +++ b/libs/hwui/font/Font.h @@ -22,14 +22,16 @@ #include <utils/KeyedVector.h> #include <SkScalar.h> -#include <SkGlyphCache.h> #include <SkPaint.h> #include <SkPathMeasure.h> +#include <SkTypeface.h> #include "FontUtil.h" #include "../Rect.h" #include "../Matrix.h" +class SkGlyphCache; + namespace android { namespace uirenderer { diff --git a/libs/hwui/font/FontUtil.h b/libs/hwui/font/FontUtil.h index aa77d98c9343..07e8b34ac66f 100644 --- a/libs/hwui/font/FontUtil.h +++ b/libs/hwui/font/FontUtil.h @@ -44,6 +44,8 @@ typedef uint16_t glyph_t; #define GET_METRICS(cache, glyph) cache->getGlyphIDMetrics(glyph) #define IS_END_OF_STRING(glyph) false -#define AUTO_KERN(prev, next) (((next) - (prev) + 32) >> 6 << 16) +// prev, next are assumed to be signed x.6 fixed-point numbers with range +// [-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 diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp new file mode 100644 index 000000000000..a34b61b56334 --- /dev/null +++ b/libs/hwui/hwui/Bitmap.cpp @@ -0,0 +1,504 @@ +/* + * Copyright (C) 2015 The Android Open 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 "Bitmap.h" + +#include "Caches.h" +#include "renderthread/EglManager.h" +#include "renderthread/RenderThread.h" +#include "renderthread/RenderProxy.h" + +#include <sys/mman.h> + +#include <log/log.h> +#include <cutils/ashmem.h> + +#include <GLES2/gl2.h> +#include <GLES2/gl2ext.h> +#include <EGL/egl.h> +#include <EGL/eglext.h> + +#include <gui/IGraphicBufferAlloc.h> +#include <gui/ISurfaceComposer.h> +#include <private/gui/ComposerService.h> +#include <binder/IServiceManager.h> +#include <ui/PixelFormat.h> + +#include <SkCanvas.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; + if (rowBytes32 < 0 || !sk_64_isS32(bigSize)) { + return false; // allocation will be too large + } + + *size = sk_64_asS32(bigSize); + return true; +} + +typedef sk_sp<Bitmap> (*AllocPixeRef)(size_t allocSize, const SkImageInfo& info, size_t rowBytes, + SkColorTable* ctable); + +static sk_sp<Bitmap> allocateBitmap(SkBitmap* bitmap, SkColorTable* ctable, AllocPixeRef alloc) { + const SkImageInfo& info = bitmap->info(); + if (info.colorType() == kUnknown_SkColorType) { + LOG_ALWAYS_FATAL("unknown bitmap configuration"); + return nullptr; + } + + size_t size; + + // we must respect the rowBytes value already set on the bitmap instead of + // attempting to compute our own. + const size_t rowBytes = bitmap->rowBytes(); + if (!computeAllocationSize(rowBytes, bitmap->height(), &size)) { + return nullptr; + } + + auto wrapper = alloc(size, info, rowBytes, ctable); + if (wrapper) { + wrapper->getSkBitmap(bitmap); + // since we're already allocated, we lockPixels right away + // HeapAllocator behaves this way too + bitmap->lockPixels(); + } + return wrapper; +} + +sk_sp<Bitmap> Bitmap::allocateAshmemBitmap(SkBitmap* bitmap, SkColorTable* ctable) { + return allocateBitmap(bitmap, ctable, &Bitmap::allocateAshmemBitmap); +} + +static sk_sp<Bitmap> allocateHeapBitmap(size_t size, const SkImageInfo& info, size_t rowBytes, + SkColorTable* ctable) { + void* addr = calloc(size, 1); + if (!addr) { + return nullptr; + } + return sk_sp<Bitmap>(new Bitmap(addr, size, info, rowBytes, ctable)); +} + +#define FENCE_TIMEOUT 2000000000 + +// 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; + } +} + +class AutoEglFence { +public: + AutoEglFence(EGLDisplay display) + : mDisplay(display) { + fence = eglCreateSyncKHR(mDisplay, EGL_SYNC_FENCE_KHR, NULL); + } + + ~AutoEglFence() { + if (fence != EGL_NO_SYNC_KHR) { + eglDestroySyncKHR(mDisplay, fence); + } + } + + 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() { + if (image != EGL_NO_IMAGE_KHR) { + eglDestroyImageKHR(mDisplay, image); + } + } + + EGLImageKHR image = EGL_NO_IMAGE_KHR; +private: + EGLDisplay mDisplay = EGL_NO_DISPLAY; +}; + +static bool uploadBitmapToGraphicBuffer(uirenderer::Caches& caches, SkBitmap& bitmap, + GraphicBuffer& buffer, GLint format, GLint type) { + SkAutoLockPixels alp(bitmap); + EGLDisplay display = eglGetCurrentDisplay(); + LOG_ALWAYS_FATAL_IF(display == EGL_NO_DISPLAY, + "Failed to get EGL_DEFAULT_DISPLAY! err=%s", + uirenderer::renderthread::EglManager::eglErrorString()); + // These objects are initialized below but the default "null" + // values are used to cleanup properly at any point in the + // initialization sequenc + GLuint texture = 0; + // 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(); + AutoEglImage autoImage(display, clientBuffer); + if (autoImage.image == EGL_NO_IMAGE_KHR) { + ALOGW("Could not create EGL image, err =%s", + uirenderer::renderthread::EglManager::eglErrorString()); + return false; + } + glGenTextures(1, &texture); + caches.textureState().bindTexture(texture); + glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, autoImage.image); + + GL_CHECKPOINT(MODERATE); + + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, bitmap.width(), bitmap.height(), + format, type, bitmap.getPixels()); + + GL_CHECKPOINT(MODERATE); + + // The fence is used to wait for the texture upload to finish + // properly. We cannot rely on glFlush() and glFinish() as + // some drivers completely ignore these API calls + AutoEglFence autoFence(display); + if (autoFence.fence == EGL_NO_SYNC_KHR) { + LOG_ALWAYS_FATAL("Could not create sync fence %#x", eglGetError()); + return false; + } + // 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); + if (waitStatus != EGL_CONDITION_SATISFIED_KHR) { + LOG_ALWAYS_FATAL("Failed to wait for the fence %#x", eglGetError()); + return false; + } + return true; +} + +sk_sp<Bitmap> Bitmap::allocateHardwareBitmap(uirenderer::renderthread::RenderThread& renderThread, + SkBitmap& skBitmap) { + renderThread.eglManager().initialize(); + uirenderer::Caches& caches = uirenderer::Caches::getInstance(); + + sp<ISurfaceComposer> composer(ComposerService::getComposerService()); + sp<IGraphicBufferAlloc> alloc(composer->createGraphicBufferAlloc()); + if (alloc == NULL) { + ALOGW("createGraphicBufferAlloc() failed in GraphicBuffer.create()"); + return nullptr; + } + + const SkImageInfo& info = skBitmap.info(); + if (info.colorType() == kUnknown_SkColorType || info.colorType() == kAlpha_8_SkColorType) { + ALOGW("unable to create hardware bitmap of colortype: %d", info.colorType()); + return nullptr; + } + + sk_sp<SkColorSpace> sRGB = SkColorSpace::MakeNamed(SkColorSpace::kSRGB_Named); + bool needSRGB = skBitmap.info().colorSpace() == sRGB.get(); + bool hasSRGB = caches.extensions().hasSRGB(); + GLint format, type, internalFormat; + uirenderer::Texture::colorTypeToGlFormatAndType(caches, skBitmap.colorType(), + needSRGB, &internalFormat, &format, &type); + + PixelFormat pixelFormat = internalFormatToPixelFormat(internalFormat); + status_t error; + sp<GraphicBuffer> buffer = alloc->createGraphicBuffer(info.width(), info.height(), pixelFormat, + 1, GraphicBuffer::USAGE_HW_TEXTURE | GraphicBuffer::USAGE_SW_WRITE_NEVER + | GraphicBuffer::USAGE_SW_READ_NEVER , &error); + + if (!buffer.get()) { + ALOGW("createGraphicBuffer() failed in GraphicBuffer.create()"); + return nullptr; + } + + SkBitmap bitmap; + if (CC_UNLIKELY(uirenderer::Texture::hasUnsupportedColorType(skBitmap.info(), + hasSRGB, sRGB.get()))) { + bitmap = uirenderer::Texture::uploadToN32(skBitmap, hasSRGB, std::move(sRGB)); + } else { + bitmap = skBitmap; + } + + if (!uploadBitmapToGraphicBuffer(caches, bitmap, *buffer, format, type)) { + return nullptr; + } + return sk_sp<Bitmap>(new Bitmap(buffer.get(), bitmap.info())); +} + +sk_sp<Bitmap> Bitmap::allocateHardwareBitmap(SkBitmap& bitmap) { + return uirenderer::renderthread::RenderProxy::allocateHardwareBitmap(bitmap); +} + +sk_sp<Bitmap> Bitmap::allocateHeapBitmap(SkBitmap* bitmap, SkColorTable* ctable) { + return allocateBitmap(bitmap, ctable, &android::allocateHeapBitmap); +} + +sk_sp<Bitmap> Bitmap::allocateHeapBitmap(const SkImageInfo& info) { + size_t size; + if (!computeAllocationSize(info.minRowBytes(), info.height(), &size)) { + LOG_ALWAYS_FATAL("trying to allocate too large bitmap"); + return nullptr; + } + return android::allocateHeapBitmap(size, info, info.minRowBytes(), nullptr); +} + +sk_sp<Bitmap> Bitmap::allocateAshmemBitmap(size_t size, const SkImageInfo& info, + size_t rowBytes, SkColorTable* ctable) { + // Create new ashmem region with read/write priv + int fd = ashmem_create_region("bitmap", size); + if (fd < 0) { + return nullptr; + } + + void* addr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (addr == MAP_FAILED) { + close(fd); + return nullptr; + } + + if (ashmem_set_prot_region(fd, PROT_READ) < 0) { + munmap(addr, size); + close(fd); + return nullptr; + } + return sk_sp<Bitmap>(new Bitmap(addr, fd, size, info, rowBytes, ctable)); +} + +void FreePixelRef(void* addr, void* context) { + auto pixelRef = (SkPixelRef*) context; + pixelRef->unlockPixels(); + pixelRef->unref(); +} + +sk_sp<Bitmap> Bitmap::createFrom(const SkImageInfo& info, SkPixelRef& pixelRef) { + pixelRef.ref(); + pixelRef.lockPixels(); + return sk_sp<Bitmap>(new Bitmap((void*) pixelRef.pixels(), (void*) &pixelRef, FreePixelRef, + info, pixelRef.rowBytes(), pixelRef.colorTable())); +} + +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)) { + return nullptr; + } + SkImageInfo info = SkImageInfo::Make(graphicBuffer->getWidth(), graphicBuffer->getHeight(), + kRGBA_8888_SkColorType, kPremul_SkAlphaType, + SkColorSpace::MakeNamed(SkColorSpace::kSRGB_Named)); + return sk_sp<Bitmap>(new Bitmap(graphicBuffer.get(), info)); +} + +void Bitmap::reconfigure(const SkImageInfo& newInfo, size_t rowBytes, SkColorTable* ctable) { + if (kIndex_8_SkColorType != newInfo.colorType()) { + ctable = nullptr; + } + mRowBytes = rowBytes; + if (mColorTable.get() != ctable) { + mColorTable.reset(SkSafeRef(ctable)); + } + + // 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( + newInfo.colorType(), newInfo.alphaType(), &alphaType), + "Failed to validate alpha type!"); + + // Dirty hack is dirty + // TODO: Figure something out here, Skia's current design makes this + // really hard to work with. Skia really, really wants immutable objects, + // but with the nested-ref-count hackery going on that's just not + // feasible without going insane trying to figure it out + SkImageInfo* myInfo = const_cast<SkImageInfo*>(&this->info()); + *myInfo = newInfo; + changeAlphaType(alphaType); + + // Docs say to only call this in the ctor, but we're going to call + // it anyway even if this isn't always the ctor. + // TODO: Fix this too as part of the above TODO + setPreLocked(getStorage(), mRowBytes, mColorTable.get()); +} + +Bitmap::Bitmap(void* address, size_t size, const SkImageInfo& info, size_t rowBytes, SkColorTable* ctable) + : SkPixelRef(info) + , mPixelStorageType(PixelStorageType::Heap) { + mPixelStorage.heap.address = address; + mPixelStorage.heap.size = size; + reconfigure(info, rowBytes, ctable); +} + +Bitmap::Bitmap(void* address, void* context, FreeFunc freeFunc, + const SkImageInfo& info, size_t rowBytes, SkColorTable* ctable) + : SkPixelRef(info) + , mPixelStorageType(PixelStorageType::External) { + mPixelStorage.external.address = address; + mPixelStorage.external.context = context; + mPixelStorage.external.freeFunc = freeFunc; + reconfigure(info, rowBytes, ctable); +} + +Bitmap::Bitmap(void* address, int fd, size_t mappedSize, + const SkImageInfo& info, size_t rowBytes, SkColorTable* ctable) + : SkPixelRef(info) + , mPixelStorageType(PixelStorageType::Ashmem) { + mPixelStorage.ashmem.address = address; + mPixelStorage.ashmem.fd = fd; + mPixelStorage.ashmem.size = mappedSize; + reconfigure(info, rowBytes, ctable); +} + +Bitmap::Bitmap(GraphicBuffer* buffer, const SkImageInfo& info) + : SkPixelRef(info) + , mPixelStorageType(PixelStorageType::Hardware) { + mPixelStorage.hardware.buffer = buffer; + buffer->incStrong(buffer); + mRowBytes = bytesPerPixel(buffer->getPixelFormat()) * buffer->getStride(); +} + +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; + + } + + if (android::uirenderer::Caches::hasInstance()) { + android::uirenderer::Caches::getInstance().textureCache.releaseTexture(getStableID()); + } +} + +bool Bitmap::hasHardwareMipMap() const { + return mHasHardwareMipMap; +} + +void Bitmap::setHasHardwareMipMap(bool hasMipMap) { + mHasHardwareMipMap = 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; + } +} + +bool Bitmap::onNewLockPixels(LockRec* rec) { + rec->fPixels = getStorage(); + rec->fRowBytes = mRowBytes; + rec->fColorTable = mColorTable.get(); + return true; +} + +size_t Bitmap::getAllocatedSizeInBytes() const { + return info().getSafeSize(mRowBytes); +} + +int Bitmap::getAshmemFd() const { + switch (mPixelStorageType) { + 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(); + } +} + +void Bitmap::reconfigure(const SkImageInfo& info) { + reconfigure(info, info.minRowBytes(), nullptr); +} + +void Bitmap::setAlphaType(SkAlphaType alphaType) { + if (!SkColorTypeValidateAlphaType(info().colorType(), alphaType, &alphaType)) { + return; + } + + changeAlphaType(alphaType); +} + +void Bitmap::getSkBitmap(SkBitmap* outBitmap) { + outBitmap->setHasHardwareMipMap(mHasHardwareMipMap); + if (isHardware()) { + ALOGW("Warning: attempt to read pixels from hardware bitmap, which is very slow operation"); + outBitmap->allocPixels(info()); + uirenderer::renderthread::RenderProxy::copyGraphicBufferInto(graphicBuffer(), outBitmap); + return; + } + outBitmap->setInfo(info(), rowBytes()); + outBitmap->setPixelRef(this); +} + +void Bitmap::getSkBitmapForShaders(SkBitmap* outBitmap) { + outBitmap->setInfo(info(), rowBytes()); + outBitmap->setPixelRef(this); + outBitmap->setHasHardwareMipMap(mHasHardwareMipMap); +} + +void Bitmap::getBounds(SkRect* bounds) const { + SkASSERT(bounds); + bounds->set(0, 0, SkIntToScalar(info().width()), SkIntToScalar(info().height())); +} + +GraphicBuffer* Bitmap::graphicBuffer() { + if (isHardware()) { + return mPixelStorage.hardware.buffer; + } + return nullptr; +} + +} // namespace android diff --git a/libs/hwui/hwui/Bitmap.h b/libs/hwui/hwui/Bitmap.h new file mode 100644 index 000000000000..518be032c0eb --- /dev/null +++ b/libs/hwui/hwui/Bitmap.h @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2015 The Android Open 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 <SkBitmap.h> +#include <SkColorTable.h> +#include <SkImageInfo.h> +#include <SkPixelRef.h> +#include <cutils/compiler.h> +#include <ui/GraphicBuffer.h> + +namespace android { + +enum class PixelStorageType { + External, + Heap, + Ashmem, + Hardware, +}; + +namespace uirenderer { +namespace renderthread { + class RenderThread; +} +} + +class PixelStorage; + +typedef void (*FreeFunc)(void* addr, void* context); + +class ANDROID_API Bitmap : public SkPixelRef { +public: + static sk_sp<Bitmap> allocateHeapBitmap(SkBitmap* bitmap, SkColorTable* ctable); + static sk_sp<Bitmap> allocateHeapBitmap(const SkImageInfo& info); + + static sk_sp<Bitmap> allocateHardwareBitmap(SkBitmap& bitmap); + + static sk_sp<Bitmap> allocateAshmemBitmap(SkBitmap* bitmap, SkColorTable* ctable); + static sk_sp<Bitmap> allocateAshmemBitmap(size_t allocSize, const SkImageInfo& info, + size_t rowBytes, SkColorTable* ctable); + + static sk_sp<Bitmap> createFrom(sp<GraphicBuffer> graphicBuffer); + + static sk_sp<Bitmap> createFrom(const SkImageInfo&, SkPixelRef&); + + static sk_sp<Bitmap> allocateHardwareBitmap(uirenderer::renderthread::RenderThread&, + SkBitmap& bitmap); + + Bitmap(void* address, size_t allocSize, const SkImageInfo& info, size_t rowBytes, + SkColorTable* ctable); + Bitmap(void* address, void* context, FreeFunc freeFunc, + const SkImageInfo& info, size_t rowBytes, SkColorTable* ctable); + Bitmap(void* address, int fd, size_t mappedSize, const SkImageInfo& info, + size_t rowBytes, SkColorTable* ctable); + + int width() const { return info().width(); } + int height() const { return info().height(); } + + // Can't mark as override since SkPixelRef::rowBytes isn't virtual + // but that's OK since we just want Bitmap to be able to rely + // on calling rowBytes() on an unlocked pixelref, which it will be + // doing on a Bitmap type, not a SkPixelRef, so static + // dispatching will do what we want. + size_t rowBytes() const { return mRowBytes; } + + int rowBytesAsPixels() const { + return mRowBytes >> info().shiftPerPixel(); + } + + void reconfigure(const SkImageInfo& info, size_t rowBytes, SkColorTable* ctable); + void reconfigure(const SkImageInfo& info); + void setAlphaType(SkAlphaType alphaType); + + void getSkBitmap(SkBitmap* outBitmap); + + // Ugly hack: in case of hardware bitmaps, it sets nullptr as pixels pointer + // so it would crash if anyone tries to render this bitmap. + void getSkBitmapForShaders(SkBitmap* outBitmap); + + int getAshmemFd() const; + size_t getAllocationByteCount() const; + + void setHasHardwareMipMap(bool hasMipMap); + bool hasHardwareMipMap() const; + + bool isOpaque() const {return info().isOpaque(); } + SkColorType colorType() const { return info().colorType(); } + void getBounds(SkRect* bounds) const; + + bool readyToDraw() const { + return this->colorType() != kIndex_8_SkColorType || mColorTable; + } + + bool isHardware() const { + return mPixelStorageType == PixelStorageType::Hardware; + } + + GraphicBuffer* graphicBuffer(); +protected: + virtual bool onNewLockPixels(LockRec* rec) override; + virtual void onUnlockPixels() override { }; + virtual size_t getAllocatedSizeInBytes() const override; +private: + Bitmap(GraphicBuffer* buffer, const SkImageInfo& info); + virtual ~Bitmap(); + void* getStorage() const; + + PixelStorageType mPixelStorageType; + + size_t mRowBytes = 0; + sk_sp<SkColorTable> mColorTable; + bool mHasHardwareMipMap = false; + + union { + struct { + void* address; + void* context; + FreeFunc freeFunc; + } external; + struct { + void* address; + int fd; + size_t size; + } ashmem; + struct { + void* address; + size_t size; + } heap; + struct { + GraphicBuffer* buffer; + } hardware; + } mPixelStorage; +}; + +} //namespace android
\ No newline at end of file diff --git a/libs/hwui/hwui/Canvas.cpp b/libs/hwui/hwui/Canvas.cpp index 7bfa15a26d56..2dc8ce8fe774 100644 --- a/libs/hwui/hwui/Canvas.cpp +++ b/libs/hwui/hwui/Canvas.cpp @@ -16,22 +16,23 @@ #include "Canvas.h" -#include "DisplayListCanvas.h" #include "RecordingCanvas.h" +#include "RenderNode.h" #include "MinikinUtils.h" #include "Paint.h" +#include "Properties.h" +#include "pipeline/skia/SkiaRecordingCanvas.h" #include "Typeface.h" #include <SkDrawFilter.h> namespace android { -Canvas* Canvas::create_recording_canvas(int width, int height) { -#if HWUI_NEW_OPS +Canvas* Canvas::create_recording_canvas(int width, int height, uirenderer::RenderNode* renderNode) { + if (uirenderer::Properties::isSkiaEnabled()) { + return new uirenderer::skiapipeline::SkiaRecordingCanvas(renderNode, width, height); + } return new uirenderer::RecordingCanvas(width, height); -#else - return new uirenderer::DisplayListCanvas(width, height); -#endif } void Canvas::drawTextDecorations(float x, float y, float length, const SkPaint& paint) { @@ -79,8 +80,9 @@ static void simplifyPaint(int color, SkPaint* paint) { class DrawTextFunctor { public: - DrawTextFunctor(const Layout& layout, Canvas* canvas, uint16_t* glyphs, float* pos, - const SkPaint& paint, float x, float y, MinikinRect& bounds, float totalAdvance) + DrawTextFunctor(const minikin::Layout& layout, Canvas* canvas, uint16_t* glyphs, float* pos, + const SkPaint& paint, float x, float y, minikin::MinikinRect& bounds, + float totalAdvance) : layout(layout) , canvas(canvas) , glyphs(glyphs) @@ -135,14 +137,14 @@ public: } } private: - const Layout& layout; + const minikin::Layout& layout; Canvas* canvas; uint16_t* glyphs; float* pos; const SkPaint& paint; float x; float y; - MinikinRect& bounds; + minikin::MinikinRect& bounds; float totalAdvance; }; @@ -151,7 +153,7 @@ void Canvas::drawText(const uint16_t* text, int start, int count, int contextCou // minikin may modify the original paint Paint paint(origPaint); - Layout layout; + minikin::Layout layout; MinikinUtils::doLayout(&layout, &paint, bidiFlags, typeface, text, start, count, contextCount); size_t nGlyphs = layout.nGlyphs(); @@ -160,7 +162,7 @@ void Canvas::drawText(const uint16_t* text, int start, int count, int contextCou x += MinikinUtils::xOffsetForTextAlign(&paint, layout); - MinikinRect bounds; + minikin::MinikinRect bounds; layout.getBounds(&bounds); if (!drawTextAbsolutePos()) { bounds.offset(x, y); @@ -178,7 +180,7 @@ void Canvas::drawText(const uint16_t* text, int start, int count, int contextCou class DrawTextOnPathFunctor { public: - DrawTextOnPathFunctor(const Layout& layout, Canvas* canvas, float hOffset, + DrawTextOnPathFunctor(const minikin::Layout& layout, Canvas* canvas, float hOffset, float vOffset, const Paint& paint, const SkPath& path) : layout(layout) , canvas(canvas) @@ -189,16 +191,10 @@ public: } void operator()(size_t start, size_t end) { - uint16_t glyphs[1]; - for (size_t i = start; i < end; i++) { - glyphs[0] = layout.getGlyphId(i); - float x = hOffset + layout.getX(i); - float y = vOffset + layout.getY(i); - canvas->drawGlyphsOnPath(glyphs, 1, path, x, y, paint); - } + canvas->drawLayoutOnPath(layout, hOffset, vOffset, paint, path, start, end); } private: - const Layout& layout; + const minikin::Layout& layout; Canvas* canvas; float hOffset; float vOffset; @@ -209,7 +205,7 @@ private: void Canvas::drawTextOnPath(const uint16_t* text, int count, int bidiFlags, const SkPath& path, float hOffset, float vOffset, const Paint& paint, Typeface* typeface) { Paint paintCopy(paint); - Layout layout; + minikin::Layout layout; MinikinUtils::doLayout(&layout, &paintCopy, bidiFlags, typeface, text, 0, count, count); hOffset += MinikinUtils::hOffsetForTextAlign(&paintCopy, layout, path); diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h index 55af33e80256..969d87716ce6 100644 --- a/libs/hwui/hwui/Canvas.h +++ b/libs/hwui/hwui/Canvas.h @@ -14,19 +14,23 @@ * limitations under the License. */ -#ifndef ANDROID_GRAPHICS_CANVAS_H -#define ANDROID_GRAPHICS_CANVAS_H +#pragma once #include <cutils/compiler.h> #include <utils/Functor.h> #include "GlFunctorLifecycleListener.h" -#include "utils/NinePatch.h" +#include "utils/Macros.h" +#include <androidfw/ResourceTypes.h> #include <SkBitmap.h> #include <SkCanvas.h> #include <SkMatrix.h> +namespace minikin { + class Layout; +} + namespace android { namespace uirenderer { @@ -61,6 +65,7 @@ class Tree; }; typedef uirenderer::VectorDrawable::Tree VectorDrawableRoot; +class Bitmap; class Paint; struct Typeface; @@ -70,7 +75,23 @@ public: static Canvas* create_canvas(const SkBitmap& bitmap); - static Canvas* create_recording_canvas(int width, int height); + /** + * Create a new Canvas object that records view system drawing operations for deferred + * rendering. A canvas returned by this call supports calls to the resetRecording(...) and + * finishRecording() calls. The latter call returns a DisplayList that is specific to the + * RenderPipeline defined by Properties::getRenderPipelineType(). + * + * @param width of the requested Canvas. + * @param height of the requested Canvas. + * @param renderNode is an optional parameter that specifies the node that will consume the + * DisplayList produced by the returned Canvas. This enables the reuse of select C++ + * objects as a speed optimization. + * @return new non-null Canvas Object. The type of DisplayList produced by this canvas is + determined based on Properties::getRenderPipelineType(). + * + */ + 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. @@ -79,7 +100,8 @@ public: * delegated to this object. This function will call ref() on the * SkCanvas, and the returned Canvas will unref() it upon * destruction. - * @return new Canvas object. Will not return NULL. + * @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); @@ -108,7 +130,8 @@ public: // View System operations (not exposed in public Canvas API) // ---------------------------------------------------------------------------- - virtual void resetRecording(int width, int height) = 0; + virtual void resetRecording(int width, int height, + uirenderer::RenderNode* renderNode = nullptr) = 0; virtual uirenderer::DisplayList* finishRecording() = 0; virtual void insertReorderBarrier(bool enableReorder) = 0; @@ -159,9 +182,8 @@ public: virtual bool quickRejectPath(const SkPath& path) const = 0; virtual bool clipRect(float left, float top, float right, float bottom, - SkRegion::Op op = SkRegion::kIntersect_Op) = 0; - virtual bool clipPath(const SkPath* path, SkRegion::Op op) = 0; - virtual bool clipRegion(const SkRegion* region, SkRegion::Op op) = 0; + SkClipOp op) = 0; + virtual bool clipPath(const SkPath* path, SkClipOp op) = 0; // filters virtual SkDrawFilter* getDrawFilter() = 0; @@ -170,7 +192,7 @@ public: // ---------------------------------------------------------------------------- // Canvas draw operations // ---------------------------------------------------------------------------- - virtual void drawColor(int color, SkXfermode::Mode mode) = 0; + virtual void drawColor(int color, SkBlendMode mode) = 0; virtual void drawPaint(const SkPaint& paint) = 0; // Geometry @@ -195,16 +217,16 @@ public: const uint16_t* indices, int indexCount, const SkPaint& paint) = 0; // Bitmap-based - virtual void drawBitmap(const SkBitmap& bitmap, float left, float top, + virtual void drawBitmap(Bitmap& bitmap, float left, float top, const SkPaint* paint) = 0; - virtual void drawBitmap(const SkBitmap& bitmap, const SkMatrix& matrix, + virtual void drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, const SkPaint* paint) = 0; - virtual void drawBitmap(const SkBitmap& bitmap, float srcLeft, float srcTop, + 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(const SkBitmap& bitmap, int meshWidth, int meshHeight, + virtual void drawBitmapMesh(Bitmap& bitmap, int meshWidth, int meshHeight, const float* vertices, const int* colors, const SkPaint* paint) = 0; - virtual void drawNinePatch(const SkBitmap& bitmap, const android::Res_png_9patch& chunk, + virtual void drawNinePatch(Bitmap& bitmap, const android::Res_png_9patch& chunk, float dstLeft, float dstTop, float dstRight, float dstBottom, const SkPaint* paint) = 0; @@ -220,7 +242,7 @@ public: /** * Draws a VectorDrawable onto the canvas. */ - virtual void drawVectorDrawable(VectorDrawableRoot* tree); + virtual void drawVectorDrawable(VectorDrawableRoot* tree) = 0; /** * Converts utf16 text to glyphs, calculating position and boundary, @@ -243,14 +265,11 @@ protected: const SkPaint& paint, float x, float y, float boundsLeft, float boundsTop, float boundsRight, float boundsBottom, float totalAdvance) = 0; - /** drawTextOnPath: count is of glyphs */ - virtual void drawGlyphsOnPath(const uint16_t* glyphs, int count, const SkPath& path, - float hOffset, float vOffset, const SkPaint& paint) = 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; friend class DrawTextFunctor; friend class DrawTextOnPathFunctor; friend class uirenderer::SkiaCanvasProxy; }; }; // namespace android -#endif // ANDROID_GRAPHICS_CANVAS_H diff --git a/libs/hwui/hwui/MinikinSkia.cpp b/libs/hwui/hwui/MinikinSkia.cpp index 159a1444e044..6a003794fb28 100644 --- a/libs/hwui/hwui/MinikinSkia.cpp +++ b/libs/hwui/hwui/MinikinSkia.cpp @@ -17,23 +17,21 @@ #include "MinikinSkia.h" #include <log/log.h> - +#include <SkFontDescriptor.h> +#include <SkFontMgr.h> #include <SkPaint.h> #include <SkTypeface.h> namespace android { -MinikinFontSkia::MinikinFontSkia(SkTypeface* typeface, const void* fontData, size_t fontSize, +MinikinFontSkia::MinikinFontSkia(sk_sp<SkTypeface> typeface, const void* fontData, size_t fontSize, int ttcIndex) : - MinikinFont(typeface->uniqueID()), mTypeface(typeface), mFontData(fontData), + minikin::MinikinFont(typeface->uniqueID()), mTypeface(std::move(typeface)), mFontData(fontData), mFontSize(fontSize), mTtcIndex(ttcIndex) { } -MinikinFontSkia::~MinikinFontSkia() { - SkSafeUnref(mTypeface); -} - -static void MinikinFontSkia_SetSkiaPaint(const MinikinFont* font, SkPaint* skPaint, const MinikinPaint& paint) { +static void MinikinFontSkia_SetSkiaPaint(const minikin::MinikinFont* font, SkPaint* skPaint, + const minikin::MinikinPaint& paint) { skPaint->setTextEncoding(SkPaint::kGlyphID_TextEncoding); skPaint->setTextSize(paint.size); skPaint->setTextScaleX(paint.scaleX); @@ -44,7 +42,7 @@ static void MinikinFontSkia_SetSkiaPaint(const MinikinFont* font, SkPaint* skPai } float MinikinFontSkia::GetHorizontalAdvance(uint32_t glyph_id, - const MinikinPaint &paint) const { + const minikin::MinikinPaint &paint) const { SkPaint skPaint; uint16_t glyph16 = glyph_id; SkScalar skWidth; @@ -56,8 +54,8 @@ float MinikinFontSkia::GetHorizontalAdvance(uint32_t glyph_id, return skWidth; } -void MinikinFontSkia::GetBounds(MinikinRect* bounds, uint32_t glyph_id, - const MinikinPaint& paint) const { +void MinikinFontSkia::GetBounds(minikin::MinikinRect* bounds, uint32_t glyph_id, + const minikin::MinikinPaint& paint) const { SkPaint skPaint; uint16_t glyph16 = glyph_id; SkRect skBounds; @@ -69,23 +67,11 @@ void MinikinFontSkia::GetBounds(MinikinRect* bounds, uint32_t glyph_id, bounds->mBottom = skBounds.fBottom; } -const void* MinikinFontSkia::GetTable(uint32_t tag, size_t* size, MinikinDestroyFunc* destroy) { - // we don't have a buffer to the font data, copy to own buffer - const size_t tableSize = mTypeface->getTableSize(tag); - *size = tableSize; - if (tableSize == 0) { - return nullptr; - } - void* buf = malloc(tableSize); - if (buf == nullptr) { - return nullptr; - } - mTypeface->getTableData(tag, 0, tableSize, buf); - *destroy = free; - return buf; +SkTypeface *MinikinFontSkia::GetSkTypeface() const { + return mTypeface.get(); } -SkTypeface *MinikinFontSkia::GetSkTypeface() const { +sk_sp<SkTypeface> MinikinFontSkia::RefSkTypeface() const { return mTypeface; } @@ -101,6 +87,28 @@ int MinikinFontSkia::GetFontIndex() const { return mTtcIndex; } +minikin::MinikinFont* MinikinFontSkia::createFontWithVariation( + const std::vector<minikin::FontVariation>& variations) const { + SkFontMgr::FontParameters params; + + int ttcIndex; + SkStreamAsset* stream = mTypeface->openStream(&ttcIndex); + LOG_ALWAYS_FATAL_IF(stream == nullptr, "openStream failed"); + + params.setCollectionIndex(ttcIndex); + std::vector<SkFontMgr::FontParameters::Axis> skAxes; + skAxes.resize(variations.size()); + for (size_t i = 0; i < variations.size(); i++) { + skAxes[i].fTag = variations[i].axisTag; + skAxes[i].fStyleValue = SkFloatToScalar(variations[i].value); + } + params.setAxes(skAxes.data(), skAxes.size()); + sk_sp<SkFontMgr> fm(SkFontMgr::RefDefault()); + sk_sp<SkTypeface> face(fm->createFromStream(stream, params)); + + return new MinikinFontSkia(std::move(face), mFontData, mFontSize, ttcIndex); +} + uint32_t MinikinFontSkia::packPaintFlags(const SkPaint* paint) { uint32_t flags = paint->getFlags(); SkPaint::Hinting hinting = paint->getHinting(); @@ -118,8 +126,9 @@ void MinikinFontSkia::unpackPaintFlags(SkPaint* paint, uint32_t paintFlags) { paint->setHinting(static_cast<SkPaint::Hinting>(paintFlags >> 16)); } -void MinikinFontSkia::populateSkPaint(SkPaint* paint, const MinikinFont* font, FontFakery fakery) { - paint->setTypeface(reinterpret_cast<const MinikinFontSkia*>(font)->GetSkTypeface()); +void MinikinFontSkia::populateSkPaint(SkPaint* paint, const MinikinFont* font, + 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 a7c9fb0b09d4..249b0cbe44de 100644 --- a/libs/hwui/hwui/MinikinSkia.h +++ b/libs/hwui/hwui/MinikinSkia.h @@ -19,42 +19,42 @@ #include <cutils/compiler.h> #include <minikin/MinikinFont.h> +#include <SkRefCnt.h> class SkPaint; class SkTypeface; namespace android { -class ANDROID_API MinikinFontSkia : public MinikinFont { +class ANDROID_API MinikinFontSkia : public minikin::MinikinFont { public: - // Note: this takes ownership of the reference (will unref on dtor) - explicit MinikinFontSkia(SkTypeface *typeface, const void* fontData, size_t fontSize, + explicit MinikinFontSkia(sk_sp<SkTypeface> typeface, const void* fontData, size_t fontSize, int ttcIndex); - ~MinikinFontSkia(); - float GetHorizontalAdvance(uint32_t glyph_id, - const MinikinPaint &paint) const; - - void GetBounds(MinikinRect* bounds, uint32_t glyph_id, - const MinikinPaint &paint) const; + const minikin::MinikinPaint &paint) const; - const void* GetTable(uint32_t tag, size_t* size, MinikinDestroyFunc* destroy); + void GetBounds(minikin::MinikinRect* bounds, uint32_t glyph_id, + const minikin::MinikinPaint &paint) const; SkTypeface* GetSkTypeface() const; + sk_sp<SkTypeface> RefSkTypeface() const; // Access to underlying raw font bytes const void* GetFontData() const; size_t GetFontSize() const; int GetFontIndex() const; + minikin::MinikinFont* createFontWithVariation( + const std::vector<minikin::FontVariation>&) const; static uint32_t packPaintFlags(const SkPaint* paint); static void unpackPaintFlags(SkPaint* paint, uint32_t paintFlags); // set typeface and fake bold/italic parameters - static void populateSkPaint(SkPaint* paint, const MinikinFont* font, FontFakery fakery); + static void populateSkPaint(SkPaint* paint, const minikin::MinikinFont* font, + minikin::FontFakery fakery); private: - SkTypeface* mTypeface; + sk_sp<SkTypeface> mTypeface; // A raw pointer to the font data - it should be owned by some other object with // lifetime at least as long as this object. diff --git a/libs/hwui/hwui/MinikinUtils.cpp b/libs/hwui/hwui/MinikinUtils.cpp index eda94bfcd430..713e5099da26 100644 --- a/libs/hwui/hwui/MinikinUtils.cpp +++ b/libs/hwui/hwui/MinikinUtils.cpp @@ -26,17 +26,18 @@ namespace android { -FontStyle MinikinUtils::prepareMinikinPaint(MinikinPaint* minikinPaint, FontCollection** pFont, - const Paint* paint, Typeface* typeface) { +minikin::FontStyle MinikinUtils::prepareMinikinPaint(minikin::MinikinPaint* minikinPaint, + minikin::FontCollection** pFont, const Paint* paint, Typeface* typeface) { const Typeface* resolvedFace = Typeface::resolveDefault(typeface); *pFont = resolvedFace->fFontCollection; - FontStyle resolved = resolvedFace->fStyle; + minikin::FontStyle resolved = resolvedFace->fStyle; /* Prepare minikin FontStyle */ - FontVariant minikinVariant = (paint->getFontVariant() == VARIANT_ELEGANT) ? VARIANT_ELEGANT - : VARIANT_COMPACT; + minikin::FontVariant minikinVariant = (paint->getFontVariant() == minikin::VARIANT_ELEGANT) ? + minikin::VARIANT_ELEGANT : minikin::VARIANT_COMPACT; const uint32_t langListId = paint->getMinikinLangListId(); - FontStyle minikinStyle(langListId, minikinVariant, resolved.getWeight(), resolved.getItalic()); + minikin::FontStyle minikinStyle(langListId, minikinVariant, resolved.getWeight(), + resolved.getItalic()); /* Prepare minikin Paint */ // Note: it would be nice to handle fractional size values (it would improve smooth zoom @@ -46,29 +47,30 @@ FontStyle MinikinUtils::prepareMinikinPaint(MinikinPaint* minikinPaint, FontColl 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 = HyphenEdit(paint->getHyphenEdit()); + minikinPaint->hyphenEdit = minikin::HyphenEdit(paint->getHyphenEdit()); return minikinStyle; } -void MinikinUtils::doLayout(Layout* layout, const Paint* paint, int bidiFlags, +void MinikinUtils::doLayout(minikin::Layout* layout, const Paint* paint, int bidiFlags, Typeface* typeface, const uint16_t* buf, size_t start, size_t count, size_t bufSize) { - FontCollection *font; - MinikinPaint minikinPaint; - FontStyle minikinStyle = prepareMinikinPaint(&minikinPaint, &font, paint, typeface); + minikin::FontCollection *font; + minikin::MinikinPaint minikinPaint; + minikin::FontStyle minikinStyle = prepareMinikinPaint(&minikinPaint, &font, paint, typeface); layout->setFontCollection(font); layout->doLayout(buf, start, count, bufSize, bidiFlags, minikinStyle, minikinPaint); } 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) { - FontCollection *font; - MinikinPaint minikinPaint; - FontStyle minikinStyle = prepareMinikinPaint(&minikinPaint, &font, paint, typeface); - return Layout::measureText(buf, start, count, bufSize, bidiFlags, minikinStyle, minikinPaint, - font, advances); + minikin::FontCollection *font; + minikin::MinikinPaint minikinPaint; + minikin::FontStyle minikinStyle = prepareMinikinPaint(&minikinPaint, &font, paint, typeface); + return minikin::Layout::measureText(buf, start, count, bufSize, bidiFlags, minikinStyle, + minikinPaint, font, advances); } bool MinikinUtils::hasVariationSelector(Typeface* typeface, uint32_t codepoint, uint32_t vs) { @@ -76,7 +78,7 @@ bool MinikinUtils::hasVariationSelector(Typeface* typeface, uint32_t codepoint, return resolvedFace->fFontCollection->hasVariationSelector(codepoint, vs); } -float MinikinUtils::xOffsetForTextAlign(Paint* paint, const Layout& layout) { +float MinikinUtils::xOffsetForTextAlign(Paint* paint, const minikin::Layout& layout) { switch (paint->getTextAlign()) { case Paint::kCenter_Align: return layout.getAdvance() * -0.5f; @@ -90,7 +92,8 @@ float MinikinUtils::xOffsetForTextAlign(Paint* paint, const Layout& layout) { return 0; } -float MinikinUtils::hOffsetForTextAlign(Paint* paint, const Layout& layout, const SkPath& path) { +float MinikinUtils::hOffsetForTextAlign(Paint* paint, const minikin::Layout& layout, + const SkPath& path) { float align = 0; switch (paint->getTextAlign()) { case Paint::kCenter_Align: diff --git a/libs/hwui/hwui/MinikinUtils.h b/libs/hwui/hwui/MinikinUtils.h index cfaa961ac1fc..d6f64d2418d5 100644 --- a/libs/hwui/hwui/MinikinUtils.h +++ b/libs/hwui/hwui/MinikinUtils.h @@ -34,31 +34,33 @@ namespace android { class MinikinUtils { public: - ANDROID_API static FontStyle prepareMinikinPaint(MinikinPaint* minikinPaint, FontCollection** pFont, - const Paint* paint, Typeface* typeface); + ANDROID_API static minikin::FontStyle prepareMinikinPaint(minikin::MinikinPaint* minikinPaint, + minikin::FontCollection** pFont, const Paint* paint, Typeface* typeface); - ANDROID_API static void doLayout(Layout* layout, const Paint* paint, int bidiFlags, + ANDROID_API static void doLayout(minikin::Layout* layout, const Paint* paint, int bidiFlags, Typeface* typeface, const uint16_t* buf, size_t start, size_t count, size_t bufSize); 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 bool hasVariationSelector(Typeface* typeface, uint32_t codepoint, uint32_t vs); + ANDROID_API static bool hasVariationSelector(Typeface* typeface, uint32_t codepoint, + uint32_t vs); - ANDROID_API static float xOffsetForTextAlign(Paint* paint, const Layout& layout); + ANDROID_API static float xOffsetForTextAlign(Paint* paint, const minikin::Layout& layout); - ANDROID_API static float hOffsetForTextAlign(Paint* paint, const Layout& layout, const SkPath& path); + ANDROID_API static float hOffsetForTextAlign(Paint* paint, const minikin::Layout& layout, + 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 Layout& layout, Paint* paint, F& f) { + ANDROID_API static void forFontRun(const minikin::Layout& layout, Paint* paint, F& f) { float saveSkewX = paint->getTextSkewX(); bool savefakeBold = paint->isFakeBoldText(); - MinikinFont* curFont = NULL; + minikin::MinikinFont* curFont = NULL; size_t start = 0; size_t nGlyphs = layout.nGlyphs(); for (size_t i = 0; i < nGlyphs; i++) { - MinikinFont* nextFont = layout.getFont(i); + minikin::MinikinFont* nextFont = layout.getFont(i); if (i > 0 && nextFont != curFont) { MinikinFontSkia::populateSkPaint(paint, curFont, layout.getFakery(start)); f(start, i); diff --git a/libs/hwui/hwui/Paint.h b/libs/hwui/hwui/Paint.h index f72a6321643a..c9b5f0031a7b 100644 --- a/libs/hwui/hwui/Paint.h +++ b/libs/hwui/hwui/Paint.h @@ -48,6 +48,14 @@ public: return mLetterSpacing; } + void setWordSpacing(float wordSpacing) { + mWordSpacing = wordSpacing; + } + + float getWordSpacing() const { + return mWordSpacing; + } + void setFontFeatureSettings(const std::string& fontFeatureSettings) { mFontFeatureSettings = fontFeatureSettings; } @@ -64,11 +72,11 @@ public: return mMinikinLangListId; } - void setFontVariant(FontVariant variant) { + void setFontVariant(minikin::FontVariant variant) { mFontVariant = variant; } - FontVariant getFontVariant() const { + minikin::FontVariant getFontVariant() const { return mFontVariant; } @@ -82,9 +90,10 @@ public: private: float mLetterSpacing = 0; + float mWordSpacing = 0; std::string mFontFeatureSettings; uint32_t mMinikinLangListId; - FontVariant mFontVariant; + minikin::FontVariant mFontVariant; uint32_t mHyphenEdit = 0; }; diff --git a/libs/hwui/hwui/PaintImpl.cpp b/libs/hwui/hwui/PaintImpl.cpp index b27672ce16a0..67427433bb89 100644 --- a/libs/hwui/hwui/PaintImpl.cpp +++ b/libs/hwui/hwui/PaintImpl.cpp @@ -19,19 +19,20 @@ namespace android { Paint::Paint() : - SkPaint(), mLetterSpacing(0), mFontFeatureSettings(), mMinikinLangListId(0), - mFontVariant(VARIANT_DEFAULT) { + SkPaint(), mLetterSpacing(0), mWordSpacing(0), mFontFeatureSettings(), + mMinikinLangListId(0), mFontVariant(minikin::VARIANT_DEFAULT) { } Paint::Paint(const Paint& paint) : SkPaint(paint), - mLetterSpacing(paint.mLetterSpacing), mFontFeatureSettings(paint.mFontFeatureSettings), + mLetterSpacing(paint.mLetterSpacing), mWordSpacing(paint.mWordSpacing), + mFontFeatureSettings(paint.mFontFeatureSettings), mMinikinLangListId(paint.mMinikinLangListId), mFontVariant(paint.mFontVariant), mHyphenEdit(paint.mHyphenEdit) { } Paint::Paint(const SkPaint& paint) : SkPaint(paint), - mLetterSpacing(0), mFontFeatureSettings(), mMinikinLangListId(0), - mFontVariant(VARIANT_DEFAULT) { + mLetterSpacing(0), mWordSpacing(0), mFontFeatureSettings(), mMinikinLangListId(0), + mFontVariant(minikin::VARIANT_DEFAULT) { } Paint::~Paint() { @@ -40,6 +41,7 @@ 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; @@ -50,6 +52,7 @@ Paint& Paint::operator=(const Paint& other) { 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 diff --git a/libs/hwui/hwui/Typeface.cpp b/libs/hwui/hwui/Typeface.cpp index 0b46c09936c3..b69b0cb29efe 100644 --- a/libs/hwui/hwui/Typeface.cpp +++ b/libs/hwui/hwui/Typeface.cpp @@ -23,10 +23,14 @@ #include "Typeface.h" #include <pthread.h> +#include <fcntl.h> // For tests. +#include <sys/stat.h> // For tests. +#include <sys/mman.h> // For tests. #include "MinikinSkia.h" #include "SkTypeface.h" #include "SkPaint.h" +#include "SkStream.h" // Fot tests. #include <minikin/FontCollection.h> #include <minikin/FontFamily.h> @@ -45,72 +49,20 @@ static void resolveStyle(Typeface* typeface) { weight = 9; } bool italic = (typeface->fSkiaStyle & SkTypeface::kItalic) != 0; - typeface->fStyle = FontStyle(weight, italic); + typeface->fStyle = minikin::FontStyle(weight, italic); } Typeface* gDefaultTypeface = NULL; -pthread_once_t gDefaultTypefaceOnce = PTHREAD_ONCE_INIT; - -// This installs a default typeface (from a hardcoded path) that allows -// layouts to work (not crash on null pointer) before the default -// typeface is set. -// TODO: investigate why layouts are being created before Typeface.java -// class initialization. -static FontCollection *makeFontCollection() { - std::vector<FontFamily *>typefaces; - const char *fns[] = { - "/system/fonts/Roboto-Regular.ttf", - }; - - FontFamily *family = new FontFamily(); - for (size_t i = 0; i < sizeof(fns)/sizeof(fns[0]); i++) { - const char *fn = fns[i]; - ALOGD("makeFontCollection adding %s", fn); - SkTypeface *skFace = SkTypeface::CreateFromFile(fn); - if (skFace != NULL) { - // TODO: might be a nice optimization to get access to the underlying font - // data, but would require us opening the file ourselves and passing that - // to the appropriate Create method of SkTypeface. - MinikinFont *font = new MinikinFontSkia(skFace, NULL, 0, 0); - family->addFont(font); - font->Unref(); - } else { - ALOGE("failed to create font %s", fn); - } - } - typefaces.push_back(family); - - FontCollection *result = new FontCollection(typefaces); - family->Unref(); - return result; -} - -static void getDefaultTypefaceOnce() { - Layout::init(); - if (gDefaultTypeface == NULL) { - // We expect the client to set a default typeface, but provide a - // default so we can make progress before that happens. - gDefaultTypeface = new Typeface; - gDefaultTypeface->fFontCollection = makeFontCollection(); - gDefaultTypeface->fSkiaStyle = SkTypeface::kNormal; - gDefaultTypeface->fBaseWeight = 400; - resolveStyle(gDefaultTypeface); - } -} Typeface* Typeface::resolveDefault(Typeface* src) { - if (src == NULL) { - pthread_once(&gDefaultTypefaceOnce, getDefaultTypefaceOnce); - return gDefaultTypeface; - } else { - return src; - } + LOG_ALWAYS_FATAL_IF(gDefaultTypeface == nullptr); + return src == nullptr ? gDefaultTypeface : src; } Typeface* Typeface::createFromTypeface(Typeface* src, SkTypeface::Style style) { Typeface* resolvedFace = Typeface::resolveDefault(src); Typeface* result = new Typeface; - if (result != 0) { + if (result != nullptr) { result->fFontCollection = resolvedFace->fFontCollection; result->fFontCollection->Ref(); result->fSkiaStyle = style; @@ -120,10 +72,30 @@ Typeface* Typeface::createFromTypeface(Typeface* src, SkTypeface::Style style) { return result; } +Typeface* Typeface::createFromTypefaceWithVariation(Typeface* src, + const std::vector<minikin::FontVariation>& variations) { + Typeface* resolvedFace = Typeface::resolveDefault(src); + Typeface* result = new Typeface(); + if (result != nullptr) { + result->fFontCollection = + resolvedFace->fFontCollection->createCollectionWithVariation(variations); + if (result->fFontCollection == nullptr) { + // None of passed axes are supported by this collection. + // So we will reuse the same collection with incrementing reference count. + result->fFontCollection = resolvedFace->fFontCollection; + result->fFontCollection->Ref(); + } + result->fSkiaStyle = resolvedFace->fSkiaStyle; + result->fBaseWeight = resolvedFace->fBaseWeight; + resolveStyle(result); + } + return result; +} + Typeface* Typeface::createWeightAlias(Typeface* src, int weight) { Typeface* resolvedFace = Typeface::resolveDefault(src); Typeface* result = new Typeface; - if (result != 0) { + if (result != nullptr) { result->fFontCollection = resolvedFace->fFontCollection; result->fFontCollection->Ref(); result->fSkiaStyle = resolvedFace->fSkiaStyle; @@ -133,16 +105,16 @@ Typeface* Typeface::createWeightAlias(Typeface* src, int weight) { return result; } -Typeface* Typeface::createFromFamilies(const std::vector<FontFamily*>& families) { +Typeface* Typeface::createFromFamilies(const std::vector<minikin::FontFamily*>& families) { Typeface* result = new Typeface; - result->fFontCollection = new FontCollection(families); + result->fFontCollection = new minikin::FontCollection(families); if (families.empty()) { ALOGW("createFromFamilies creating empty collection"); result->fSkiaStyle = SkTypeface::kNormal; } else { - const FontStyle defaultStyle; - FontFamily* firstFamily = reinterpret_cast<FontFamily*>(families[0]); - MinikinFont* mf = firstFamily->getClosestMatch(defaultStyle).font; + const minikin::FontStyle defaultStyle; + minikin::FontFamily* firstFamily = reinterpret_cast<minikin::FontFamily*>(families[0]); + minikin::MinikinFont* mf = firstFamily->getClosestMatch(defaultStyle).font; if (mf != NULL) { SkTypeface* skTypeface = reinterpret_cast<MinikinFontSkia*>(mf)->GetSkTypeface(); // TODO: probably better to query more precise style from family, will be important @@ -166,4 +138,34 @@ void Typeface::setDefault(Typeface* face) { gDefaultTypeface = face; } +void Typeface::setRobotoTypefaceForTest() { + const char* kRobotoFont = "/system/fonts/Roboto-Regular.ttf"; + + int fd = open(kRobotoFont, O_RDONLY); + LOG_ALWAYS_FATAL_IF(fd == -1, "Failed to open file %s", kRobotoFont); + struct stat st = {}; + LOG_ALWAYS_FATAL_IF(fstat(fd, &st) == -1, "Failed to stat file %s", kRobotoFont); + void* data = mmap(nullptr, st.st_size, PROT_READ, MAP_SHARED, fd, 0); + std::unique_ptr<SkMemoryStream> fontData(new SkMemoryStream(data, st.st_size)); + sk_sp<SkTypeface> typeface = SkTypeface::MakeFromStream(fontData.release()); + LOG_ALWAYS_FATAL_IF(typeface == nullptr, "Failed to make typeface from %s", kRobotoFont); + + minikin::MinikinFont* font = new MinikinFontSkia(std::move(typeface), data, st.st_size, 0); + minikin::FontFamily* family = new minikin::FontFamily( + std::vector<minikin::Font>({ minikin::Font(font, minikin::FontStyle()) })); + font->Unref(); + + std::vector<minikin::FontFamily*> typefaces = { family }; + minikin::FontCollection *collection = new minikin::FontCollection(typefaces); + family->Unref(); + + Typeface* hwTypeface = new Typeface(); + hwTypeface->fFontCollection = collection; + hwTypeface->fSkiaStyle = SkTypeface::kNormal; + hwTypeface->fBaseWeight = 400; + hwTypeface->fStyle = minikin::FontStyle(4 /* weight */, false /* italic */); + + Typeface::setDefault(hwTypeface); +} + } diff --git a/libs/hwui/hwui/Typeface.h b/libs/hwui/hwui/Typeface.h index 8862e5a5a911..4392ebc36bad 100644 --- a/libs/hwui/hwui/Typeface.h +++ b/libs/hwui/hwui/Typeface.h @@ -27,7 +27,7 @@ namespace android { struct ANDROID_API Typeface { - FontCollection *fFontCollection; + minikin::FontCollection *fFontCollection; // style used for constructing and querying Typeface objects SkTypeface::Style fSkiaStyle; @@ -35,7 +35,7 @@ struct ANDROID_API Typeface { int fBaseWeight; // resolved style actually used for rendering - FontStyle fStyle; + minikin::FontStyle fStyle; void unref(); @@ -43,11 +43,17 @@ struct ANDROID_API Typeface { static Typeface* createFromTypeface(Typeface* src, SkTypeface::Style style); + static Typeface* createFromTypefaceWithVariation(Typeface* src, + const std::vector<minikin::FontVariation>& variations); + static Typeface* createWeightAlias(Typeface* src, int baseweight); - static Typeface* createFromFamilies(const std::vector<FontFamily*>& families); + static Typeface* createFromFamilies(const std::vector<minikin::FontFamily*>& families); static void setDefault(Typeface* face); + + // Sets roboto font as the default typeface for testing purpose. + static void setRobotoTypefaceForTest(); }; } diff --git a/libs/hwui/hwui_static_deps.mk b/libs/hwui/hwui_static_deps.mk index dca78b38e942..37126a69f701 100644 --- a/libs/hwui/hwui_static_deps.mk +++ b/libs/hwui/hwui_static_deps.mk @@ -18,6 +18,7 @@ LOCAL_SHARED_LIBRARIES += \ libutils \ libEGL \ libGLESv2 \ + libvulkan \ libskia \ libui \ libgui \ diff --git a/libs/hwui/pipeline/skia/AnimatedDrawables.h b/libs/hwui/pipeline/skia/AnimatedDrawables.h new file mode 100644 index 000000000000..44c494f77231 --- /dev/null +++ b/libs/hwui/pipeline/skia/AnimatedDrawables.h @@ -0,0 +1,90 @@ +/* + * 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. + */ + +#pragma once + +#include "CanvasProperty.h" +#include <utils/RefBase.h> +#include <SkCanvas.h> +#include <SkDrawable.h> + +namespace android { +namespace uirenderer { +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) {} + +protected: + virtual SkRect onGetBounds() override { + return SkRect::MakeLTRB(mLeft->value, mTop->value, mRight->value, mBottom->value); + } + virtual void onDraw(SkCanvas* canvas) override { + SkRect rect = SkRect::MakeLTRB(mLeft->value, mTop->value, mRight->value, mBottom->value); + canvas->drawRoundRect(rect, mRx->value, mRy->value, mPaint->value); + } + +private: + sp<uirenderer::CanvasPropertyPrimitive> mLeft; + sp<uirenderer::CanvasPropertyPrimitive> mTop; + sp<uirenderer::CanvasPropertyPrimitive> mRight; + sp<uirenderer::CanvasPropertyPrimitive> mBottom; + sp<uirenderer::CanvasPropertyPrimitive> mRx; + sp<uirenderer::CanvasPropertyPrimitive> mRy; + sp<uirenderer::CanvasPropertyPaint> mPaint; +}; + +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) {} + +protected: + virtual SkRect onGetBounds() override { + const float x = mX->value; + const float y = mY->value; + const float radius = mRadius->value; + return SkRect::MakeLTRB(x - radius, y - radius, x + radius, y + radius); + } + virtual void onDraw(SkCanvas* canvas) override { + canvas->drawCircle(mX->value, mY->value, mRadius->value, mPaint->value); + } + +private: + sp<uirenderer::CanvasPropertyPrimitive> mX; + sp<uirenderer::CanvasPropertyPrimitive> mY; + sp<uirenderer::CanvasPropertyPrimitive> mRadius; + sp<uirenderer::CanvasPropertyPaint> mPaint; +}; + +}; // namespace skiapipeline +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp new file mode 100644 index 000000000000..419c8a99fe74 --- /dev/null +++ b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp @@ -0,0 +1,118 @@ +/* + * 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 "GLFunctorDrawable.h" +#include "GlFunctorLifecycleListener.h" +#include "RenderNode.h" +#include "SkClipStack.h" +#include <private/hwui/DrawGlInfo.h> +#include <SkPath.h> +#include <GrContext.h> + +namespace android { +namespace uirenderer { +namespace skiapipeline { + +GLFunctorDrawable::~GLFunctorDrawable() { + if(mListener.get() != nullptr) { + mListener->onGlFunctorReleased(mFunctor); + } +} + +void GLFunctorDrawable::syncFunctor() const { + (*mFunctor)(DrawGlInfo::kModeSync, nullptr); +} + +static void setScissor(int viewportHeight, const SkIRect& clip) { + SkASSERT(!clip.isEmpty()); + // transform to Y-flipped GL space, and prevent negatives + GLint y = viewportHeight - clip.fBottom; + GLint height = (viewportHeight - clip.fTop) - y; + glScissor(clip.fLeft, y, clip.width(), height); +} + +void GLFunctorDrawable::onDraw(SkCanvas* canvas) { + if (canvas->getGrContext() == nullptr) { + SkDEBUGF(("Attempting to draw GLFunctor into an unsupported surface")); + return; + } + + canvas->flush(); + + if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) { + canvas->clear(SK_ColorRED); + return; + } + + SkImageInfo canvasInfo = canvas->imageInfo(); + SkMatrix44 mat4(canvas->getTotalMatrix()); + + SkIRect ibounds; + canvas->getClipDeviceBounds(&ibounds); + + DrawGlInfo info; + info.clipLeft = ibounds.fLeft; + info.clipTop = ibounds.fTop; + info.clipRight = ibounds.fRight; + info.clipBottom = ibounds.fBottom; + // info.isLayer = hasLayer(); + info.isLayer = false; + info.width = canvasInfo.width(); + info.height = canvasInfo.height(); + mat4.asColMajorf(&info.transform[0]); + + //apply a simple clip with a scissor or a complex clip with a stencil + SkRegion clipRegion; + SkPath path; + canvas->getClipStack()->asPath(&path); + clipRegion.setPath(path, SkRegion(ibounds)); + 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); + 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(); + } + glDisable(GL_SCISSOR_TEST); + glStencilFunc(GL_EQUAL, 0x1, 0xff); + glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + glEnable(GL_STENCIL_TEST); + } else if (clipRegion.isEmpty()) { + glDisable(GL_STENCIL_TEST); + glDisable(GL_SCISSOR_TEST); + } else { + glDisable(GL_STENCIL_TEST); + glEnable(GL_SCISSOR_TEST); + setScissor(info.height, clipRegion.getBounds()); + } + + (*mFunctor)(DrawGlInfo::kModeDraw, &info); + + canvas->getGrContext()->resetContext(); + } + +}; // namespace skiapipeline +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/pipeline/skia/GLFunctorDrawable.h b/libs/hwui/pipeline/skia/GLFunctorDrawable.h new file mode 100644 index 000000000000..bf39dadbfcc5 --- /dev/null +++ b/libs/hwui/pipeline/skia/GLFunctorDrawable.h @@ -0,0 +1,59 @@ +/* + * 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. + */ + +#pragma once + +#include <SkCanvas.h> +#include <SkDrawable.h> + +#include <utils/RefBase.h> +#include <utils/Functor.h> + +namespace android { +namespace uirenderer { + +class GlFunctorLifecycleListener; + +namespace skiapipeline { + +/** + * This drawable wraps a OpenGL functor enabling it to be recorded into a list + * of Skia drawing commands. + */ +class GLFunctorDrawable : public SkDrawable { +public: + GLFunctorDrawable(Functor* functor, GlFunctorLifecycleListener* listener, SkCanvas* canvas) + : mFunctor(functor) + , mListener(listener) { + canvas->getClipBounds(&mBounds); + } + virtual ~GLFunctorDrawable(); + + void syncFunctor() const; + + protected: + virtual SkRect onGetBounds() override { return mBounds; } + virtual void onDraw(SkCanvas* canvas) override; + + private: + Functor* mFunctor; + sp<GlFunctorLifecycleListener> mListener; + SkRect mBounds; +}; + +}; // namespace skiapipeline +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/pipeline/skia/LayerDrawable.cpp b/libs/hwui/pipeline/skia/LayerDrawable.cpp new file mode 100644 index 000000000000..2ebfbcc1f18e --- /dev/null +++ b/libs/hwui/pipeline/skia/LayerDrawable.cpp @@ -0,0 +1,80 @@ +/* + * 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 "GlLayer.h" +#include "LayerDrawable.h" +#include "VkLayer.h" + +#include "SkColorFilter.h" +#include "SkSurface.h" +#include "gl/GrGLTypes.h" + +namespace android { +namespace uirenderer { +namespace skiapipeline { + +void LayerDrawable::onDraw(SkCanvas* canvas) { + DrawLayer(canvas->getGrContext(), canvas, mLayer.get()); +} + +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); + } + + sk_sp<SkImage> layerImage; + if (layer->getApi() == Layer::Api::OpenGL) { + GlLayer* glLayer = static_cast<GlLayer*>(layer); + GrGLTextureInfo externalTexture; + externalTexture.fTarget = glLayer->getRenderTarget(); + externalTexture.fID = glLayer->getTextureId(); + GrBackendTextureDesc textureDescription; + textureDescription.fWidth = glLayer->getWidth(); + textureDescription.fHeight = glLayer->getHeight(); + textureDescription.fConfig = kRGBA_8888_GrPixelConfig; + textureDescription.fOrigin = kTopLeft_GrSurfaceOrigin; + textureDescription.fTextureHandle = reinterpret_cast<GrBackendObject>(&externalTexture); + layerImage = SkImage::MakeFromTexture(context, textureDescription); + } else { + SkASSERT(layer->getApi() == Layer::Api::Vulkan); + VkLayer* vkLayer = static_cast<VkLayer*>(layer); + canvas->clear(SK_ColorGREEN); + layerImage = vkLayer->getImage(); + } + + if (layerImage) { + 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); + } + + return layerImage; +} + +}; // namespace skiapipeline +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/pipeline/skia/LayerDrawable.h b/libs/hwui/pipeline/skia/LayerDrawable.h new file mode 100644 index 000000000000..431989519a70 --- /dev/null +++ b/libs/hwui/pipeline/skia/LayerDrawable.h @@ -0,0 +1,49 @@ +/* + * 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. + */ + +#pragma once + +#include "Layer.h" + +#include <SkCanvas.h> +#include <SkDrawable.h> + +namespace android { +namespace uirenderer { +namespace skiapipeline { + +/* + * Draws a layer backed by an OpenGL texture into a SkCanvas. + */ +class LayerDrawable : public SkDrawable { + public: + explicit LayerDrawable(Layer* layer) + : mLayer(layer) {} + + 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; + +private: + sp<Layer> mLayer; +}; + +}; // namespace skiapipeline +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp new file mode 100644 index 000000000000..117395bfc75c --- /dev/null +++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp @@ -0,0 +1,279 @@ +/* + * 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 "RenderNodeDrawable.h" +#include "RenderNode.h" +#include "SkiaDisplayList.h" +#include "SkiaPipeline.h" +#include "utils/TraceUtils.h" + +namespace android { +namespace uirenderer { +namespace skiapipeline { + +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 + 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. + 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 + if (0 == nestLevel || !displayList.containsProjectionReceiver()) { + SkAutoCanvasRestore acr(canvas, true); + SkMatrix nodeMatrix; + mat4 hwuiMatrix(child.getRecordedMatrix()); + auto childNode = child.getRenderNode(); + childNode->applyViewPropertyTransforms(hwuiMatrix); + hwuiMatrix.copyTo(nodeMatrix); + canvas->concat(nodeMatrix); + SkiaDisplayList* childDisplayList = static_cast<SkiaDisplayList*>( + (const_cast<DisplayList*>(childNode->getDisplayList()))); + if (childDisplayList) { + drawBackwardsProjectedNodes(canvas, *childDisplayList, nestLevel+1); + } + } + } +} + +static void clipOutline(const Outline& outline, SkCanvas* canvas, const SkRect* pendingClip) { + Rect possibleRect; + float radius; + LOG_ALWAYS_FATAL_IF(!outline.getAsRoundRect(&possibleRect, &radius), + "clipping outlines should be at most roundedRects"); + SkRect rect = possibleRect.toSkRect(); + if (radius != 0.0f) { + if (pendingClip && !pendingClip->contains(rect)) { + canvas->clipRect(*pendingClip); + } + canvas->clipRRect(SkRRect::MakeRectXY(rect, radius, radius), SkClipOp::kIntersect, true); + } else { + if (pendingClip) { + (void)rect.intersect(*pendingClip); + } + canvas->clipRect(rect); + } +} + +const RenderProperties& RenderNodeDrawable::getNodeProperties() const { + return mRenderNode->properties(); +} + +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 + if ((!mInReorderingSection) || MathUtils::isZero(mRenderNode->properties().getZ())) { + this->forceDraw(canvas); + } +} + +void RenderNodeDrawable::forceDraw(SkCanvas* canvas) { + RenderNode* renderNode = mRenderNode.get(); + if (SkiaPipeline::skpCaptureEnabled()) { + SkRect dimensions = SkRect::MakeWH(renderNode->getWidth(), renderNode->getHeight()); + canvas->drawAnnotation(dimensions, renderNode->getName(), nullptr); + } + + // 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)) { + return; + } + + SkASSERT(renderNode->getDisplayList()->isSkiaDL()); + SkiaDisplayList* displayList = (SkiaDisplayList*)renderNode->getDisplayList(); + + 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; + if (!properties.getProjectBackwards()) { + drawContent(canvas); + if (mProjectedDisplayList) { + acr.restore(); //draw projected children using parent matrix + LOG_ALWAYS_FATAL_IF(!mProjectedDisplayList->mProjectedOutline); + const bool shouldClip = mProjectedDisplayList->mProjectedOutline->getPath(); + SkAutoCanvasRestore acr2(canvas, shouldClip); + canvas->setMatrix(mProjectedDisplayList->mProjectedReceiverParentMatrix); + if (shouldClip) { + clipOutline(*mProjectedDisplayList->mProjectedOutline, canvas, nullptr); + } + drawBackwardsProjectedNodes(canvas, *mProjectedDisplayList); + } + } + 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) { + paint->setAlpha(properties.alpha() * alphaMultiplier); + paint->setBlendMode(properties.xferMode()); + paint->setColorFilter(sk_ref_sp(properties.colorFilter())); + return true; + } + return false; +} + +void RenderNodeDrawable::drawContent(SkCanvas* canvas) const { + RenderNode* renderNode = mRenderNode.get(); + float alphaMultiplier = 1.0f; + const RenderProperties& properties = renderNode->properties(); + + // If we are drawing the contents of layer, we don't want to apply any of + // the RenderNode's properties during this pass. Those will all be applied + // when the layer is composited. + if (mComposeLayer) { + setViewProperties(properties, canvas, &alphaMultiplier); + } + SkiaDisplayList* displayList = (SkiaDisplayList*)mRenderNode->getDisplayList(); + if (displayList->containsProjectionReceiver()) { + displayList->mProjectedReceiverParentMatrix = canvas->getTotalMatrix(); + } + + //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) { + SkiaDisplayList* displayList = (SkiaDisplayList*)renderNode->getDisplayList(); + const LayerProperties& layerProperties = properties.layerProperties(); + // 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); + + if (!renderNode->getSkiaLayer()->hasRenderedSinceRepaint) { + renderNode->getSkiaLayer()->hasRenderedSinceRepaint = true; + if (CC_UNLIKELY(Properties::debugLayersUpdates)) { + SkPaint layerPaint; + layerPaint.setColor(0x7f00ff00); + canvas->drawRect(bounds, layerPaint); + } else if (CC_UNLIKELY(Properties::debugOverdraw)) { + // Render transparent rect to increment overdraw for repaint area. + // This can be "else if" because flashing green on layer updates + // will also increment the overdraw if it happens to be turned on. + SkPaint transparentPaint; + transparentPaint.setColor(SK_ColorTRANSPARENT); + canvas->drawRect(bounds, transparentPaint); + } + } + + // composing a software layer with alpha + } else if (properties.effectiveLayerType() == LayerType::Software) { + SkPaint paint; + bool needsLayer = layerNeedsPaint(layerProperties, alphaMultiplier, &paint); + if (needsLayer) { + canvas->saveLayer(bounds, &paint); + } + canvas->drawDrawable(displayList->mDrawable.get()); + if (needsLayer) { + canvas->restore(); + } + } else { + canvas->drawDrawable(displayList->mDrawable.get()); + } + } +} + +void RenderNodeDrawable::setViewProperties(const RenderProperties& properties, SkCanvas* canvas, + float* alphaMultiplier) { + if (properties.getLeft() != 0 || properties.getTop() != 0) { + canvas->translate(properties.getLeft(), properties.getTop()); + } + if (properties.getStaticMatrix()) { + canvas->concat(*properties.getStaticMatrix()); + } else if (properties.getAnimationMatrix()) { + canvas->concat(*properties.getAnimationMatrix()); + } + if (properties.hasTransformMatrix()) { + if (properties.isTransformTranslateOnly()) { + canvas->translate(properties.getTranslationX(), properties.getTranslationY()); + } else { + canvas->concat(*properties.getTransformMatrix()); + } + } + 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 + } + if (CC_LIKELY(isLayer || !properties.getHasOverlappingRendering())) { + *alphaMultiplier = properties.getAlpha(); + } else { + // savelayer needed to create an offscreen buffer + Rect layerBounds(0, 0, properties.getWidth(), properties.getHeight()); + if (clipFlags) { + properties.getClippingRectForFlags(clipFlags, &layerBounds); + 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)); + } + + 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()); + } + } + + const SkRect* pendingClip = nullptr; + SkRect clipRect; + + if (clipFlags) { + Rect tmpRect; + properties.getClippingRectForFlags(clipFlags, &tmpRect); + clipRect = tmpRect.toSkRect(); + pendingClip = &clipRect; + } + + if (properties.getRevealClip().willClip()) { + canvas->clipPath(*properties.getRevealClip().getPath(), SkClipOp::kIntersect, true); + } else if (properties.getOutline().willClip()) { + clipOutline(properties.getOutline(), canvas, pendingClip); + pendingClip = nullptr; + } + + if (pendingClip) { + canvas->clipRect(*pendingClip); + } +} + +}; // namespace skiapipeline +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.h b/libs/hwui/pipeline/skia/RenderNodeDrawable.h new file mode 100644 index 000000000000..3eed6476c994 --- /dev/null +++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.h @@ -0,0 +1,155 @@ +/* + * 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. + */ + +#pragma once + +#include <SkCanvas.h> +#include <SkDrawable.h> +#include <SkMatrix.h> +#include <utils/RefBase.h> + +namespace android { +namespace uirenderer { + +class RenderNode; +class RenderProperties; + +namespace skiapipeline { + +class SkiaDisplayList; + +/** + * This drawable wraps a RenderNode and enables it to be recorded into a list + * of Skia drawing commands. + */ +class RenderNodeDrawable : public SkDrawable { +public: + /** + * Creates a new RenderNodeDrawable backed by a render node. + * + * @param node that has to be drawn + * @param canvas is a recording canvas used to extract its matrix + * @param composeLayer if the node's layer type is RenderLayer this flag determines whether + * we should draw into the contents of the layer or compose the existing contents of the + * layer into the canvas. + */ + explicit RenderNodeDrawable(RenderNode* node, SkCanvas* canvas, bool composeLayer = true, + bool inReorderingSection = false) + : mRenderNode(node) + , mRecordedTransform(canvas->getTotalMatrix()) + , mComposeLayer(composeLayer) + , mInReorderingSection(inReorderingSection) {} + + /** + * Draws into the canvas this render node and its children. If the node is marked as a + * projection receiver then all projected children (excluding direct children) will be drawn + * last. Any projected node not matching those requirements will not be drawn by this function. + */ + void forceDraw(SkCanvas* canvas); + + /** + * Returns readonly render properties for this render node. + */ + const RenderProperties& getNodeProperties() const; + + /** + * The renderNode (and its properties) that is to be drawn + */ + RenderNode* getRenderNode() const { return mRenderNode.get(); } + + /** + * Returns the transform on the canvas at time of recording and is used for + * computing total transform without rerunning DL contents. + */ + const SkMatrix& getRecordedMatrix() const { return mRecordedTransform; } + + /** + * Sets a pointer to a display list of the parent render node. The display list is used when + * drawing backward projected nodes, when this node is a projection receiver. + */ + void setProjectedDisplayList(SkiaDisplayList* projectedDisplayList) { + mProjectedDisplayList = projectedDisplayList; + } + +protected: + /* + * Return the (conservative) bounds of what the drawable will draw. + */ + virtual SkRect onGetBounds() override { + // We don't want to enable a record time quick reject because the properties + // of the RenderNode may be updated on subsequent frames. + return SkRect::MakeLargest(); + } + /** + * This function draws into a canvas as forceDraw, but does nothing if the render node has a + * non-zero elevation. + */ + virtual void onDraw(SkCanvas* canvas) override; + +private: + /* + * Render node that is wrapped by this class. + */ + sp<RenderNode> mRenderNode; + + /** + * Walks recursively the display list and draws the content of backward projected nodes. + * + * @param canvas used to draw the backward projected nodes + * @param displayList is a display list that contains a projection receiver + * @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); + + /** + * Applies the rendering properties of a view onto a SkCanvas. + */ + static void setViewProperties(const RenderProperties& properties, SkCanvas* canvas, + float* alphaMultiplier); + + /** + * Stores transform on the canvas at time of recording and is used for + * computing total transform without rerunning DL contents. + */ + const SkMatrix mRecordedTransform; + + /** + * If mRenderNode's layer type is RenderLayer this flag determines whether we + * should draw into the contents of the layer or compose the existing contents + * of the layer into the canvas. + */ + const bool mComposeLayer; + + /* + * True if the render node is in a reordering section + */ + bool mInReorderingSection; + + /* + * Draw the content into a canvas, depending on the render node layer type and mComposeLayer. + */ + void drawContent(SkCanvas* canvas) const; + + /* + * display list that is searched for any render nodes with getProjectBackwards==true + */ + SkiaDisplayList* mProjectedDisplayList = nullptr; +}; + +}; // namespace skiapipeline +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp new file mode 100644 index 000000000000..d05e7f660c72 --- /dev/null +++ b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp @@ -0,0 +1,708 @@ +/* + * 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 "ReorderBarrierDrawables.h" +#include "RenderNode.h" +#include "SkiaDisplayList.h" +#include "SkiaPipeline.h" + +#include <SkBlurMask.h> +#include <SkBlurMaskFilter.h> +#include <SkGaussianEdgeShader.h> +#include <SkPathOps.h> +#include <SkRRectsGaussianEdgeMaskFilter.h> + +namespace android { +namespace uirenderer { +namespace skiapipeline { + +StartReorderBarrierDrawable::StartReorderBarrierDrawable(SkiaDisplayList* 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.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; + }); + + SkASSERT(!mChildren.empty()); + + size_t drawIndex = 0; + const size_t endIndex = mChildren.size(); + while (drawIndex < endIndex) { + RenderNodeDrawable* childNode = mChildren[drawIndex]; + SkASSERT(childNode); + const float casterZ = childNode->getNodeProperties().getZ(); + if (casterZ >= -NON_ZERO_EPSILON) { //draw only children with negative Z + return; + } + childNode->forceDraw(canvas); + drawIndex++; + } +} + +EndReorderBarrierDrawable::EndReorderBarrierDrawable(StartReorderBarrierDrawable* startBarrier) + : mStartBarrier(startBarrier) { + mStartBarrier->mEndChildIndex = mStartBarrier->mDisplayList->mChildNodes.size() - 1; +} + +#define SHADOW_DELTA 0.1f + +void EndReorderBarrierDrawable::onDraw(SkCanvas* canvas) { + auto& zChildren = mStartBarrier->mChildren; + SkASSERT(!zChildren.empty()); + + /** + * Draw shadows and (potential) casters mostly in order, but allow the shadows of casters + * with very similar Z heights to draw together. + * + * This way, if Views A & B have the same Z height and are both casting shadows, the shadows are + * underneath both, and neither's shadow is drawn on top of the other. + */ + 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++; + size_t shadowIndex = drawIndex; + + float lastCasterZ = 0.0f; + while (shadowIndex < endIndex || drawIndex < endIndex) { + if (shadowIndex < endIndex) { + const float casterZ = zChildren[shadowIndex]->getNodeProperties().getZ(); + + // attempt to render the shadow if the caster about to be drawn is its caster, + // 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 + shadowIndex++; + continue; + } + } + + RenderNodeDrawable* childNode = zChildren[drawIndex]; + SkASSERT(childNode); + childNode->forceDraw(canvas); + + drawIndex++; + } +} + +/** + * @param canvas the destination for the shadow draws + * @param shape the shape casting the shadow + * @param casterZValue the Z value of the caster RRect + * @param ambientAlpha the maximum alpha value to use when drawing the ambient shadow + * @param draw the function used to draw 'shape' + */ +template <typename Shape, typename F> +static void DrawAmbientShadowGeneral(SkCanvas* canvas, const Shape& shape, float casterZValue, + float ambientAlpha, F&& draw) { + if (ambientAlpha <= 0) { + return; + } + + const float kHeightFactor = 1.f/128.f; + const float kGeomFactor = 64; + + float umbraAlpha = 1 / (1 + SkMaxScalar(casterZValue*kHeightFactor, 0)); + float radius = casterZValue*kHeightFactor*kGeomFactor; + + sk_sp<SkMaskFilter> mf = SkBlurMaskFilter::Make(kNormal_SkBlurStyle, + SkBlurMask::ConvertRadiusToSigma(radius), SkBlurMaskFilter::kNone_BlurFlag); + SkPaint paint; + paint.setAntiAlias(true); + paint.setMaskFilter(std::move(mf)); + paint.setARGB(ambientAlpha*umbraAlpha, 0, 0, 0); + + draw(shape, paint); +} + +/** + * @param canvas the destination for the shadow draws + * @param shape the shape casting the shadow + * @param casterZValue the Z value of the caster RRect + * @param lightPos the position of the light casting the shadow + * @param lightWidth + * @param spotAlpha the maximum alpha value to use when drawing the spot shadow + * @param draw the function used to draw 'shape' + */ +template <typename Shape, typename F> +static void DrawSpotShadowGeneral(SkCanvas* canvas, const Shape& shape, float casterZValue, + float spotAlpha, F&& draw) { + if (spotAlpha <= 0) { + return; + } + + const Vector3 lightPos = SkiaPipeline::getLightCenter(); + float zRatio = casterZValue / (lightPos.z - casterZValue); + // clamp + if (zRatio < 0.0f) { + zRatio = 0.0f; + } else if (zRatio > 0.95f) { + zRatio = 0.95f; + } + + float blurRadius = SkiaPipeline::getLightRadius()*zRatio; + + SkAutoCanvasRestore acr(canvas, true); + + sk_sp<SkMaskFilter> mf = SkBlurMaskFilter::Make(kNormal_SkBlurStyle, + SkBlurMask::ConvertRadiusToSigma(blurRadius), SkBlurMaskFilter::kNone_BlurFlag); + + SkPaint paint; + paint.setAntiAlias(true); + paint.setMaskFilter(std::move(mf)); + paint.setARGB(spotAlpha, 0, 0, 0); + + // approximate projection by translating and scaling projected offset of bounds center + // TODO: compute the actual 2D projection + SkScalar scale = lightPos.z / (lightPos.z - casterZValue); + canvas->scale(scale, scale); + SkPoint center = SkPoint::Make(shape.getBounds().centerX(), shape.getBounds().centerY()); + SkMatrix ctmInverse; + if (!canvas->getTotalMatrix().invert(&ctmInverse)) { + ALOGW("Matrix is degenerate. Will not render shadow!"); + return; + } + SkPoint lightPos2D = SkPoint::Make(lightPos.x, lightPos.y); + ctmInverse.mapPoints(&lightPos2D, 1); + canvas->translate(zRatio*(center.fX - lightPos2D.fX), zRatio*(center.fY - lightPos2D.fY)); + + draw(shape, paint); +} + +#define MAX_BLUR_RADIUS 16383.75f +#define MAX_PAD 64 + +/** + * @param casterRect the rectangle bounds of the RRect casting the shadow + * @param casterCornerRadius the x&y radius for all the corners of the RRect casting the shadow + * @param ambientAlpha the maximum alpha value to use when drawing the ambient shadow + * @param spotAlpha the maximum alpha value to use when drawing the spot shadow + * @param casterAlpha the alpha value of the RRect casting the shadow (0.0-1.0 range) + * @param casterZValue the Z value of the caster RRect + * @param scaleFactor the scale needed to map from src-space to device-space + * @param canvas the destination for the shadow draws + */ +static void DrawRRectShadows(const SkRect& casterRect, SkScalar casterCornerRadius, + SkScalar ambientAlpha, SkScalar spotAlpha, SkScalar casterAlpha, SkScalar casterZValue, + SkScalar scaleFactor, SkCanvas* canvas) { + SkASSERT(casterCornerRadius >= 0.0f); + + // For all of these, we need to ensure we have a rrect with radius >= 0.5f in device space + const SkScalar minRadius = 0.5f / scaleFactor; + + const bool isOval = casterCornerRadius >= std::max(SkScalarHalf(casterRect.width()), + SkScalarHalf(casterRect.height())); + const bool isRect = casterCornerRadius <= minRadius; + + sk_sp<SkShader> edgeShader(SkGaussianEdgeShader::Make()); + + if (ambientAlpha > 0.0f) { + static const float kHeightFactor = 1.0f / 128.0f; + static const float kGeomFactor = 64.0f; + + SkScalar srcSpaceAmbientRadius = casterZValue * kHeightFactor * kGeomFactor; + // the device-space radius sent to the blur shader must fit in 14.2 fixed point + if (srcSpaceAmbientRadius*scaleFactor > MAX_BLUR_RADIUS) { + srcSpaceAmbientRadius = MAX_BLUR_RADIUS/scaleFactor; + } + const float umbraAlpha = 1.0f / (1.0f + std::max(casterZValue * kHeightFactor, 0.0f)); + const SkScalar ambientOffset = srcSpaceAmbientRadius * umbraAlpha; + + // For the ambient rrect, we inset the offset rect by half the srcSpaceAmbientRadius + // to get our stroke shape. + SkScalar ambientPathOutset = std::max(ambientOffset - srcSpaceAmbientRadius * 0.5f, + minRadius); + + SkRRect ambientRRect; + const SkRect temp = casterRect.makeOutset(ambientPathOutset, ambientPathOutset); + if (isOval) { + ambientRRect = SkRRect::MakeOval(temp); + } else if (isRect) { + ambientRRect = SkRRect::MakeRectXY(temp, ambientPathOutset, ambientPathOutset); + } else { + ambientRRect = SkRRect::MakeRectXY(temp, casterCornerRadius + ambientPathOutset, + casterCornerRadius + ambientPathOutset); + } + + SkPaint paint; + paint.setAntiAlias(true); + paint.setStyle(SkPaint::kStroke_Style); + // we outset the stroke a little to cover up AA on the interior edge + float pad = 0.5f; + paint.setStrokeWidth(srcSpaceAmbientRadius + 2.0f * pad); + // handle scale of radius and pad due to CTM + pad *= scaleFactor; + const SkScalar devSpaceAmbientRadius = srcSpaceAmbientRadius * scaleFactor; + SkASSERT(devSpaceAmbientRadius <= MAX_BLUR_RADIUS); + SkASSERT(pad < MAX_PAD); + // convert devSpaceAmbientRadius to 14.2 fixed point and place in the R & G components + // convert pad to 6.2 fixed point and place in the B component + uint16_t iDevSpaceAmbientRadius = (uint16_t)(4.0f * devSpaceAmbientRadius); + paint.setColor(SkColorSetARGB((unsigned char) ambientAlpha, iDevSpaceAmbientRadius >> 8, + iDevSpaceAmbientRadius & 0xff, (unsigned char)(4.0f * pad))); + + paint.setShader(edgeShader); + canvas->drawRRect(ambientRRect, paint); + } + + if (spotAlpha > 0.0f) { + const Vector3 lightPos = SkiaPipeline::getLightCenter(); + float zRatio = casterZValue / (lightPos.z - casterZValue); + // clamp + if (zRatio < 0.0f) { + zRatio = 0.0f; + } else if (zRatio > 0.95f) { + zRatio = 0.95f; + } + + const SkScalar lightWidth = SkiaPipeline::getLightRadius(); + SkScalar srcSpaceSpotRadius = 2.0f * lightWidth * zRatio; + // the device-space radius sent to the blur shader must fit in 14.2 fixed point + if (srcSpaceSpotRadius*scaleFactor > MAX_BLUR_RADIUS) { + srcSpaceSpotRadius = MAX_BLUR_RADIUS/scaleFactor; + } + + SkRRect spotRRect; + if (isOval) { + spotRRect = SkRRect::MakeOval(casterRect); + } else if (isRect) { + spotRRect = SkRRect::MakeRectXY(casterRect, minRadius, minRadius); + } else { + spotRRect = SkRRect::MakeRectXY(casterRect, casterCornerRadius, casterCornerRadius); + } + + SkRRect spotShadowRRect; + // Compute the scale and translation for the spot shadow. + const SkScalar scale = lightPos.z / (lightPos.z - casterZValue); + spotRRect.transform(SkMatrix::MakeScale(scale, scale), &spotShadowRRect); + + SkPoint center = SkPoint::Make(spotShadowRRect.rect().centerX(), + spotShadowRRect.rect().centerY()); + SkMatrix ctmInverse; + if (!canvas->getTotalMatrix().invert(&ctmInverse)) { + ALOGW("Matrix is degenerate. Will not render spot shadow!"); + return; + } + SkPoint lightPos2D = SkPoint::Make(lightPos.x, lightPos.y); + ctmInverse.mapPoints(&lightPos2D, 1); + const SkPoint spotOffset = SkPoint::Make(zRatio*(center.fX - lightPos2D.fX), + zRatio*(center.fY - lightPos2D.fY)); + + SkAutoCanvasRestore acr(canvas, true); + + // We want to extend the stroked area in so that it meets up with the caster + // geometry. The stroked geometry will, by definition already be inset half the + // stroke width but we also have to account for the scaling. + // We also add 1/2 to cover up AA on the interior edge. + SkScalar scaleOffset = (scale - 1.0f) * SkTMax(SkTMax(SkTAbs(casterRect.fLeft), + SkTAbs(casterRect.fRight)), SkTMax(SkTAbs(casterRect.fTop), + SkTAbs(casterRect.fBottom))); + SkScalar insetAmount = spotOffset.length() - (0.5f * srcSpaceSpotRadius) + + scaleOffset + 0.5f; + + // Compute area + SkScalar strokeWidth = srcSpaceSpotRadius + insetAmount; + SkScalar strokedArea = 2.0f*strokeWidth * (spotShadowRRect.width() + + spotShadowRRect.height()); + SkScalar filledArea = (spotShadowRRect.height() + srcSpaceSpotRadius) + * (spotShadowRRect.width() + srcSpaceSpotRadius); + + SkPaint paint; + paint.setAntiAlias(true); + + // If the area of the stroked geometry is larger than the fill geometry, just fill it. + if (strokedArea > filledArea || casterAlpha < 1.0f || insetAmount < 0.0f) { + paint.setStyle(SkPaint::kStrokeAndFill_Style); + paint.setStrokeWidth(srcSpaceSpotRadius); + } else { + // Since we can't have unequal strokes, inset the shadow rect so the inner + // and outer edges of the stroke will land where we want. + SkRect insetRect = spotShadowRRect.rect().makeInset(insetAmount/2.0f, insetAmount/2.0f); + SkScalar insetRad = SkTMax(spotShadowRRect.getSimpleRadii().fX - insetAmount/2.0f, + minRadius); + spotShadowRRect = SkRRect::MakeRectXY(insetRect, insetRad, insetRad); + paint.setStyle(SkPaint::kStroke_Style); + paint.setStrokeWidth(strokeWidth); + } + + // handle scale of radius and pad due to CTM + const SkScalar devSpaceSpotRadius = srcSpaceSpotRadius * scaleFactor; + SkASSERT(devSpaceSpotRadius <= MAX_BLUR_RADIUS); + + const SkScalar devSpaceSpotPad = 0; + SkASSERT(devSpaceSpotPad < MAX_PAD); + + // convert devSpaceSpotRadius to 14.2 fixed point and place in the R & G + // components convert devSpaceSpotPad to 6.2 fixed point and place in the B component + uint16_t iDevSpaceSpotRadius = (uint16_t)(4.0f * devSpaceSpotRadius); + paint.setColor(SkColorSetARGB((unsigned char) spotAlpha, iDevSpaceSpotRadius >> 8, + iDevSpaceSpotRadius & 0xff, (unsigned char)(4.0f * devSpaceSpotPad))); + paint.setShader(edgeShader); + + canvas->translate(spotOffset.fX, spotOffset.fY); + canvas->drawRRect(spotShadowRRect, paint); + } +} + +/** + * @param casterRect the rectangle bounds of the RRect casting the shadow + * @param casterCornerRadius the x&y radius for all the corners of the RRect casting the shadow + * @param ambientAlpha the maximum alpha value to use when drawing the ambient shadow + * @param spotAlpha the maximum alpha value to use when drawing the spot shadow + * @param casterZValue the Z value of the caster RRect + * @param scaleFactor the scale needed to map from src-space to device-space + * @param clipRR the oval or rect with which the drawn roundrect must be intersected + * @param canvas the destination for the shadow draws + */ +static void DrawRRectShadowsWithClip(const SkRect& casterRect, SkScalar casterCornerRadius, + SkScalar ambientAlpha, SkScalar spotAlpha, SkScalar casterZValue, SkScalar scaleFactor, + const SkRRect& clipRR, SkCanvas* canvas) { + SkASSERT(casterCornerRadius >= 0.0f); + + const bool isOval = casterCornerRadius >= std::max(SkScalarHalf(casterRect.width()), + SkScalarHalf(casterRect.height())); + + if (ambientAlpha > 0.0f) { + static const float kHeightFactor = 1.0f / 128.0f; + static const float kGeomFactor = 64.0f; + + const SkScalar srcSpaceAmbientRadius = casterZValue * kHeightFactor * kGeomFactor; + const SkScalar devSpaceAmbientRadius = srcSpaceAmbientRadius * scaleFactor; + + const float umbraAlpha = 1.0f / (1.0f + std::max(casterZValue * kHeightFactor, 0.0f)); + const SkScalar ambientOffset = srcSpaceAmbientRadius * umbraAlpha; + + const SkRect srcSpaceAmbientRect = casterRect.makeOutset(ambientOffset, ambientOffset); + SkRect devSpaceAmbientRect; + canvas->getTotalMatrix().mapRect(&devSpaceAmbientRect, srcSpaceAmbientRect); + + SkRRect devSpaceAmbientRRect; + if (isOval) { + devSpaceAmbientRRect = SkRRect::MakeOval(devSpaceAmbientRect); + } else { + const SkScalar devSpaceCornerRadius = scaleFactor * (casterCornerRadius + ambientOffset); + devSpaceAmbientRRect = SkRRect::MakeRectXY(devSpaceAmbientRect, devSpaceCornerRadius, + devSpaceCornerRadius); + } + + const SkRect srcSpaceAmbClipRect = clipRR.rect().makeOutset(ambientOffset, ambientOffset); + SkRect devSpaceAmbClipRect; + canvas->getTotalMatrix().mapRect(&devSpaceAmbClipRect, srcSpaceAmbClipRect); + SkRRect devSpaceAmbientClipRR; + if (clipRR.isOval()) { + devSpaceAmbientClipRR = SkRRect::MakeOval(devSpaceAmbClipRect); + } else { + SkASSERT(clipRR.isRect()); + devSpaceAmbientClipRR = SkRRect::MakeRect(devSpaceAmbClipRect); + } + + SkRect cover = srcSpaceAmbClipRect; + if (!cover.intersect(srcSpaceAmbientRect)) { + return; + } + + SkPaint paint; + paint.setColor(SkColorSetARGB((unsigned char) ambientAlpha, 0, 0, 0)); + paint.setMaskFilter(SkRRectsGaussianEdgeMaskFilter::Make(devSpaceAmbientRRect, + devSpaceAmbientClipRR, devSpaceAmbientRadius)); + canvas->drawRect(cover, paint); + } + + if (spotAlpha > 0.0f) { + const Vector3 lightPos = SkiaPipeline::getLightCenter(); + float zRatio = casterZValue / (lightPos.z - casterZValue); + // clamp + if (zRatio < 0.0f) { + zRatio = 0.0f; + } else if (zRatio > 0.95f) { + zRatio = 0.95f; + } + + const SkScalar lightWidth = SkiaPipeline::getLightRadius(); + const SkScalar srcSpaceSpotRadius = 2.0f * lightWidth * zRatio; + const SkScalar devSpaceSpotRadius = srcSpaceSpotRadius * scaleFactor; + + // Compute the scale and translation for the spot shadow. + const SkScalar scale = lightPos.z / (lightPos.z - casterZValue); + const SkMatrix spotMatrix = SkMatrix::MakeScale(scale, scale); + + SkRect srcSpaceScaledRect = casterRect; + spotMatrix.mapRect(&srcSpaceScaledRect); + srcSpaceScaledRect.outset(SkScalarHalf(srcSpaceSpotRadius), + SkScalarHalf(srcSpaceSpotRadius)); + + SkRRect srcSpaceSpotRRect; + if (isOval) { + srcSpaceSpotRRect = SkRRect::MakeOval(srcSpaceScaledRect); + } else { + srcSpaceSpotRRect = SkRRect::MakeRectXY(srcSpaceScaledRect, casterCornerRadius * scale, + casterCornerRadius * scale); + } + + SkPoint center = SkPoint::Make(srcSpaceSpotRRect.rect().centerX(), + srcSpaceSpotRRect.rect().centerY()); + SkMatrix ctmInverse; + if (!canvas->getTotalMatrix().invert(&ctmInverse)) { + ALOGW("Matrix is degenerate. Will not render spot shadow!"); + return; + } + SkPoint lightPos2D = SkPoint::Make(lightPos.x, lightPos.y); + ctmInverse.mapPoints(&lightPos2D, 1); + const SkPoint spotOffset = SkPoint::Make(zRatio*(center.fX - lightPos2D.fX), + zRatio*(center.fY - lightPos2D.fY)); + + SkAutoCanvasRestore acr(canvas, true); + canvas->translate(spotOffset.fX, spotOffset.fY); + + SkRect devSpaceScaledRect; + canvas->getTotalMatrix().mapRect(&devSpaceScaledRect, srcSpaceScaledRect); + + SkRRect devSpaceSpotRRect; + if (isOval) { + devSpaceSpotRRect = SkRRect::MakeOval(devSpaceScaledRect); + } else { + const SkScalar devSpaceScaledCornerRadius = casterCornerRadius * scale * scaleFactor; + devSpaceSpotRRect = SkRRect::MakeRectXY(devSpaceScaledRect, devSpaceScaledCornerRadius, + devSpaceScaledCornerRadius); + } + + SkPaint paint; + paint.setColor(SkColorSetARGB((unsigned char) spotAlpha, 0, 0, 0)); + + SkRect srcSpaceScaledClipRect = clipRR.rect(); + spotMatrix.mapRect(&srcSpaceScaledClipRect); + srcSpaceScaledClipRect.outset(SkScalarHalf(srcSpaceSpotRadius), + SkScalarHalf(srcSpaceSpotRadius)); + + SkRect devSpaceScaledClipRect; + canvas->getTotalMatrix().mapRect(&devSpaceScaledClipRect, srcSpaceScaledClipRect); + SkRRect devSpaceSpotClipRR; + if (clipRR.isOval()) { + devSpaceSpotClipRR = SkRRect::MakeOval(devSpaceScaledClipRect); + } else { + SkASSERT(clipRR.isRect()); + devSpaceSpotClipRR = SkRRect::MakeRect(devSpaceScaledClipRect); + } + + paint.setMaskFilter(SkRRectsGaussianEdgeMaskFilter::Make(devSpaceSpotRRect, + devSpaceSpotClipRR, devSpaceSpotRadius)); + + SkRect cover = srcSpaceScaledClipRect; + if (!cover.intersect(srcSpaceSpotRRect.rect())) { + return; + } + + canvas->drawRect(cover, paint); + } +} + +/** + * @param casterRect the rectangle bounds of the RRect casting the shadow + * @param casterCornerRadius the x&y radius for all the corners of the RRect casting the shadow + * @param casterClipRect a rectangular clip that must be intersected with the + * shadow-casting RRect prior to casting the shadow + * @param revealClip a circular clip that must be interested with the castClipRect + * and the shadow-casting rect prior to casting the shadow + * @param ambientAlpha the maximum alpha value to use when drawing the ambient shadow + * @param spotAlpha the maximum alpha value to use when drawing the spot shadow + * @param casterAlpha the alpha value of the RRect casting the shadow (0.0-1.0 range) + * @param casterZValue the Z value of the caster RRect + * @param canvas the destination for the shadow draws + * + * We have special cases for 4 round rect shadow draws: + * 1) a RRect clipped by a reveal animation + * 2) a RRect clipped by a rectangle + * 3) an unclipped RRect with non-uniform scale + * 4) an unclipped RRect with uniform scale + * 1,2 and 4 require that the scale is uniform. + * 1 and 2 require that rects stay rects. + */ +static bool DrawShadowsAsRRects(const SkRect& casterRect, SkScalar casterCornerRadius, + const SkRect& casterClipRect, const RevealClip& revealClip, SkScalar ambientAlpha, + SkScalar spotAlpha, SkScalar casterAlpha, SkScalar casterZValue, SkCanvas* canvas) { + SkScalar scaleFactors[2]; + if (!canvas->getTotalMatrix().getMinMaxScales(scaleFactors)) { + ALOGW("Matrix is degenerate. Will not render shadow!"); + return false; + } + + // The casterClipRect will be empty when bounds clipping is disabled + bool casterIsClippedByRect = !casterClipRect.isEmpty(); + bool uniformScale = scaleFactors[0] == scaleFactors[1]; + + if (revealClip.willClip()) { + if (casterIsClippedByRect || !uniformScale || !canvas->getTotalMatrix().rectStaysRect()) { + return false; // Fall back to the slow path since PathOps are required + } + + const float revealRadius = revealClip.getRadius(); + SkRect revealClipRect = SkRect::MakeLTRB(revealClip.getX()-revealRadius, + revealClip.getY()-revealRadius, revealClip.getX()+revealRadius, + revealClip.getY()+revealRadius); + SkRRect revealClipRR = SkRRect::MakeOval(revealClipRect); + + DrawRRectShadowsWithClip(casterRect, casterCornerRadius, ambientAlpha, spotAlpha, + casterZValue, scaleFactors[0], revealClipRR, canvas); + return true; + } + + if (casterIsClippedByRect) { + if (!uniformScale || !canvas->getTotalMatrix().rectStaysRect()) { + return false; // Fall back to the slow path since PathOps are required + } + + SkRRect casterClipRR = SkRRect::MakeRect(casterClipRect); + + DrawRRectShadowsWithClip(casterRect, casterCornerRadius, ambientAlpha, spotAlpha, + casterZValue, scaleFactors[0], casterClipRR, canvas); + return true; + } + + // The fast path needs uniform scale + if (!uniformScale) { + SkRRect casterRR = SkRRect::MakeRectXY(casterRect, casterCornerRadius, casterCornerRadius); + DrawAmbientShadowGeneral(canvas, casterRR, casterZValue, ambientAlpha, + [&](const SkRRect& rrect, const SkPaint& paint) { + canvas->drawRRect(rrect, paint); + }); + DrawSpotShadowGeneral(canvas, casterRR, casterZValue, spotAlpha, + [&](const SkRRect& rrect, const SkPaint& paint) { + canvas->drawRRect(rrect, paint); + }); + return true; + } + + DrawRRectShadows(casterRect, casterCornerRadius, ambientAlpha, spotAlpha, casterAlpha, + casterZValue, scaleFactors[0], canvas); + return true; +} + +// copied from FrameBuilder::deferShadow +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) { + // no shadow to draw + return; + } + + const SkScalar casterAlpha = casterProperties.getAlpha() + * casterProperties.getOutline().getAlpha(); + if (casterAlpha <= 0.0f) { + return; + } + + float ambientAlpha = SkiaPipeline::getAmbientShadowAlpha()*casterAlpha; + float spotAlpha = SkiaPipeline::getSpotShadowAlpha()*casterAlpha; + const float casterZValue = casterProperties.getZ(); + + const RevealClip& revealClip = casterProperties.getRevealClip(); + const SkPath* revealClipPath = revealClip.getPath(); + if (revealClipPath && revealClipPath->isEmpty()) { + // An empty reveal clip means nothing is drawn + return; + } + + bool clippedToBounds = casterProperties.getClippingFlags() & CLIP_TO_CLIP_BOUNDS; + + SkRect casterClipRect = SkRect::MakeEmpty(); + if (clippedToBounds) { + Rect clipBounds; + casterProperties.getClippingRectForFlags(CLIP_TO_CLIP_BOUNDS, &clipBounds); + casterClipRect = clipBounds.toSkRect(); + if (casterClipRect.isEmpty()) { + // An empty clip rect means nothing is drawn + return; + } + } + + SkAutoCanvasRestore acr(canvas, true); + + SkMatrix shadowMatrix; + mat4 hwuiMatrix(caster->getRecordedMatrix()); + // TODO we don't pass the optional boolean to treat it as a 4x4 matrix + caster->getRenderNode()->applyViewPropertyTransforms(hwuiMatrix); + hwuiMatrix.copyTo(shadowMatrix); + canvas->concat(shadowMatrix); + + const Outline& casterOutline = casterProperties.getOutline(); + Rect possibleRect; + float radius; + if (casterOutline.getAsRoundRect(&possibleRect, &radius)) { + if (DrawShadowsAsRRects(possibleRect.toSkRect(), radius, casterClipRect, revealClip, + ambientAlpha, spotAlpha, casterAlpha, casterZValue, canvas)) { + return; + } + } + + // Hard cases and calls to general shadow code + const SkPath* casterOutlinePath = casterProperties.getOutline().getPath(); + + // holds temporary SkPath to store the result of intersections + SkPath tmpPath; + const SkPath* casterPath = casterOutlinePath; + + // 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 + if (revealClipPath) { + Op(*casterPath, *revealClipPath, kIntersect_SkPathOp, &tmpPath); + 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); + casterPath = &tmpPath; + } + + DrawAmbientShadowGeneral(canvas, *casterPath, casterZValue, ambientAlpha, + [&](const SkPath& path, const SkPaint& paint) { + canvas->drawPath(path, paint); + }); + + DrawSpotShadowGeneral(canvas, *casterPath, casterZValue, spotAlpha, + [&](const SkPath& path, const SkPaint& paint) { + canvas->drawPath(path, paint); + }); +} + +}; // namespace skiapipeline +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.h b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.h new file mode 100644 index 000000000000..9f00d23ae985 --- /dev/null +++ b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.h @@ -0,0 +1,80 @@ +/* + * 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. + */ + +#pragma once + +#include "RenderNodeDrawable.h" + +#include <SkCanvas.h> +#include <SkDrawable.h> +#include <utils/FatVector.h> + +namespace android { +namespace uirenderer { +namespace skiapipeline { + +class SkiaDisplayList; +class EndReorderBarrierDrawable; + +/** + * StartReorderBarrierDrawable and EndReorderBarrierDrawable work together to define + * a sub-list in a display list that need to be drawn out-of-order sorted instead by render + * node Z index. + * StartReorderBarrierDrawable will sort the entire range and it will draw + * render nodes in the range with negative Z index. + */ +class StartReorderBarrierDrawable : public SkDrawable { +public: + explicit StartReorderBarrierDrawable(SkiaDisplayList* data); + +protected: + virtual SkRect onGetBounds() override { + return SkRect::MakeLargest(); + } + virtual void onDraw(SkCanvas* canvas) override; + +private: + int mEndChildIndex; + int mBeginChildIndex; + FatVector<RenderNodeDrawable*, 16> mChildren; + SkiaDisplayList* mDisplayList; + + friend class EndReorderBarrierDrawable; +}; + +/** + * See StartReorderBarrierDrawable. + * EndReorderBarrierDrawable relies on StartReorderBarrierDrawable to host and sort the render + * nodes by Z index. When EndReorderBarrierDrawable is drawn it will draw all render nodes in the + * range with positive Z index. It is also responsible for drawing shadows for the nodes + * corresponding to their z-index. + */ +class EndReorderBarrierDrawable : public SkDrawable { +public: + explicit EndReorderBarrierDrawable(StartReorderBarrierDrawable* startBarrier); +protected: + 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 diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp new file mode 100644 index 000000000000..9db8cd3fe2e2 --- /dev/null +++ b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp @@ -0,0 +1,120 @@ +/* + * 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 "SkiaDisplayList.h" + +#include "renderthread/CanvasContext.h" +#include "VectorDrawable.h" + +#include <SkImagePriv.h> + + +namespace android { +namespace uirenderer { +namespace skiapipeline { + +SkiaDisplayList::SkiaDisplayList(SkRect bounds) : mDrawable(SkLiteDL::New(bounds)) { + SkASSERT(projectionReceiveIndex == -1); +} + +void SkiaDisplayList::syncContents() { + for (auto& functor : mChildFunctors) { + functor.syncFunctor(); + } + for (auto& vectorDrawable : mVectorDrawables) { + vectorDrawable->syncProperties(); + } +} + +bool SkiaDisplayList::reuseDisplayList(RenderNode* node, renderthread::CanvasContext* context) { + reset(SkRect::MakeEmpty()); + node->attachAvailableList(this); + return true; +} + +void SkiaDisplayList::updateChildren(std::function<void(RenderNode*)> updateFn) { + for (auto& child : mChildNodes) { + updateFn(child.getRenderNode()); + } +} + +bool SkiaDisplayList::prepareListAndChildren(TreeInfo& info, bool functorsNeedLayer, + std::function<void(RenderNode*, 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 + // until the next UI thread draw. + if (info.prepareTextures && !info.canvasContext.pinImages(mMutableImages)) { + // In the event that pinning failed we prevent future pinImage calls for the + // remainder of this tree traversal and also unpin any currently pinned images + // to free up GPU resources. + info.prepareTextures = false; + info.canvasContext.unpinImages(); + } + + bool hasBackwardProjectedNodesHere = false; + bool hasBackwardProjectedNodesSubtree= false; + + for (auto& child : mChildNodes) { + hasBackwardProjectedNodesHere |= child.getNodeProperties().getProjectBackwards(); + RenderNode* childNode = child.getRenderNode(); + Matrix4 mat4(child.getRecordedMatrix()); + info.damageAccumulator->pushTransform(&mat4); + // TODO: a layer is needed if the canvas is rotated or has a non-rect clip + info.hasBackwardProjectedNodes = false; + childFn(childNode, info, functorsNeedLayer); + hasBackwardProjectedNodesSubtree |= info.hasBackwardProjectedNodes; + 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 + if (mProjectionReceiver) { + mProjectionReceiver->setProjectedDisplayList(hasBackwardProjectedNodesSubtree ? this : nullptr); + info.hasBackwardProjectedNodes = hasBackwardProjectedNodesHere; + } else { + info.hasBackwardProjectedNodes = hasBackwardProjectedNodesSubtree + || hasBackwardProjectedNodesHere; + } + + bool isDirty = false; + for (auto& vectorDrawable : mVectorDrawables) { + // If any vector drawable in the display list needs update, damage the node. + if (vectorDrawable->isDirty()) { + isDirty = true; + } + vectorDrawable->setPropertyChangeWillBeConsumed(true); + } + return isDirty; +} + +void SkiaDisplayList::reset(SkRect bounds) { + mProjectionReceiver = nullptr; + + mDrawable->reset(bounds); + + mMutableImages.clear(); + mVectorDrawables.clear(); + mChildFunctors.clear(); + mChildNodes.clear(); + + projectionReceiveIndex = -1; + allocator.~LinearAllocator(); + new (&allocator) LinearAllocator(); +} + +}; // namespace skiapipeline +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.h b/libs/hwui/pipeline/skia/SkiaDisplayList.h new file mode 100644 index 000000000000..ff86fd18a9a4 --- /dev/null +++ b/libs/hwui/pipeline/skia/SkiaDisplayList.h @@ -0,0 +1,160 @@ +/* + * 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. + */ + +#pragma once + +#include "DisplayList.h" +#include "GLFunctorDrawable.h" +#include "RenderNodeDrawable.h" + +#include <deque> +#include <SkLiteDL.h> +#include <SkPictureRecorder.h> + +namespace android { +namespace uirenderer { + +class Outline; + +namespace skiapipeline { + +/** + * This class is intended to be self contained, but still subclasses from + * DisplayList to make it easier to support switching between the two at + * runtime. The downside of this inheritance is that we pay for the overhead + * of the parent class construction/destruction without any real benefit. + */ +class SkiaDisplayList : public DisplayList { +public: + SkiaDisplayList(SkRect bounds); + virtual ~SkiaDisplayList() { + /* Given that we are using a LinearStdAllocator to store some of the + * SkDrawable contents we must ensure that any other object that is + * holding a reference to those drawables is destroyed prior to their + * deletion. + */ + mDrawable.reset(); + } + + /** + * This resets the DisplayList so that it behaves as if the object were newly + * constructed with the provided bounds. The reuse avoids any overhead + * associated with destroying the SkLiteDL as well as the deques and vectors. + */ + void reset(SkRect bounds); + + /** + * Use the linear allocator to create any SkDrawables needed by the display + * list. This could be dangerous as these objects are ref-counted, so we + * need to monitor that they don't extend beyond the lifetime of the class + * that creates them. + */ + template<class T, typename... Params> + SkDrawable* allocateDrawable(Params&&... params) { + return allocator.create<T>(std::forward<Params>(params)...); + } + + bool isSkiaDL() const override { return true; } + + /** + * Returns true if the DisplayList does not have any recorded content + */ + bool isEmpty() const override { return mDrawable->empty(); } + + /** + * Returns true if this list directly contains a GLFunctor drawing command. + */ + bool hasFunctor() const override { return !mChildFunctors.empty(); } + + /** + * Returns true if this list directly contains a VectorDrawable drawing command. + */ + bool hasVectorDrawables() const override { return !mVectorDrawables.empty(); } + + /** + * Attempts to reset and reuse this DisplayList. + * + * @return true if the displayList will be reused and therefore should not be deleted + */ + bool reuseDisplayList(RenderNode* node, renderthread::CanvasContext* context) override; + + /** + * ONLY to be called by RenderNode::syncDisplayList so that we can notify any + * contained VectorDrawables or GLFunctors to sync their state. + * + * NOTE: This function can be folded into RenderNode when we no longer need + * to subclass from DisplayList + */ + void syncContents() override; + + /** + * ONLY to be called by RenderNode::prepareTree in order to prepare this + * list while the UI thread is blocked. Here we can upload mutable bitmaps + * and notify our parent if any of our content has been invalidated and in + * need of a redraw. If the renderNode has any children then they are also + * call in order to prepare them. + * + * @return true if any content change requires the node to be invalidated + * + * NOTE: This function can be folded into RenderNode when we no longer need + * to subclass from DisplayList + */ + + bool prepareListAndChildren(TreeInfo& info, bool functorsNeedLayer, + std::function<void(RenderNode*, TreeInfo&, bool)> childFn) override; + + /** + * Calls the provided function once for each child of this DisplayList + */ + void updateChildren(std::function<void(RenderNode*)> updateFn) override; + + /** + * Returns true if there is a child render node that is a projection receiver. + */ + inline bool containsProjectionReceiver() const { return mProjectionReceiver; } + + /** + * We use std::deque here because (1) we need to iterate through these + * elements and (2) mDrawable holds pointers to the elements, so they cannot + * relocate. + */ + std::deque<RenderNodeDrawable> mChildNodes; + std::deque<GLFunctorDrawable> mChildFunctors; + std::vector<SkImage*> mMutableImages; + std::vector<VectorDrawableRoot*> mVectorDrawables; + sk_sp<SkLiteDL> mDrawable; + + //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. + 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. + SkMatrix mProjectedReceiverParentMatrix; +}; + +}; // namespace skiapipeline +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/pipeline/skia/SkiaLayer.h b/libs/hwui/pipeline/skia/SkiaLayer.h new file mode 100644 index 000000000000..904d57e073ca --- /dev/null +++ b/libs/hwui/pipeline/skia/SkiaLayer.h @@ -0,0 +1,39 @@ +/* + * 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. + */ + +#pragma once + +#include <SkSurface.h> +#include "Matrix.h" + +namespace android { +namespace uirenderer { +namespace skiapipeline { + +/** + * An offscreen rendering target used to contain the contents a RenderNode. + */ +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 new file mode 100644 index 000000000000..65a1dc38ab8e --- /dev/null +++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp @@ -0,0 +1,195 @@ +/* + * 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 "SkiaOpenGLPipeline.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 "utils/TraceUtils.h" + +#include <android/native_window.h> +#include <cutils/properties.h> +#include <strings.h> + +using namespace android::uirenderer::renderthread; + +namespace android { +namespace uirenderer { +namespace skiapipeline { + +SkiaOpenGLPipeline::SkiaOpenGLPipeline(RenderThread& thread) + : SkiaPipeline(thread) + , mEglManager(thread.eglManager()) { +} + +MakeCurrentResult SkiaOpenGLPipeline::makeCurrent() { + // TODO: Figure out why this workaround is needed, see b/13913604 + // In the meantime this matches the behavior of GLRenderer, so it is not a regression + EGLint error = 0; + if (!mEglManager.makeCurrent(mEglSurface, &error)) { + return MakeCurrentResult::AlreadyCurrent; + } + return error ? MakeCurrentResult::Failed : MakeCurrentResult::Succeeded; +} + +Frame SkiaOpenGLPipeline::getFrame() { + LOG_ALWAYS_FATAL_IF(mEglSurface == EGL_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, + const BakedOpRenderer::LightInfo& lightInfo, + const std::vector<sp<RenderNode>>& renderNodes, + FrameInfoVisualizer* profiler) { + + mEglManager.damageFrame(frame, dirty); + + // setup surface for fbo0 + GrBackendRenderTargetDesc renderTargetDesc; + renderTargetDesc.fWidth = frame.width(); + renderTargetDesc.fHeight = frame.height(); + renderTargetDesc.fConfig = kRGBA_8888_GrPixelConfig; + renderTargetDesc.fOrigin = kBottomLeft_GrSurfaceOrigin; + renderTargetDesc.fSampleCnt = 0; + renderTargetDesc.fStencilBits = STENCIL_BUFFER_SIZE; + renderTargetDesc.fRenderTargetHandle = 0; + + SkSurfaceProps props(0, kUnknown_SkPixelGeometry); + + SkASSERT(mRenderThread.getGrContext() != nullptr); + sk_sp<SkSurface> surface(SkSurface::MakeFromBackendRenderTarget( + mRenderThread.getGrContext(), renderTargetDesc, &props)); + + SkiaPipeline::updateLighting(lightGeometry, lightInfo); + renderFrame(*layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface); + layerUpdateQueue->clear(); + + // Draw visual debugging features + if (CC_UNLIKELY(Properties::showDirtyRegions + || ProfileType::None != Properties::getProfileType())) { + SkCanvas* profileCanvas = surface->getCanvas(); + SkiaProfileRenderer profileRenderer(profileCanvas); + profiler->draw(profileRenderer); + profileCanvas->flush(); + } + + // Log memory statistics + if (CC_UNLIKELY(Properties::debugLevel != kDebugDisabled)) { + dumpResourceCacheUsage(); + } + + return true; +} + +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 + // metrics the frame was swapped at this point + currentFrameInfo->markSwapBuffers(); + + *requireSwap = drew || mEglManager.damageRequiresSwap(); + + if (*requireSwap && (CC_UNLIKELY(!mEglManager.swapBuffers(frame, screenDirty)))) { + return false; + } + + return *requireSwap; +} + +bool SkiaOpenGLPipeline::copyLayerInto(DeferredLayerUpdater* deferredLayer, SkBitmap* bitmap) { + if (!mRenderThread.getGrContext()) { + return false; + } + + deferredLayer->apply(); + + SkCanvas canvas(*bitmap); + Layer* layer = deferredLayer->backingLayer(); + return LayerDrawable::DrawLayer(mRenderThread.getGrContext(), &canvas, layer); +} + +DeferredLayerUpdater* SkiaOpenGLPipeline::createTextureLayer() { + mEglManager.initialize(); + GlLayer* layer = new GlLayer(mRenderThread.renderState(), 0, 0); + layer->generateTexture(); + return new DeferredLayerUpdater(layer); +} + +void SkiaOpenGLPipeline::onStop() { + if (mEglManager.isCurrent(mEglSurface)) { + mEglManager.makeCurrent(EGL_NO_SURFACE); + } +} + +bool SkiaOpenGLPipeline::setSurface(Surface* surface, SwapBehavior swapBehavior) { + + if (mEglSurface != EGL_NO_SURFACE) { + mEglManager.destroySurface(mEglSurface); + mEglSurface = EGL_NO_SURFACE; + } + + if (surface) { + mEglSurface = mEglManager.createSurface(surface); + } + + if (mEglSurface != EGL_NO_SURFACE) { + const bool preserveBuffer = (swapBehavior != SwapBehavior::kSwap_discardBuffer); + mBufferPreserved = mEglManager.setPreserveBuffer(mEglSurface, preserveBuffer); + return true; + } + + return false; +} + +bool SkiaOpenGLPipeline::isSurfaceReady() { + return CC_UNLIKELY(mEglSurface != EGL_NO_SURFACE); +} + +bool SkiaOpenGLPipeline::isContextReady() { + return CC_LIKELY(mEglManager.hasEglContext()); +} + +void SkiaOpenGLPipeline::invokeFunctor(const RenderThread& thread, Functor* functor) { + DrawGlInfo::Mode mode = DrawGlInfo::kModeProcessNoContext; + if (thread.eglManager().hasEglContext()) { + mode = DrawGlInfo::kModeProcess; + } + + (*functor)(mode, nullptr); + + // If there's no context we don't need to reset as there's no gl state to save/restore + if (mode != DrawGlInfo::kModeProcessNoContext) { + thread.getGrContext()->resetContext(); + } +} + +} /* namespace skiapipeline */ +} /* namespace uirenderer */ +} /* namespace android */ diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h new file mode 100644 index 000000000000..36685ddb17a7 --- /dev/null +++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h @@ -0,0 +1,58 @@ +/* + * 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. + */ + +#pragma once + +#include "SkiaPipeline.h" + +namespace android { +namespace uirenderer { +namespace skiapipeline { + +class SkiaOpenGLPipeline : public SkiaPipeline { +public: + SkiaOpenGLPipeline(renderthread::RenderThread& thread); + virtual ~SkiaOpenGLPipeline() {} + + 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, + 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; + bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) override; + DeferredLayerUpdater* createTextureLayer() override; + bool setSurface(Surface* window, renderthread::SwapBehavior swapBehavior) override; + void onStop() override; + bool isSurfaceReady() override; + bool isContextReady() override; + + static void invokeFunctor(const renderthread::RenderThread& thread, Functor* functor); + +private: + renderthread::EglManager& mEglManager; + EGLSurface mEglSurface = EGL_NO_SURFACE; + bool mBufferPreserved = false; +}; + +} /* namespace skiapipeline */ +} /* namespace uirenderer */ +} /* namespace android */ diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp new file mode 100644 index 000000000000..a18d26471a29 --- /dev/null +++ b/libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp @@ -0,0 +1,121 @@ +/* + * 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 "SkiaOpenGLReadback.h" + +#include "Matrix.h" +#include "Properties.h" +#include <SkCanvas.h> +#include <SkSurface.h> +#include <gl/GrGLInterface.h> +#include <gl/GrGLTypes.h> +#include <GLES2/gl2.h> +#include <GLES2/gl2ext.h> + +using namespace android::uirenderer::renderthread; + +namespace android { +namespace uirenderer { +namespace skiapipeline { + +CopyResult SkiaOpenGLReadback::copyImageInto(EGLImageKHR eglImage, const Matrix4& imgTransform, + int imgWidth, int imgHeight, const Rect& srcRect, SkBitmap* bitmap) { + + GLuint sourceTexId; + glGenTextures(1, &sourceTexId); + glBindTexture(GL_TEXTURE_EXTERNAL_OES, sourceTexId); + glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, eglImage); + + sk_sp<GrContext> grContext = sk_ref_sp(mRenderThread.getGrContext()); + 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())); + } else { + grContext->resetContext(); + } + + GrGLTextureInfo externalTexture; + externalTexture.fTarget = GL_TEXTURE_EXTERNAL_OES; + externalTexture.fID = sourceTexId; + + GrBackendTextureDesc textureDescription; + textureDescription.fWidth = imgWidth; + textureDescription.fHeight = imgHeight; + textureDescription.fConfig = kRGBA_8888_GrPixelConfig; + textureDescription.fOrigin = kTopLeft_GrSurfaceOrigin; + textureDescription.fTextureHandle = reinterpret_cast<GrBackendObject>(&externalTexture); + + CopyResult copyResult = CopyResult::UnknownError; + sk_sp<SkImage> image(SkImage::MakeFromAdoptedTexture(grContext.get(), textureDescription)); + if (image) { + SkAutoLockPixels alp(*bitmap); + + // convert to Skia data structures + const SkRect bufferRect = SkRect::MakeIWH(imgWidth, imgHeight); + SkRect skiaSrcRect = srcRect.toSkRect(); + SkMatrix textureMatrix; + imgTransform.copyTo(textureMatrix); + + // remove the y-flip applied to the matrix so that we can scale the srcRect. + // This flip is not needed as we specify the origin of the texture when we + // wrap it as an SkImage. + SkMatrix yFlip = SkMatrix::MakeScale(1, -1); + yFlip.postTranslate(0,1); + textureMatrix.preConcat(yFlip); + + // copy the entire src if the rect is empty + if (skiaSrcRect.isEmpty()) { + skiaSrcRect = bufferRect; + } + + // since the y-flip has been removed we can simply scale & translate + // the source rectangle + textureMatrix.mapRect(&skiaSrcRect); + + if (skiaSrcRect.intersect(bufferRect)) { + SkPoint srcOrigin = SkPoint::Make(skiaSrcRect.fLeft, skiaSrcRect.fTop); + + // if we need to scale the result we must render to an offscreen buffer + if (bitmap->width() != skiaSrcRect.width() + || bitmap->height() != skiaSrcRect.height()) { + sk_sp<SkSurface> scaledSurface = SkSurface::MakeRenderTarget( + grContext.get(), SkBudgeted::kYes, bitmap->info()); + SkPaint paint; + paint.setBlendMode(SkBlendMode::kSrc); + scaledSurface->getCanvas()->drawImageRect(image, skiaSrcRect, + SkRect::MakeWH(bitmap->width(), bitmap->height()), &paint); + image = scaledSurface->makeImageSnapshot(); + srcOrigin.set(0,0); + } + + if (image->readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(), + srcOrigin.fX, srcOrigin.fY)) { + copyResult = CopyResult::Success; + } + } + } + + // make sure that we have deleted the texture (in the SkImage) before we + // destroy the EGLImage that it was created from + image.reset(); + return copyResult; +} + +} /* namespace skiapipeline */ +} /* namespace uirenderer */ +} /* namespace android */ diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLReadback.h b/libs/hwui/pipeline/skia/SkiaOpenGLReadback.h new file mode 100644 index 000000000000..d914409628d0 --- /dev/null +++ b/libs/hwui/pipeline/skia/SkiaOpenGLReadback.h @@ -0,0 +1,35 @@ +/* + * 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. + */ + +#pragma once + +#include "OpenGLReadback.h" + +namespace android { +namespace uirenderer { +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; +}; + +} /* namespace skiapipeline */ +} /* namespace uirenderer */ +} /* namespace android */ diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp new file mode 100644 index 000000000000..430d6bea70c1 --- /dev/null +++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp @@ -0,0 +1,374 @@ +/* + * 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 "SkiaPipeline.h" + +#include "utils/TraceUtils.h" +#include <SkImageEncoder.h> +#include <SkImagePriv.h> +#include <SkOSFile.h> +#include <SkOverdrawCanvas.h> +#include <SkOverdrawColorFilter.h> +#include <SkPicture.h> +#include <SkPictureRecorder.h> +#include <SkPixelSerializer.h> +#include <SkStream.h> + +using namespace android::uirenderer::renderthread; + +namespace android { +namespace uirenderer { +namespace skiapipeline { + +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) { } + +TaskManager* SkiaPipeline::getTaskManager() { + return &mTaskManager; +} + +void SkiaPipeline::onDestroyHardwareResources() { + // No need to flush the caches here. There is a timer + // which will flush temporary resources over time. +} + +bool SkiaPipeline::pinImages(std::vector<SkImage*>& mutableImages) { + for (SkImage* image : mutableImages) { + if (SkImage_pinAsTexture(image, mRenderThread.getGrContext())) { + mPinnedImages.emplace_back(sk_ref_sp(image)); + } else { + return false; + } + } + return true; +} + +void SkiaPipeline::unpinImages() { + for (auto& image : mPinnedImages) { + SkImage_unpinAsTexture(image.get(), mRenderThread.getGrContext()); + } + mPinnedImages.clear(); +} + +void SkiaPipeline::renderLayers(const FrameBuilder::LightGeometry& lightGeometry, + LayerUpdateQueue* layerUpdateQueue, bool opaque, + const BakedOpRenderer::LightInfo& lightInfo) { + updateLighting(lightGeometry, lightInfo); + ATRACE_NAME("draw layers"); + renderLayersImpl(*layerUpdateQueue, opaque); + layerUpdateQueue->clear(); +} + +void SkiaPipeline::renderLayersImpl(const LayerUpdateQueue& layers, bool opaque) { + // 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; + // only schedule repaint if node still on layer - possible it may have been + // removed during a dropped frame, but layers may still remain scheduled so + // as not to lose info on what portion is damaged + if (CC_LIKELY(layerNode->getLayerSurface() != nullptr)) { + SkASSERT(layerNode->getLayerSurface()); + SkASSERT(layerNode->getDisplayList()->isSkiaDL()); + SkiaDisplayList* displayList = (SkiaDisplayList*)layerNode->getDisplayList(); + if (!displayList || displayList->isEmpty()) { + SkDEBUGF(("%p drawLayers(%s) : missing drawable", this, layerNode->getName())); + return; + } + + const Rect& layerDamage = layers.entries()[i].damage; + + SkCanvas* layerCanvas = layerNode->getLayerSurface()->getCanvas(); + + int saveCount = layerCanvas->save(); + SkASSERT(saveCount == 1); + + layerCanvas->androidFramework_setDeviceClipRestriction(layerDamage.toSkIRect()); + + auto savedLightCenter = mLightCenter; + // map current light center into RenderNode's coordinate space + layerNode->getSkiaLayer()->inverseTransformInWindow.mapPoint3d(mLightCenter); + + const RenderProperties& properties = layerNode->properties(); + const SkRect bounds = SkRect::MakeWH(properties.getWidth(), properties.getHeight()); + if (properties.getClipToBounds() && layerCanvas->quickReject(bounds)) { + return; + } + + layerNode->getSkiaLayer()->hasRenderedSinceRepaint = false; + layerCanvas->clear(SK_ColorTRANSPARENT); + + RenderNodeDrawable root(layerNode, layerCanvas, false); + root.forceDraw(layerCanvas); + layerCanvas->restoreToCount(saveCount); + layerCanvas->flush(); + mLightCenter = savedLightCenter; + } + } +} + +bool SkiaPipeline::createOrUpdateLayer(RenderNode* node, + const DamageAccumulator& damageAccumulator) { + SkSurface* layer = node->getLayerSurface(); + if (!layer || layer->width() != node->getWidth() || layer->height() != node->getHeight()) { + SkImageInfo info = SkImageInfo::MakeN32Premul(node->getWidth(), node->getHeight()); + SkSurfaceProps props(0, kUnknown_SkPixelGeometry); + SkASSERT(mRenderThread.getGrContext() != nullptr); + 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 + Matrix4 windowTransform; + damageAccumulator.computeCurrentTransform(&windowTransform); + node->getSkiaLayer()->inverseTransformInWindow = windowTransform; + } + return true; + } + return false; +} + +void SkiaPipeline::destroyLayer(RenderNode* node) { + node->setLayerSurface(nullptr); +} + +void SkiaPipeline::prepareToDraw(const RenderThread& thread, Bitmap* bitmap) { + GrContext* context = thread.getGrContext(); + if (context) { + ATRACE_FORMAT("Bitmap#prepareToDraw %dx%d", bitmap->width(), bitmap->height()); + SkBitmap skiaBitmap; + bitmap->getSkBitmap(&skiaBitmap); + sk_sp<SkImage> image = SkMakeImageFromRasterBitmap(skiaBitmap, kNever_SkCopyPixelsMode); + SkImage_pinAsTexture(image.get(), context); + SkImage_unpinAsTexture(image.get(), context); + } +} + +// 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::renderFrame(const LayerUpdateQueue& layers, const SkRect& clip, + const std::vector<sp<RenderNode>>& nodes, bool opaque, const Rect &contentDrawBounds, + sk_sp<SkSurface> surface) { + + // draw all layers up front + renderLayersImpl(layers, opaque); + + // initialize the canvas for the current frame + SkCanvas* canvas = surface->getCanvas(); + + 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' && !sk_exists(prop); + if (recordingPicture) { + recorder.reset(new SkPictureRecorder()); + canvas = recorder->beginRecording(surface->width(), surface->height(), + nullptr, SkPictureRecorder::kPlaybackDrawPicture_RecordFlag); + } + } + + renderFrameImpl(layers, clip, nodes, opaque, contentDrawBounds, canvas); + + if (skpCaptureEnabled() && recordingPicture) { + sk_sp<SkPicture> picture = recorder->finishRecordingAsPicture(); + 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); + } + } + surface->getCanvas()->drawPicture(picture); + } + + if (CC_UNLIKELY(Properties::debugOverdraw)) { + renderOverdraw(layers, clip, nodes, contentDrawBounds, surface); + } + + ATRACE_NAME("flush commands"); + canvas->flush(); +} + +namespace { +static Rect nodeBounds(RenderNode& node) { + auto& props = node.properties(); + 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, const Rect &contentDrawBounds, + SkCanvas* canvas) { + SkAutoCanvasRestore saver(canvas, true); + canvas->androidFramework_setDeviceClipRestriction(clip.roundOut()); + + if (!opaque) { + canvas->clear(SK_ColorTRANSPARENT); + } + + if (1 == nodes.size()) { + if (!nodes[0]->nothingToDraw()) { + RenderNodeDrawable root(nodes[0].get(), canvas); + root.draw(canvas); + } + } else if (0 == nodes.size()) { + //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 + // cropped to the backdrop (since that indicates a shrinking of the window). + // + // Additional nodes will be drawn on top with no particular clipping semantics. + + // Usually the contents bounds should be mContentDrawBounds - however - we will + // move it towards the fixed edge to give it a more stable appearance (for the moment). + // If there is no content bounds we ignore the layering as stated above and start with 2. + + // 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) + 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 + // 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)); + 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)); + backdropNode.draw(canvas); + } + } + + RenderNodeDrawable contentNode(nodes[1].get(), canvas); + if (!backdrop.isEmpty()) { + // content node translation to catch up with backdrop + float dx = backdrop.left - contentDrawBounds.left; + float dy = backdrop.top - contentDrawBounds.top; + + SkAutoCanvasRestore acr(canvas, true); + canvas->translate(dx, dy); + const SkRect contentLocalClip = SkRect::MakeXYWH(contentDrawBounds.left, + contentDrawBounds.top, backdrop.getWidth(), backdrop.getHeight()); + canvas->clipRect(contentLocalClip); + contentNode.draw(canvas); + } else { + SkAutoCanvasRestore acr(canvas, true); + contentNode.draw(canvas); + } + + // remaining overlay nodes, simply defer + for (size_t index = 2; index < nodes.size(); index++) { + if (!nodes[index]->nothingToDraw()) { + SkAutoCanvasRestore acr(canvas, true); + RenderNodeDrawable overlayNode(nodes[index].get(), canvas); + overlayNode.draw(canvas); + } + } + } +} + +void SkiaPipeline::dumpResourceCacheUsage() const { + int resources, maxResources; + size_t bytes, maxBytes; + mRenderThread.getGrContext()->getResourceCacheUsage(&resources, &bytes); + mRenderThread.getGrContext()->getResourceCacheLimits(&maxResources, &maxBytes); + + 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))); + + ALOGD("%s", log.c_str()); +} + +// Overdraw debugging + +// These colors should be kept in sync with Caches::getOverdrawColor() with a few differences. +// This implementation: +// (1) Requires transparent entries for "no overdraw" and "single draws". +// (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, }, +}; + +void SkiaPipeline::renderOverdraw(const LayerUpdateQueue& layers, const SkRect& clip, + 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); + SkOverdrawCanvas overdrawCanvas(offscreen->getCanvas()); + + // Fake a redraw to replay the draw commands. This will increment the alpha channel + // each time a pixel would have been drawn. + // Pass true for opaque so we skip the clear - the overdrawCanvas is already zero + // initialized. + renderFrameImpl(layers, clip, nodes, true, contentDrawBounds, &overdrawCanvas); + sk_sp<SkImage> counts = offscreen->makeImageSnapshot(); + + // Draw overdraw colors to the canvas. The color filter will convert counts to colors. + SkPaint paint; + const SkPMColor* colors = kOverdrawColors[static_cast<int>(Properties::overdrawColorSet)]; + paint.setColorFilter(SkOverdrawColorFilter::Make(colors)); + surface->getCanvas()->drawImage(counts.get(), 0.0f, 0.0f, &paint); +} + +} /* namespace skiapipeline */ +} /* namespace uirenderer */ +} /* namespace android */ diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.h b/libs/hwui/pipeline/skia/SkiaPipeline.h new file mode 100644 index 000000000000..c58fedf834ff --- /dev/null +++ b/libs/hwui/pipeline/skia/SkiaPipeline.h @@ -0,0 +1,132 @@ +/* + * 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. + */ + +#pragma once + +#include "renderthread/CanvasContext.h" +#include "FrameBuilder.h" +#include "renderthread/IRenderPipeline.h" +#include <SkSurface.h> + +namespace android { +namespace uirenderer { +namespace skiapipeline { + +class SkiaPipeline : public renderthread::IRenderPipeline { +public: + SkiaPipeline(renderthread::RenderThread& thread); + virtual ~SkiaPipeline() {} + + TaskManager* getTaskManager() override; + + void onDestroyHardwareResources() override; + + bool pinImages(std::vector<SkImage*>& mutableImages) override; + bool pinImages(LsaVector<sk_sp<Bitmap>>& images) override { return false; } + void unpinImages() override; + + void renderLayers(const FrameBuilder::LightGeometry& lightGeometry, + LayerUpdateQueue* layerUpdateQueue, bool opaque, + const BakedOpRenderer::LightInfo& lightInfo) override; + + bool createOrUpdateLayer(RenderNode* node, + const DamageAccumulator& damageAccumulator) override; + + void renderFrame(const LayerUpdateQueue& layers, const SkRect& clip, + const std::vector< sp<RenderNode> >& nodes, bool opaque, const Rect &contentDrawBounds, + sk_sp<SkSurface> surface); + + static void destroyLayer(RenderNode* node); + + static void prepareToDraw(const renderthread::RenderThread& thread, Bitmap* bitmap); + + static void renderLayersImpl(const LayerUpdateQueue& layers, bool opaque); + + static bool skpCaptureEnabled() { return false; } + + static float getLightRadius() { + if (CC_UNLIKELY(Properties::overrideLightRadius > 0)) { + return Properties::overrideLightRadius; + } + return mLightRadius; + } + + static uint8_t getAmbientShadowAlpha() { + if (CC_UNLIKELY(Properties::overrideAmbientShadowStrength >= 0)) { + return Properties::overrideAmbientShadowStrength; + } + return mAmbientShadowAlpha; + } + + static uint8_t getSpotShadowAlpha() { + if (CC_UNLIKELY(Properties::overrideSpotShadowStrength >= 0)) { + return Properties::overrideSpotShadowStrength; + } + return mSpotShadowAlpha; + } + + static Vector3 getLightCenter() { + if (CC_UNLIKELY(Properties::overrideLightPosY > 0 || Properties::overrideLightPosZ > 0)) { + Vector3 adjustedLightCenter = mLightCenter; + if (CC_UNLIKELY(Properties::overrideLightPosY > 0)) { + // negated since this shifts up + adjustedLightCenter.y = - Properties::overrideLightPosY; + } + if (CC_UNLIKELY(Properties::overrideLightPosZ > 0)) { + adjustedLightCenter.z = Properties::overrideLightPosZ; + } + return adjustedLightCenter; + } + return mLightCenter; + } + + static void updateLighting(const FrameBuilder::LightGeometry& lightGeometry, + const BakedOpRenderer::LightInfo& lightInfo) { + mLightRadius = lightGeometry.radius; + mAmbientShadowAlpha = lightInfo.ambientShadowAlpha; + mSpotShadowAlpha = lightInfo.spotShadowAlpha; + mLightCenter = lightGeometry.center; + } + +protected: + void dumpResourceCacheUsage() const; + + renderthread::RenderThread& mRenderThread; + +private: + void renderFrameImpl(const LayerUpdateQueue& layers, const SkRect& clip, + const std::vector< sp<RenderNode> >& nodes, bool opaque, 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>); + + TaskManager mTaskManager; + std::vector<sk_sp<SkImage>> mPinnedImages; + static float mLightRadius; + static uint8_t mAmbientShadowAlpha; + static uint8_t mSpotShadowAlpha; + static Vector3 mLightCenter; +}; + +} /* namespace skiapipeline */ +} /* namespace uirenderer */ +} /* namespace android */ diff --git a/libs/hwui/pipeline/skia/SkiaProfileRenderer.cpp b/libs/hwui/pipeline/skia/SkiaProfileRenderer.cpp new file mode 100644 index 000000000000..d97fb372fe0c --- /dev/null +++ b/libs/hwui/pipeline/skia/SkiaProfileRenderer.cpp @@ -0,0 +1,45 @@ +/* + * 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 "SkiaProfileRenderer.h" + +namespace android { +namespace uirenderer { + +void SkiaProfileRenderer::drawRect(float left, float top, float right, float bottom, + const SkPaint& paint) { + SkRect rect = SkRect::MakeLTRB(left, top, right, bottom); + mCanvas->drawRect(rect, paint); +} + +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]); + mCanvas->drawRect(rect, paint); + } +} + +uint32_t SkiaProfileRenderer::getViewportWidth() { + return mCanvas->imageInfo().width(); +} + +uint32_t SkiaProfileRenderer::getViewportHeight() { + return mCanvas->imageInfo().height(); +} + +} /* namespace uirenderer */ +} /* namespace android */ diff --git a/libs/hwui/pipeline/skia/SkiaProfileRenderer.h b/libs/hwui/pipeline/skia/SkiaProfileRenderer.h new file mode 100644 index 000000000000..e6b7f8307379 --- /dev/null +++ b/libs/hwui/pipeline/skia/SkiaProfileRenderer.h @@ -0,0 +1,42 @@ +/* + * 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 "IProfileRenderer.h" + +#include "BakedOpRenderer.h" + +namespace android { +namespace uirenderer { + +class SkiaProfileRenderer : public IProfileRenderer { +public: + 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; + uint32_t getViewportWidth() override; + uint32_t getViewportHeight() override; + + virtual ~SkiaProfileRenderer() {} + +private: + // Does not have ownership. + SkCanvas* mCanvas; +}; + +} /* namespace uirenderer */ +} /* namespace android */ diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp new file mode 100644 index 000000000000..dbe0296eae24 --- /dev/null +++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp @@ -0,0 +1,248 @@ +/* + * 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 "SkiaRecordingCanvas.h" + +#include "Layer.h" +#include "RenderNode.h" +#include "LayerDrawable.h" +#include "NinePatchUtils.h" +#include "pipeline/skia/AnimatedDrawables.h" +#include <SkImagePriv.h> + +namespace android { +namespace uirenderer { +namespace skiapipeline { + +// ---------------------------------------------------------------------------- +// Recording Canvas Setup +// ---------------------------------------------------------------------------- + +void SkiaRecordingCanvas::initDisplayList(uirenderer::RenderNode* renderNode, int width, + int height) { + mCurrentBarrier = nullptr; + SkASSERT(mDisplayList.get() == nullptr); + + if (renderNode) { + mDisplayList = renderNode->detachAvailableList(); + } + SkRect bounds = SkRect::MakeWH(width, height); + if (mDisplayList) { + mDisplayList->reset(bounds); + } else { + mDisplayList.reset(new SkiaDisplayList(bounds)); + } + + mRecorder.reset(mDisplayList->mDrawable.get()); + SkiaCanvas::reset(&mRecorder); +} + +uirenderer::DisplayList* SkiaRecordingCanvas::finishRecording() { + // close any existing chunks if necessary + insertReorderBarrier(false); + mRecorder.restoreToCount(1); + return mDisplayList.release(); +} + +// ---------------------------------------------------------------------------- +// Recording Canvas draw operations: View System +// ---------------------------------------------------------------------------- + +void SkiaRecordingCanvas::drawRoundRect(uirenderer::CanvasPropertyPrimitive* left, + uirenderer::CanvasPropertyPrimitive* top, uirenderer::CanvasPropertyPrimitive* right, + uirenderer::CanvasPropertyPrimitive* bottom, uirenderer::CanvasPropertyPrimitive* rx, + uirenderer::CanvasPropertyPrimitive* ry, uirenderer::CanvasPropertyPaint* 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) { + drawDrawable(mDisplayList->allocateDrawable<AnimatedCircle>(x, y, radius, paint)); +} + +void SkiaRecordingCanvas::insertReorderBarrier(bool enableReorder) { + if (nullptr != mCurrentBarrier) { + // finish off the existing chunk + SkDrawable* drawable = + mDisplayList->allocateDrawable<EndReorderBarrierDrawable>( + mCurrentBarrier); + mCurrentBarrier = nullptr; + drawDrawable(drawable); + } + if (enableReorder) { + mCurrentBarrier = (StartReorderBarrierDrawable*) + mDisplayList->allocateDrawable<StartReorderBarrierDrawable>( + mDisplayList.get()); + drawDrawable(mCurrentBarrier); + } +} + +void SkiaRecordingCanvas::drawLayer(uirenderer::DeferredLayerUpdater* layerUpdater) { + if (layerUpdater != nullptr && layerUpdater->backingLayer() != nullptr) { + uirenderer::Layer* layer = layerUpdater->backingLayer(); + sk_sp<SkDrawable> drawable(new LayerDrawable(layer)); + drawDrawable(drawable.get()); + } +} + +void SkiaRecordingCanvas::drawRenderNode(uirenderer::RenderNode* renderNode) { + // record the child node + mDisplayList->mChildNodes.emplace_back(renderNode, asSkCanvas(), true, mCurrentBarrier); + auto& renderNodeDrawable = mDisplayList->mChildNodes.back(); + drawDrawable(&renderNodeDrawable); + + // use staging property, since recording on UI thread + if (renderNode->stagingProperties().isProjectionReceiver()) { + mDisplayList->mProjectionReceiver = &renderNodeDrawable; + // set projectionReceiveIndex so that RenderNode.hasProjectionReceiver returns true + mDisplayList->projectionReceiveIndex = mDisplayList->mChildNodes.size() - 1; + } +} + +void SkiaRecordingCanvas::callDrawGLFunction(Functor* functor, + uirenderer::GlFunctorLifecycleListener* listener) { + mDisplayList->mChildFunctors.emplace_back(functor, listener, asSkCanvas()); + drawDrawable(&mDisplayList->mChildFunctors.back()); +} + +class VectorDrawable : public SkDrawable { + public: + VectorDrawable(VectorDrawableRoot* tree) : mRoot(tree) {} + + protected: + virtual SkRect onGetBounds() override { + return SkRect::MakeLargest(); + } + virtual void onDraw(SkCanvas* canvas) override { + Bitmap& hwuiBitmap = mRoot->getBitmapUpdateIfDirty(); + SkBitmap bitmap; + hwuiBitmap.getSkBitmap(&bitmap); + SkPaint* paint = mRoot->getPaint(); + canvas->drawBitmapRect(bitmap, mRoot->mutateProperties()->getBounds(), paint); + /* + * TODO we can draw this directly but need to address the following... + * + * 1) Add drawDirect(SkCanvas*) to VectorDrawableRoot + * 2) fix VectorDrawable.cpp's Path::draw to not make a temporary path + * so that we don't break caching + * 3) figure out how to set path's as volatile during animation + * 4) if mRoot->getPaint() != null either promote to layer (during + * animation) or cache in SkSurface (for static content) + * + */ + } + + private: + sp<VectorDrawableRoot> mRoot; +}; + +void SkiaRecordingCanvas::drawVectorDrawable(VectorDrawableRoot* tree) { + drawDrawable(mDisplayList->allocateDrawable<VectorDrawable>(tree)); + mDisplayList->mVectorDrawables.push_back(tree); +} + +// ---------------------------------------------------------------------------- +// Recording Canvas draw operations: Bitmaps +// ---------------------------------------------------------------------------- + +inline static const SkPaint* nonAAPaint(const SkPaint* origPaint, SkPaint* tmpPaint) { + if (origPaint && origPaint->isAntiAlias()) { + *tmpPaint = *origPaint; + tmpPaint->setAntiAlias(false); + return tmpPaint; + } else { + return origPaint; + } +} + +void SkiaRecordingCanvas::drawBitmap(Bitmap& bitmap, float left, float top, const SkPaint* paint) { + SkBitmap skBitmap; + bitmap.getSkBitmap(&skBitmap); + + sk_sp<SkImage> image = SkMakeImageFromRasterBitmap(skBitmap, kNever_SkCopyPixelsMode); + if (!skBitmap.isImmutable()) { + mDisplayList->mMutableImages.push_back(image.get()); + } + SkPaint tmpPaint; + mRecorder.drawImage(image, left, top, nonAAPaint(paint, &tmpPaint)); +} + +void SkiaRecordingCanvas::drawBitmap(Bitmap& hwuiBitmap, const SkMatrix& matrix, + const SkPaint* paint) { + SkBitmap bitmap; + hwuiBitmap.getSkBitmap(&bitmap); + SkAutoCanvasRestore acr(&mRecorder, true); + concat(matrix); + sk_sp<SkImage> image = SkMakeImageFromRasterBitmap(bitmap, kNever_SkCopyPixelsMode); + if (!bitmap.isImmutable()) { + mDisplayList->mMutableImages.push_back(image.get()); + } + SkPaint tmpPaint; + mRecorder.drawImage(image, 0, 0, nonAAPaint(paint, &tmpPaint)); +} + +void SkiaRecordingCanvas::drawBitmap(Bitmap& hwuiBitmap, float srcLeft, float srcTop, + float srcRight, float srcBottom, float dstLeft, float dstTop, float dstRight, + float dstBottom, const SkPaint* paint) { + SkBitmap bitmap; + hwuiBitmap.getSkBitmap(&bitmap); + SkRect srcRect = SkRect::MakeLTRB(srcLeft, srcTop, srcRight, srcBottom); + SkRect dstRect = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom); + sk_sp<SkImage> image = SkMakeImageFromRasterBitmap(bitmap, kNever_SkCopyPixelsMode); + if (!bitmap.isImmutable()) { + mDisplayList->mMutableImages.push_back(image.get()); + } + SkPaint tmpPaint; + mRecorder.drawImageRect(image, srcRect, dstRect, nonAAPaint(paint, &tmpPaint)); +} + +void SkiaRecordingCanvas::drawNinePatch(Bitmap& hwuiBitmap, const Res_png_9patch& chunk, + float dstLeft, float dstTop, float dstRight, float dstBottom, const SkPaint* paint) { + SkBitmap bitmap; + hwuiBitmap.getSkBitmap(&bitmap); + + SkCanvas::Lattice lattice; + NinePatchUtils::SetLatticeDivs(&lattice, chunk, bitmap.width(), bitmap.height()); + + lattice.fFlags = 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. + // Skia requires placeholder flags for degenerate rects. + numFlags = (lattice.fXCount + 1) * (lattice.fYCount + 1); + } + + SkAutoSTMalloc<25, SkCanvas::Lattice::Flags> flags(numFlags); + if (numFlags > 0) { + NinePatchUtils::SetLatticeFlags(&lattice, flags.get(), numFlags, chunk); + } + + lattice.fBounds = nullptr; + SkRect dst = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom); + sk_sp<SkImage> image = SkMakeImageFromRasterBitmap(bitmap, kNever_SkCopyPixelsMode); + if (!bitmap.isImmutable()) { + mDisplayList->mMutableImages.push_back(image.get()); + } + + SkPaint tmpPaint; + mRecorder.drawImageLattice(image.get(), lattice, dst, nonAAPaint(paint, &tmpPaint)); +} + +}; // namespace skiapipeline +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h new file mode 100644 index 000000000000..10829f87efb9 --- /dev/null +++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h @@ -0,0 +1,93 @@ +/* + * 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. + */ +#pragma once + +#include "SkiaCanvas.h" +#include "SkiaDisplayList.h" +#include "ReorderBarrierDrawables.h" +#include <SkLiteRecorder.h> + +namespace android { +namespace uirenderer { +namespace skiapipeline { + +/** + * A SkiaCanvas implementation that records drawing operations for deferred rendering backed by a + * SkLiteRecorder and a SkiaDisplayList. + */ +class SkiaRecordingCanvas : public SkiaCanvas { + public: + explicit SkiaRecordingCanvas(uirenderer::RenderNode* renderNode, int width, int height) { + initDisplayList(renderNode, width, height); + } + + virtual void setBitmap(const SkBitmap& bitmap) override { + LOG_ALWAYS_FATAL("DisplayListCanvas is not backed by a bitmap."); + } + + virtual void resetRecording(int width, int height, + 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 drawNinePatch(Bitmap& hwuiBitmap, const android::Res_png_9patch& chunk, + float dstLeft, float dstTop, float dstRight, float dstBottom, + const SkPaint* paint) 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; + virtual void drawCircle(uirenderer::CanvasPropertyPrimitive* x, + uirenderer::CanvasPropertyPrimitive* y, uirenderer::CanvasPropertyPrimitive* radius, + uirenderer::CanvasPropertyPaint* paint) override; + + virtual void drawVectorDrawable(VectorDrawableRoot* vectorDrawable) override; + + virtual void insertReorderBarrier(bool enableReorder) override; + virtual void drawLayer(uirenderer::DeferredLayerUpdater* layerHandle) override; + virtual void drawRenderNode(uirenderer::RenderNode* renderNode) override; + virtual void callDrawGLFunction(Functor* functor, + uirenderer::GlFunctorLifecycleListener* listener) override; + +private: + SkLiteRecorder mRecorder; + std::unique_ptr<SkiaDisplayList> mDisplayList; + StartReorderBarrierDrawable* mCurrentBarrier; + + /** + * A new SkiaDisplayList is created or recycled if available. + * + * @param renderNode is optional and used to recycle an old display list. + * @param width used to calculate recording bounds. + * @param height used to calculate recording bounds. + */ + void initDisplayList(uirenderer::RenderNode* renderNode, int width, int height); +}; + +}; // namespace skiapipeline +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp new file mode 100644 index 000000000000..910c339c4d1c --- /dev/null +++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp @@ -0,0 +1,160 @@ +/* + * 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 "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 <SkSurface.h> +#include <SkTypes.h> + +#include <GrContext.h> +#include <GrTypes.h> +#include <vk/GrVkTypes.h> + +#include <android/native_window.h> +#include <cutils/properties.h> +#include <strings.h> + +using namespace android::uirenderer::renderthread; + +namespace android { +namespace uirenderer { +namespace skiapipeline { + +SkiaVulkanPipeline::SkiaVulkanPipeline(renderthread::RenderThread& thread) + : SkiaPipeline(thread) + , mVkManager(thread.vulkanManager()) {} + +MakeCurrentResult SkiaVulkanPipeline::makeCurrent() { + return MakeCurrentResult::AlreadyCurrent; +} + +Frame SkiaVulkanPipeline::getFrame() { + LOG_ALWAYS_FATAL_IF(mVkSurface == nullptr, + "drawRenderNode called on a context with no surface!"); + + SkSurface* backBuffer = mVkManager.getBackbufferSurface(mVkSurface); + if (backBuffer == nullptr) { + SkDebugf("failed to get backbuffer"); + return Frame(-1, -1, 0); + } + + Frame frame(backBuffer->width(), backBuffer->height(), mVkManager.getAge(mVkSurface)); + 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, + 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, contentDrawBounds, backBuffer); + layerUpdateQueue->clear(); + + // Draw visual debugging features + if (CC_UNLIKELY(Properties::showDirtyRegions + || ProfileType::None != Properties::getProfileType())) { + SkCanvas* profileCanvas = backBuffer->getCanvas(); + SkiaProfileRenderer profileRenderer(profileCanvas); + profiler->draw(profileRenderer); + profileCanvas->flush(); + } + + // Log memory statistics + if (CC_UNLIKELY(Properties::debugLevel != kDebugDisabled)) { + dumpResourceCacheUsage(); + } + + return true; +} + +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 + // metrics the frame was swapped at this point + currentFrameInfo->markSwapBuffers(); + + if (*requireSwap) { + mVkManager.swapBuffers(mVkSurface); + } + + return *requireSwap; +} + +bool SkiaVulkanPipeline::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) { + // TODO: implement copyLayerInto for vulkan. + return false; +} + +DeferredLayerUpdater* SkiaVulkanPipeline::createTextureLayer() { + mVkManager.initialize(); + + VkLayer* layer = new VkLayer(mRenderThread.renderState(), 0, 0); + return new DeferredLayerUpdater(layer); +} + +void SkiaVulkanPipeline::onStop() { +} + +bool SkiaVulkanPipeline::setSurface(Surface* surface, SwapBehavior swapBehavior) { + if (mVkSurface) { + mVkManager.destroySurface(mVkSurface); + mVkSurface = nullptr; + } + + if (surface) { + mVkSurface = mVkManager.createSurface(surface); + } + + return mVkSurface != nullptr; +} + +bool SkiaVulkanPipeline::isSurfaceReady() { + return CC_UNLIKELY(mVkSurface != nullptr); +} + +bool SkiaVulkanPipeline::isContextReady() { + return CC_LIKELY(mVkManager.hasVkContext()); +} + +void SkiaVulkanPipeline::invokeFunctor(const RenderThread& thread, Functor* functor) { + // TODO: we currently don't support OpenGL WebView's + DrawGlInfo::Mode mode = DrawGlInfo::kModeProcessNoContext; + (*functor)(mode, nullptr); +} + +} /* namespace skiapipeline */ +} /* namespace uirenderer */ +} /* namespace android */ diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h new file mode 100644 index 000000000000..aab1d7a547c0 --- /dev/null +++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h @@ -0,0 +1,58 @@ +/* + * 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. + */ + +#pragma once + +#include "SkiaPipeline.h" +#include "renderthread/VulkanManager.h" + +namespace android { +namespace uirenderer { +namespace skiapipeline { + +class SkiaVulkanPipeline : public SkiaPipeline { +public: + SkiaVulkanPipeline(renderthread::RenderThread& thread); + virtual ~SkiaVulkanPipeline() {} + + 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, + 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; + bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) override; + DeferredLayerUpdater* createTextureLayer() override; + bool setSurface(Surface* window, renderthread::SwapBehavior swapBehavior) override; + void onStop() override; + bool isSurfaceReady() override; + bool isContextReady() override; + + static void invokeFunctor(const renderthread::RenderThread& thread, Functor* functor); + +private: + renderthread::VulkanManager& mVkManager; + renderthread::VulkanSurface* mVkSurface = nullptr; +}; + +} /* namespace skiapipeline */ +} /* namespace uirenderer */ +} /* namespace android */ diff --git a/libs/hwui/renderstate/Blend.cpp b/libs/hwui/renderstate/Blend.cpp index 93f787d31745..8865c6efce8c 100644 --- a/libs/hwui/renderstate/Blend.cpp +++ b/libs/hwui/renderstate/Blend.cpp @@ -25,70 +25,70 @@ namespace uirenderer { * Structure mapping Skia xfermodes to OpenGL blending factors. */ struct Blender { - SkXfermode::Mode mode; + SkBlendMode mode; GLenum src; GLenum dst; }; // assumptions made by lookup tables in either this file or ProgramCache -static_assert(0 == SkXfermode::kClear_Mode, "SkXfermode enums have changed"); -static_assert(1 == SkXfermode::kSrc_Mode, "SkXfermode enums have changed"); -static_assert(2 == SkXfermode::kDst_Mode, "SkXfermode enums have changed"); -static_assert(3 == SkXfermode::kSrcOver_Mode, "SkXfermode enums have changed"); -static_assert(4 == SkXfermode::kDstOver_Mode, "SkXfermode enums have changed"); -static_assert(5 == SkXfermode::kSrcIn_Mode, "SkXfermode enums have changed"); -static_assert(6 == SkXfermode::kDstIn_Mode, "SkXfermode enums have changed"); -static_assert(7 == SkXfermode::kSrcOut_Mode, "SkXfermode enums have changed"); -static_assert(8 == SkXfermode::kDstOut_Mode, "SkXfermode enums have changed"); -static_assert(9 == SkXfermode::kSrcATop_Mode, "SkXfermode enums have changed"); -static_assert(10 == SkXfermode::kDstATop_Mode, "SkXfermode enums have changed"); -static_assert(11 == SkXfermode::kXor_Mode, "SkXfermode enums have changed"); -static_assert(12 == SkXfermode::kPlus_Mode, "SkXfermode enums have changed"); -static_assert(13 == SkXfermode::kModulate_Mode, "SkXfermode enums have changed"); -static_assert(14 == SkXfermode::kScreen_Mode, "SkXfermode enums have changed"); -static_assert(15 == SkXfermode::kOverlay_Mode, "SkXfermode enums have changed"); -static_assert(16 == SkXfermode::kDarken_Mode, "SkXfermode enums have changed"); -static_assert(17 == SkXfermode::kLighten_Mode, "SkXfermode enums have changed"); +static_assert(0 == static_cast<int>(SkBlendMode::kClear), "SkBlendMode enums have changed"); +static_assert(1 == static_cast<int>(SkBlendMode::kSrc), "SkBlendMode enums have changed"); +static_assert(2 == static_cast<int>(SkBlendMode::kDst), "SkBlendMode enums have changed"); +static_assert(3 == static_cast<int>(SkBlendMode::kSrcOver), "SkBlendMode enums have changed"); +static_assert(4 == static_cast<int>(SkBlendMode::kDstOver), "SkBlendMode enums have changed"); +static_assert(5 == static_cast<int>(SkBlendMode::kSrcIn), "SkBlendMode enums have changed"); +static_assert(6 == static_cast<int>(SkBlendMode::kDstIn), "SkBlendMode enums have changed"); +static_assert(7 == static_cast<int>(SkBlendMode::kSrcOut), "SkBlendMode enums have changed"); +static_assert(8 == static_cast<int>(SkBlendMode::kDstOut), "SkBlendMode enums have changed"); +static_assert(9 == static_cast<int>(SkBlendMode::kSrcATop), "SkBlendMode enums have changed"); +static_assert(10 == static_cast<int>(SkBlendMode::kDstATop), "SkBlendMode enums have changed"); +static_assert(11 == static_cast<int>(SkBlendMode::kXor), "SkBlendMode enums have changed"); +static_assert(12 == static_cast<int>(SkBlendMode::kPlus), "SkBlendMode enums have changed"); +static_assert(13 == static_cast<int>(SkBlendMode::kModulate), "SkBlendMode enums have changed"); +static_assert(14 == static_cast<int>(SkBlendMode::kScreen), "SkBlendMode enums have changed"); +static_assert(15 == static_cast<int>(SkBlendMode::kOverlay), "SkBlendMode enums have changed"); +static_assert(16 == static_cast<int>(SkBlendMode::kDarken), "SkBlendMode enums have changed"); +static_assert(17 == static_cast<int>(SkBlendMode::kLighten), "SkBlendMode enums have changed"); // In this array, the index of each Blender equals the value of the first -// entry. For instance, gBlends[1] == gBlends[SkXfermode::kSrc_Mode] +// entry. For instance, gBlends[1] == gBlends[SkBlendMode::kSrc] const Blender kBlends[] = { - { SkXfermode::kClear_Mode, GL_ZERO, GL_ONE_MINUS_SRC_ALPHA }, - { SkXfermode::kSrc_Mode, GL_ONE, GL_ZERO }, - { SkXfermode::kDst_Mode, GL_ZERO, GL_ONE }, - { SkXfermode::kSrcOver_Mode, GL_ONE, GL_ONE_MINUS_SRC_ALPHA }, - { SkXfermode::kDstOver_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ONE }, - { SkXfermode::kSrcIn_Mode, GL_DST_ALPHA, GL_ZERO }, - { SkXfermode::kDstIn_Mode, GL_ZERO, GL_SRC_ALPHA }, - { SkXfermode::kSrcOut_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ZERO }, - { SkXfermode::kDstOut_Mode, GL_ZERO, GL_ONE_MINUS_SRC_ALPHA }, - { SkXfermode::kSrcATop_Mode, GL_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA }, - { SkXfermode::kDstATop_Mode, GL_ONE_MINUS_DST_ALPHA, GL_SRC_ALPHA }, - { SkXfermode::kXor_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA }, - { SkXfermode::kPlus_Mode, GL_ONE, GL_ONE }, - { SkXfermode::kModulate_Mode, GL_ZERO, GL_SRC_COLOR }, - { SkXfermode::kScreen_Mode, GL_ONE, GL_ONE_MINUS_SRC_COLOR } + { 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 SkXfermode. For instance +// 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[] = { - { SkXfermode::kClear_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ZERO }, - { SkXfermode::kSrc_Mode, GL_ZERO, GL_ONE }, - { SkXfermode::kDst_Mode, GL_ONE, GL_ZERO }, - { SkXfermode::kSrcOver_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ONE }, - { SkXfermode::kDstOver_Mode, GL_ONE, GL_ONE_MINUS_SRC_ALPHA }, - { SkXfermode::kSrcIn_Mode, GL_ZERO, GL_SRC_ALPHA }, - { SkXfermode::kDstIn_Mode, GL_DST_ALPHA, GL_ZERO }, - { SkXfermode::kSrcOut_Mode, GL_ZERO, GL_ONE_MINUS_SRC_ALPHA }, - { SkXfermode::kDstOut_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ZERO }, - { SkXfermode::kSrcATop_Mode, GL_ONE_MINUS_DST_ALPHA, GL_SRC_ALPHA }, - { SkXfermode::kDstATop_Mode, GL_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA }, - { SkXfermode::kXor_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA }, - { SkXfermode::kPlus_Mode, GL_ONE, GL_ONE }, - { SkXfermode::kModulate_Mode, GL_DST_COLOR, GL_ZERO }, - { SkXfermode::kScreen_Mode, GL_ONE_MINUS_DST_COLOR, GL_ONE } + { 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() @@ -111,9 +111,10 @@ void Blend::syncEnabled() { } } -void Blend::getFactors(SkXfermode::Mode mode, ModeOrderSwap modeUsage, GLenum* outSrc, GLenum* outDst) { - *outSrc = (modeUsage == ModeOrderSwap::Swap) ? kBlendsSwap[mode].src : kBlends[mode].src; - *outDst = (modeUsage == ModeOrderSwap::Swap) ? kBlendsSwap[mode].dst : kBlends[mode].dst; +void Blend::getFactors(SkBlendMode mode, ModeOrderSwap modeUsage, GLenum* outSrc, GLenum* outDst) { + int index = static_cast<int>(mode); + *outSrc = (modeUsage == ModeOrderSwap::Swap) ? kBlendsSwap[index].src : kBlends[index].src; + *outDst = (modeUsage == ModeOrderSwap::Swap) ? kBlendsSwap[index].dst : kBlends[index].dst; } void Blend::setFactors(GLenum srcMode, GLenum dstMode) { diff --git a/libs/hwui/renderstate/Blend.h b/libs/hwui/renderstate/Blend.h index df9e5a8af879..ec0e114c998f 100644 --- a/libs/hwui/renderstate/Blend.h +++ b/libs/hwui/renderstate/Blend.h @@ -20,7 +20,7 @@ #include <GLES2/gl2.h> #include <GLES2/gl2ext.h> -#include <SkXfermode.h> +#include <SkBlendMode.h> #include <memory> namespace android { @@ -36,7 +36,7 @@ public: }; void syncEnabled(); - static void getFactors(SkXfermode::Mode mode, ModeOrderSwap modeUsage, + static void getFactors(SkBlendMode mode, ModeOrderSwap modeUsage, GLenum* outSrc, GLenum* outDst); void setFactors(GLenum src, GLenum dst); diff --git a/libs/hwui/renderstate/MeshState.cpp b/libs/hwui/renderstate/MeshState.cpp index b575c696586e..6d0293695412 100644 --- a/libs/hwui/renderstate/MeshState.cpp +++ b/libs/hwui/renderstate/MeshState.cpp @@ -17,8 +17,6 @@ #include "Program.h" -#include "ShadowTessellator.h" - namespace android { namespace uirenderer { @@ -100,6 +98,12 @@ 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) { + bindMeshBuffer(buffer); + glBufferSubData(GL_ARRAY_BUFFER, offset, size, data); +} + void MeshState::deleteMeshBuffer(GLuint buffer) { if (buffer == mCurrentBuffer) { // GL defines that deleting the currently bound VBO rebinds to 0 (no VBO). diff --git a/libs/hwui/renderstate/MeshState.h b/libs/hwui/renderstate/MeshState.h index dd684686f584..17ad4622e44a 100644 --- a/libs/hwui/renderstate/MeshState.h +++ b/libs/hwui/renderstate/MeshState.h @@ -72,6 +72,7 @@ public: void unbindMeshBuffer(); void genOrUpdateMeshBuffer(GLuint* buffer, GLsizeiptr size, const void* data, GLenum usage); + void updateMeshBufferSubData(GLuint buffer, GLintptr offset, GLsizeiptr size, const void* data); void deleteMeshBuffer(GLuint); /////////////////////////////////////////////////////////////////////////////// diff --git a/libs/hwui/renderstate/OffscreenBufferPool.cpp b/libs/hwui/renderstate/OffscreenBufferPool.cpp index 10a26e08f897..a9bbb273dbb5 100644 --- a/libs/hwui/renderstate/OffscreenBufferPool.cpp +++ b/libs/hwui/renderstate/OffscreenBufferPool.cpp @@ -22,6 +22,7 @@ #include "utils/FatVector.h" #include "utils/TraceUtils.h" +#include <utils/Color.h> #include <utils/Log.h> #include <GLES2/gl2.h> @@ -44,7 +45,7 @@ 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, GL_RGBA); + texture.resize(width, height, 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 diff --git a/libs/hwui/renderstate/RenderState.cpp b/libs/hwui/renderstate/RenderState.cpp index 5e600644ca19..17ee390c90bd 100644 --- a/libs/hwui/renderstate/RenderState.cpp +++ b/libs/hwui/renderstate/RenderState.cpp @@ -13,6 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#include "GlLayer.h" +#include "VkLayer.h" #include <GpuMemoryTracker.h> #include "renderstate/RenderState.h" @@ -40,7 +42,7 @@ RenderState::~RenderState() { void RenderState::onGLContextCreated() { LOG_ALWAYS_FATAL_IF(mBlend || mMeshState || mScissor || mStencil, "State object lifecycle not managed correctly"); - GpuMemoryTracker::onGLContextCreated(); + GpuMemoryTracker::onGpuContextCreated(); mBlend = new Blend(); mMeshState = new MeshState(); @@ -52,51 +54,19 @@ void RenderState::onGLContextCreated() { mCaches = &Caches::createInstance(*this); } mCaches->init(); - mCaches->textureCache.setAssetAtlas(&mAssetAtlas); } static void layerLostGlContext(Layer* layer) { - layer->onGlContextLost(); + LOG_ALWAYS_FATAL_IF(layer->getApi() != Layer::Api::OpenGL, + "layerLostGlContext on non GL layer"); + static_cast<GlLayer*>(layer)->onGlContextLost(); } void RenderState::onGLContextDestroyed() { -/* - size_t size = mActiveLayers.size(); - if (CC_UNLIKELY(size != 0)) { - ALOGE("Crashing, have %d contexts and %d layers at context destruction. isempty %d", - mRegisteredContexts.size(), size, mActiveLayers.empty()); - mCaches->dumpMemoryUsage(); - for (std::set<renderthread::CanvasContext*>::iterator cit = mRegisteredContexts.begin(); - cit != mRegisteredContexts.end(); cit++) { - renderthread::CanvasContext* context = *cit; - ALOGE("Context: %p (root = %p)", context, context->mRootRenderNode.get()); - ALOGE(" Prefeteched layers: %zu", context->mPrefetechedLayers.size()); - for (std::set<RenderNode*>::iterator pit = context->mPrefetechedLayers.begin(); - pit != context->mPrefetechedLayers.end(); pit++) { - (*pit)->debugDumpLayers(" "); - } - context->mRootRenderNode->debugDumpLayers(" "); - } - - - if (mActiveLayers.begin() == mActiveLayers.end()) { - ALOGE("set has become empty. wat."); - } - for (std::set<const Layer*>::iterator lit = mActiveLayers.begin(); - lit != mActiveLayers.end(); lit++) { - const Layer* layer = *(lit); - ALOGE("Layer %p, state %d, texlayer %d, fbo %d, buildlayered %d", - layer, layer->state, layer->isTextureLayer(), layer->getFbo(), layer->wasBuildLayered); - } - LOG_ALWAYS_FATAL("%d layers have survived gl context destruction", size); - } -*/ - mLayerPool.clear(); // TODO: reset all cached state in state objects std::for_each(mActiveLayers.begin(), mActiveLayers.end(), layerLostGlContext); - mAssetAtlas.terminate(); mCaches->terminate(); @@ -109,7 +79,29 @@ void RenderState::onGLContextDestroyed() { delete mStencil; mStencil = nullptr; - GpuMemoryTracker::onGLContextDestroyed(); + GpuMemoryTracker::onGpuContextDestroyed(); +} + +void RenderState::onVkContextCreated() { + LOG_ALWAYS_FATAL_IF(mBlend || mMeshState || mScissor || mStencil, + "State object lifecycle not managed correctly"); + GpuMemoryTracker::onGpuContextCreated(); +} + +static void layerDestroyedVkContext(Layer* layer) { + LOG_ALWAYS_FATAL_IF(layer->getApi() != Layer::Api::Vulkan, + "layerLostVkContext on non Vulkan layer"); + static_cast<VkLayer*>(layer)->onVkContextDestroyed(); +} + +void RenderState::onVkContextDestroyed() { + mLayerPool.clear(); + std::for_each(mActiveLayers.begin(), mActiveLayers.end(), layerDestroyedVkContext); + GpuMemoryTracker::onGpuContextDestroyed(); +} + +GrContext* RenderState::getGrContext() const { + return mRenderThread.getGrContext(); } void RenderState::flush(Caches::FlushMode mode) { @@ -179,9 +171,17 @@ void RenderState::interruptForFunctorInvoke() { meshState().resetVertexPointers(); meshState().disableTexCoordsVertexArray(); debugOverdraw(false, false); + // TODO: We need a way to know whether the functor is sRGB aware (b/32072673) + if (mCaches->extensions().hasSRGBWriteControl()) { + glDisable(GL_FRAMEBUFFER_SRGB_EXT); + } } void RenderState::resumeFromFunctorInvoke() { + if (mCaches->extensions().hasSRGBWriteControl()) { + glEnable(GL_FRAMEBUFFER_SRGB_EXT); + } + glViewport(0, 0, mViewportWidth, mViewportHeight); glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer); debugOverdraw(false, false); @@ -304,12 +304,12 @@ void RenderState::render(const Glop& glop, const Matrix4& orthoMatrix) { // texture always takes slot 0, shader samplers increment from there mCaches->textureState().activateTexture(0); - mCaches->textureState().bindTexture(texture.target, texture.texture->id()); + mCaches->textureState().bindTexture(texture.texture->target(), texture.texture->id()); if (texture.clamp != GL_INVALID_ENUM) { - texture.texture->setWrap(texture.clamp, false, false, texture.target); + texture.texture->setWrap(texture.clamp, false, false); } if (texture.filter != GL_INVALID_ENUM) { - texture.texture->setFilter(texture.filter, false, false, texture.target); + texture.texture->setFilter(texture.filter, false, false); } if (texture.textureTransform) { @@ -340,7 +340,7 @@ void RenderState::render(const Glop& glop, const Matrix4& orthoMatrix) { glVertexAttribPointer(alphaLocation, 1, GL_FLOAT, GL_FALSE, vertices.stride, alphaCoords); } // Shader uniforms - SkiaShader::apply(*mCaches, fill.skiaShaderData); + SkiaShader::apply(*mCaches, fill.skiaShaderData, mViewportWidth, mViewportHeight); GL_CHECKPOINT(MODERATE); Texture* texture = (fill.skiaShaderData.skiaShaderType & kBitmap_SkiaShaderType) ? diff --git a/libs/hwui/renderstate/RenderState.h b/libs/hwui/renderstate/RenderState.h index 9e0fb121be65..d183a15f3842 100644 --- a/libs/hwui/renderstate/RenderState.h +++ b/libs/hwui/renderstate/RenderState.h @@ -16,7 +16,6 @@ #ifndef RENDERSTATE_H #define RENDERSTATE_H -#include "AssetAtlas.h" #include "Caches.h" #include "Glop.h" #include "renderstate/Blend.h" @@ -36,6 +35,8 @@ #include <utils/RefBase.h> #include <private/hwui/DrawGlInfo.h> +class GrContext; + namespace android { namespace uirenderer { @@ -57,6 +58,9 @@ public: void onGLContextCreated(); void onGLContextDestroyed(); + void onVkContextCreated(); + void onVkContextDestroyed(); + void flush(Caches::FlushMode flushMode); void setViewport(GLsizei width, GLsizei height); @@ -92,7 +96,6 @@ public: void render(const Glop& glop, const Matrix4& orthoMatrix); - AssetAtlas& assetAtlas() { return mAssetAtlas; } Blend& blend() { return *mBlend; } MeshState& meshState() { return *mMeshState; } Scissor& scissor() { return *mScissor; } @@ -100,6 +103,8 @@ public: OffscreenBufferPool& layerPool() { return mLayerPool; } + GrContext* getGrContext() const; + void dump(); private: @@ -120,7 +125,6 @@ private: OffscreenBufferPool mLayerPool; - AssetAtlas mAssetAtlas; std::set<Layer*> mActiveLayers; std::set<renderthread::CanvasContext*> mRegisteredContexts; diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index 975ac8368e3d..1b3bf96998dd 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -19,17 +19,19 @@ #include "AnimationContext.h" #include "Caches.h" -#include "DeferredLayerUpdater.h" #include "EglManager.h" +#include "Frame.h" #include "LayerUpdateQueue.h" -#include "LayerRenderer.h" -#include "OpenGLRenderer.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 "utils/GLUtils.h" #include "utils/TimeUtils.h" @@ -61,15 +63,89 @@ namespace android { namespace uirenderer { namespace renderthread { +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)); + case RenderPipelineType::SkiaGL: + return new CanvasContext(thread, translucent, rootRenderNode, contextFactory, + std::make_unique<skiapipeline::SkiaOpenGLPipeline>(thread)); + case RenderPipelineType::SkiaVulkan: + return new CanvasContext(thread, translucent, rootRenderNode, contextFactory, + std::make_unique<skiapipeline::SkiaVulkanPipeline>(thread)); + default: + LOG_ALWAYS_FATAL("canvas context type %d not supported", (int32_t) renderType); + break; + } + return nullptr; +} + +void CanvasContext::destroyLayer(RenderNode* node) { + auto renderType = Properties::getRenderPipelineType(); + switch (renderType) { + case RenderPipelineType::OpenGL: + OpenGLPipeline::destroyLayer(node); + break; + case RenderPipelineType::SkiaGL: + case RenderPipelineType::SkiaVulkan: + skiapipeline::SkiaPipeline::destroyLayer(node); + break; + default: + LOG_ALWAYS_FATAL("canvas context type %d not supported", (int32_t) renderType); + break; + } +} + +void CanvasContext::invokeFunctor(const RenderThread& thread, Functor* functor) { + ATRACE_CALL(); + auto renderType = Properties::getRenderPipelineType(); + switch (renderType) { + case RenderPipelineType::OpenGL: + OpenGLPipeline::invokeFunctor(thread, functor); + break; + case RenderPipelineType::SkiaGL: + skiapipeline::SkiaOpenGLPipeline::invokeFunctor(thread, functor); + break; + case RenderPipelineType::SkiaVulkan: + skiapipeline::SkiaVulkanPipeline::invokeFunctor(thread, functor); + break; + default: + LOG_ALWAYS_FATAL("canvas context type %d not supported", (int32_t) renderType); + break; + } +} + +void CanvasContext::prepareToDraw(const RenderThread& thread, Bitmap* bitmap) { + auto renderType = Properties::getRenderPipelineType(); + switch (renderType) { + case RenderPipelineType::OpenGL: + OpenGLPipeline::prepareToDraw(thread, bitmap); + break; + case RenderPipelineType::SkiaGL: + case RenderPipelineType::SkiaVulkan: + skiapipeline::SkiaPipeline::prepareToDraw(thread, bitmap); + break; + default: + LOG_ALWAYS_FATAL("canvas context type %d not supported", (int32_t) renderType); + break; + } +} + CanvasContext::CanvasContext(RenderThread& thread, bool translucent, - RenderNode* rootRenderNode, IContextFactory* contextFactory) + RenderNode* rootRenderNode, IContextFactory* contextFactory, + std::unique_ptr<IRenderPipeline> renderPipeline) : mRenderThread(thread) - , mEglManager(thread.eglManager()) , mOpaque(!translucent) , mAnimationContext(contextFactory->createAnimationContext(mRenderThread.timeLord())) , mJankTracker(thread.mainDisplayInfo()) , mProfiler(mFrames) - , mContentDrawBounds(0, 0, 0, 0) { + , mContentDrawBounds(0, 0, 0, 0) + , mRenderPipeline(std::move(renderPipeline)) { mRenderNodes.emplace_back(rootRenderNode); mRenderThread.renderState().registerCanvasContext(this); mProfiler.setDensity(mRenderThread.mainDisplayInfo().density); @@ -86,12 +162,6 @@ void CanvasContext::destroy(TreeObserver* observer) { freePrefetchedLayers(observer); destroyHardwareResources(observer); mAnimationContext->destroy(); -#if !HWUI_NEW_OPS - if (mCanvas) { - delete mCanvas; - mCanvas = nullptr; - } -#endif } void CanvasContext::setSurface(Surface* surface) { @@ -99,24 +169,15 @@ void CanvasContext::setSurface(Surface* surface) { mNativeSurface = surface; - if (mEglSurface != EGL_NO_SURFACE) { - mEglManager.destroySurface(mEglSurface); - mEglSurface = EGL_NO_SURFACE; - } - - if (surface) { - mEglSurface = mEglManager.createSurface(surface); - } + bool hasSurface = mRenderPipeline->setSurface(surface, mSwapBehavior); mFrameNumber = -1; - if (mEglSurface != EGL_NO_SURFACE) { - const bool preserveBuffer = (mSwapBehavior != kSwap_discardBuffer); - mBufferPreserved = mEglManager.setPreserveBuffer(mEglSurface, preserveBuffer); - mHaveNewSurface = true; - mSwapHistory.clear(); + if (hasSurface) { + mHaveNewSurface = true; + mSwapHistory.clear(); } else { - mRenderThread.removeFrameCallback(this); + mRenderThread.removeFrameCallback(this); } } @@ -126,11 +187,6 @@ void CanvasContext::setSwapBehavior(SwapBehavior swapBehavior) { void CanvasContext::initialize(Surface* surface) { setSurface(surface); -#if !HWUI_NEW_OPS - if (mCanvas) return; - mCanvas = new OpenGLRenderer(mRenderThread.renderState()); - mCanvas->initProperties(); -#endif } void CanvasContext::updateSurface(Surface* surface) { @@ -146,35 +202,22 @@ void CanvasContext::setStopped(bool stopped) { mStopped = stopped; if (mStopped) { mRenderThread.removeFrameCallback(this); - if (mEglManager.isCurrent(mEglSurface)) { - mEglManager.makeCurrent(EGL_NO_SURFACE); - } + mRenderPipeline->onStop(); } else if (mIsDirty && hasSurface()) { mRenderThread.postFrameCallback(this); } } } -// TODO: don't pass viewport size, it's automatic via EGL -void CanvasContext::setup(int width, int height, float lightRadius, +void CanvasContext::setup(float lightRadius, uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha) { -#if HWUI_NEW_OPS mLightGeometry.radius = lightRadius; mLightInfo.ambientShadowAlpha = ambientShadowAlpha; mLightInfo.spotShadowAlpha = spotShadowAlpha; -#else - if (!mCanvas) return; - mCanvas->initLight(lightRadius, ambientShadowAlpha, spotShadowAlpha); -#endif } void CanvasContext::setLightCenter(const Vector3& lightCenter) { -#if HWUI_NEW_OPS mLightGeometry.center = lightCenter; -#else - if (!mCanvas) return; - mCanvas->setLightCenter(lightCenter); -#endif } void CanvasContext::setOpaque(bool opaque) { @@ -184,14 +227,23 @@ void CanvasContext::setOpaque(bool opaque) { bool CanvasContext::makeCurrent() { if (mStopped) return false; - // TODO: Figure out why this workaround is needed, see b/13913604 - // In the meantime this matches the behavior of GLRenderer, so it is not a regression - EGLint error = 0; - mHaveNewSurface |= mEglManager.makeCurrent(mEglSurface, &error); - if (error) { - setSurface(nullptr); + auto result = mRenderPipeline->makeCurrent(); + switch (result) { + case MakeCurrentResult::AlreadyCurrent: + return true; + case MakeCurrentResult::Failed: + mHaveNewSurface = true; + setSurface(nullptr); + return false; + case MakeCurrentResult::Succeeded: + mHaveNewSurface = true; + return true; + default: + LOG_ALWAYS_FATAL("unexpected result %d from IRenderPipeline::makeCurrent", + (int32_t) result); } - return !error; + + return true; } static bool wasSkipped(FrameInfo* info) { @@ -254,11 +306,7 @@ void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo, mCurrentFrameInfo->markSyncStart(); info.damageAccumulator = &mDamageAccumulator; -#if HWUI_NEW_OPS info.layerUpdateQueue = &mLayerUpdateQueue; -#else - info.renderer = mCanvas; -#endif mAnimationContext->startFrame(info.mode); for (const sp<RenderNode>& node : mRenderNodes) { @@ -283,7 +331,7 @@ void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo, return; } - if (CC_LIKELY(mSwapHistory.size())) { + if (CC_LIKELY(mSwapHistory.size() && !Properties::forceDrawFrame)) { nsecs_t latestVsync = mRenderThread.timeLord().latestVsync(); SwapHistory& lastSwap = mSwapHistory.back(); nsecs_t vsyncDelta = std::abs(lastSwap.vsyncTime - latestVsync); @@ -335,11 +383,6 @@ void CanvasContext::notifyFramePending() { } void CanvasContext::draw() { -#if !HWUI_NEW_OPS - LOG_ALWAYS_FATAL_IF(!mCanvas || mEglSurface == EGL_NO_SURFACE, - "drawRenderNode called on a context with no canvas or surface!"); -#endif - SkRect dirty; mDamageAccumulator.finish(&dirty); @@ -351,215 +394,27 @@ void CanvasContext::draw() { mCurrentFrameInfo->markIssueDrawCommandsStart(); - Frame frame = mEglManager.beginFrame(mEglSurface); + Frame frame = mRenderPipeline->getFrame(); - if (frame.width() != mLastFrameWidth || frame.height() != mLastFrameHeight) { - // can't rely on prior content of window if viewport size changes - dirty.setEmpty(); - mLastFrameWidth = frame.width(); - mLastFrameHeight = frame.height(); - } else if (mHaveNewSurface || frame.bufferAge() == 0) { - // New surface needs a full draw - 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()); - dirty.setEmpty(); - } - profiler().unionDirty(&dirty); - } + SkRect windowDirty = computeDirtyRect(frame, &dirty); - if (dirty.isEmpty()) { - dirty.set(0, 0, frame.width(), frame.height()); - } - - // At this point dirty is the area of the screen to update. However, - // the area of the frame we need to repaint is potentially different, so - // stash the screen area for later - SkRect screenDirty(dirty); - - // If the buffer age is 0 we do a full-screen repaint (handled above) - // If the buffer age is 1 the buffer contents are the same as they were - // 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()) { - // 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()); - } else { - // At this point we haven't yet added the latest frame - // to the damage history (happens below) - // So we need to damage - for (int i = mSwapHistory.size() - 1; - i > ((int) mSwapHistory.size()) - frame.bufferAge(); i--) { - dirty.join(mSwapHistory[i].damage); - } - } - } - - mEglManager.damageFrame(frame, dirty); - -#if HWUI_NEW_OPS - auto& caches = Caches::getInstance(); - FrameBuilder frameBuilder(dirty, frame.width(), frame.height(), mLightGeometry, caches); - - frameBuilder.deferLayers(mLayerUpdateQueue); - mLayerUpdateQueue.clear(); - - frameBuilder.deferRenderNodeScene(mRenderNodes, mContentDrawBounds); - - BakedOpRenderer renderer(caches, mRenderThread.renderState(), - mOpaque, mLightInfo); - frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer); - profiler().draw(&renderer); - bool drew = renderer.didDraw(); - - // post frame cleanup - caches.clearGarbage(); - caches.pathCache.trim(); - caches.tessellationCache.trim(); - -#if DEBUG_MEMORY_USAGE - mCaches.dumpMemoryUsage(); -#else - if (CC_UNLIKELY(Properties::debugLevel & kDebugMemory)) { - caches.dumpMemoryUsage(); - } -#endif - -#else - mCanvas->prepareDirty(frame.width(), frame.height(), - dirty.fLeft, dirty.fTop, dirty.fRight, dirty.fBottom, mOpaque); - - Rect outBounds; - // It there are multiple render nodes, they are laid out as follows: - // #0 - backdrop (content + caption) - // #1 - content (positioned at (0,0) and clipped to - its bounds mContentDrawBounds) - // #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 - // cropped to the backdrop (since that indicates a shrinking of the window). - // - // Additional nodes will be drawn on top with no particular clipping semantics. - - // The bounds of the backdrop against which the content should be clipped. - Rect backdropBounds = mContentDrawBounds; - // Usually the contents bounds should be mContentDrawBounds - however - we will - // move it towards the fixed edge to give it a more stable appearance (for the moment). - Rect contentBounds; - // If there is no content bounds we ignore the layering as stated above and start with 2. - int layer = (mContentDrawBounds.isEmpty() || mRenderNodes.size() == 1) ? 2 : 0; - // Draw all render nodes. Note that - for (const sp<RenderNode>& node : mRenderNodes) { - if (layer == 0) { // Backdrop. - // Draw the backdrop clipped to the inverse content bounds, but assume that the content - // was moved to the upper left corner. - const RenderProperties& properties = node->properties(); - Rect targetBounds(properties.getLeft(), properties.getTop(), - properties.getRight(), properties.getBottom()); - // Move the content bounds towards the fixed corner of the backdrop. - const int x = targetBounds.left; - const int y = targetBounds.top; - contentBounds.set(x, y, x + mContentDrawBounds.getWidth(), - y + mContentDrawBounds.getHeight()); - // Remember the intersection of the target bounds and the intersection bounds against - // which we have to crop the content. - backdropBounds.set(x, y, x + backdropBounds.getWidth(), y + backdropBounds.getHeight()); - backdropBounds.doIntersect(targetBounds); - // Check if we have to draw something on the left side ... - if (targetBounds.left < contentBounds.left) { - mCanvas->save(SaveFlags::Clip); - if (mCanvas->clipRect(targetBounds.left, targetBounds.top, - contentBounds.left, targetBounds.bottom, - SkRegion::kIntersect_Op)) { - mCanvas->drawRenderNode(node.get(), outBounds); - } - // Reduce the target area by the area we have just painted. - targetBounds.left = std::min(contentBounds.left, targetBounds.right); - mCanvas->restore(); - } - // ... or on the right side ... - if (targetBounds.right > contentBounds.right && - !targetBounds.isEmpty()) { - mCanvas->save(SaveFlags::Clip); - if (mCanvas->clipRect(contentBounds.right, targetBounds.top, - targetBounds.right, targetBounds.bottom, - SkRegion::kIntersect_Op)) { - mCanvas->drawRenderNode(node.get(), outBounds); - } - // Reduce the target area by the area we have just painted. - targetBounds.right = std::max(targetBounds.left, contentBounds.right); - mCanvas->restore(); - } - // ... or at the top ... - if (targetBounds.top < contentBounds.top && - !targetBounds.isEmpty()) { - mCanvas->save(SaveFlags::Clip); - if (mCanvas->clipRect(targetBounds.left, targetBounds.top, targetBounds.right, - contentBounds.top, - SkRegion::kIntersect_Op)) { - mCanvas->drawRenderNode(node.get(), outBounds); - } - // Reduce the target area by the area we have just painted. - targetBounds.top = std::min(contentBounds.top, targetBounds.bottom); - mCanvas->restore(); - } - // ... or at the bottom. - if (targetBounds.bottom > contentBounds.bottom && - !targetBounds.isEmpty()) { - mCanvas->save(SaveFlags::Clip); - if (mCanvas->clipRect(targetBounds.left, contentBounds.bottom, targetBounds.right, - targetBounds.bottom, SkRegion::kIntersect_Op)) { - mCanvas->drawRenderNode(node.get(), outBounds); - } - mCanvas->restore(); - } - } else if (layer == 1) { // Content - // It gets cropped against the bounds of the backdrop to stay inside. - mCanvas->save(SaveFlags::MatrixClip); - - // We shift and clip the content to match its final location in the window. - const float left = mContentDrawBounds.left; - const float top = mContentDrawBounds.top; - const float dx = backdropBounds.left - left; - const float dy = backdropBounds.top - top; - const float width = backdropBounds.getWidth(); - const float height = backdropBounds.getHeight(); - - mCanvas->translate(dx, dy); - if (mCanvas->clipRect(left, top, left + width, top + height, SkRegion::kIntersect_Op)) { - mCanvas->drawRenderNode(node.get(), outBounds); - } - mCanvas->restore(); - } else { // draw the rest on top at will! - mCanvas->drawRenderNode(node.get(), outBounds); - } - layer++; - } - - profiler().draw(mCanvas); - - bool drew = mCanvas->finish(); -#endif + bool drew = mRenderPipeline->draw(frame, windowDirty, dirty, mLightGeometry, &mLayerUpdateQueue, + mContentDrawBounds, mOpaque, mLightInfo, mRenderNodes, &(profiler())); waitOnFences(); - GL_CHECKPOINT(LOW); + bool requireSwap = false; + bool didSwap = mRenderPipeline->swapBuffers(frame, drew, windowDirty, mCurrentFrameInfo, + &requireSwap); - // Even if we decided to cancel the frame, from the perspective of jank - // metrics the frame was swapped at this point - mCurrentFrameInfo->markSwapBuffers(); mIsDirty = false; - if (drew || mEglManager.damageRequiresSwap()) { - if (CC_UNLIKELY(!mEglManager.swapBuffers(frame, screenDirty))) { + if (requireSwap) { + if (!didSwap) { //some error happened setSurface(nullptr); } SwapHistory& swap = mSwapHistory.next(); - swap.damage = screenDirty; + swap.damage = windowDirty; swap.swapCompletedTime = systemTime(CLOCK_MONOTONIC); swap.vsyncTime = mRenderThread.timeLord().latestVsync(); if (mNativeSurface.get()) { @@ -609,18 +464,18 @@ void CanvasContext::draw() { GpuMemoryTracker::onFrameCompleted(); #ifdef BUGREPORT_FONT_CACHE_USAGE - caches.fontRenderer.getFontRenderer().historyTracker().frameCompleted(); + auto renderType = Properties::getRenderPipelineType(); + if (RenderPipelineType::OpenGL == renderType) { + Caches& caches = Caches::getInstance(); + caches.fontRenderer.getFontRenderer().historyTracker().frameCompleted(); + } #endif } // Called by choreographer to do an RT-driven animation void CanvasContext::doFrame() { -#if HWUI_NEW_OPS - if (CC_UNLIKELY(mEglSurface == EGL_NO_SURFACE)) return; -#else - if (CC_UNLIKELY(!mCanvas || mEglSurface == EGL_NO_SURFACE)) return; -#endif + if (!mRenderPipeline->isSurfaceReady()) return; prepareAndDraw(nullptr); } @@ -643,16 +498,6 @@ void CanvasContext::prepareAndDraw(RenderNode* node) { } } -void CanvasContext::invokeFunctor(RenderThread& thread, Functor* functor) { - ATRACE_CALL(); - DrawGlInfo::Mode mode = DrawGlInfo::kModeProcessNoContext; - if (thread.eglManager().hasEglContext()) { - mode = DrawGlInfo::kModeProcess; - } - - thread.renderState().invokeFunctor(functor, mode, nullptr); -} - void CanvasContext::markLayerInUse(RenderNode* node) { if (mPrefetchedLayers.erase(node)) { node->decStrong(nullptr); @@ -673,10 +518,7 @@ void CanvasContext::freePrefetchedLayers(TreeObserver* observer) { void CanvasContext::buildLayer(RenderNode* node, TreeObserver* observer) { ATRACE_CALL(); - if (!mEglManager.hasEglContext()) return; -#if !HWUI_NEW_OPS - if (!mCanvas) return; -#endif + if (!mRenderPipeline->isContextReady()) return; // buildLayer() will leave the tree in an unknown state, so we must stop drawing stopDrawing(); @@ -684,11 +526,7 @@ void CanvasContext::buildLayer(RenderNode* node, TreeObserver* observer) { TreeInfo info(TreeInfo::MODE_FULL, *this); info.damageAccumulator = &mDamageAccumulator; info.observer = observer; -#if HWUI_NEW_OPS info.layerUpdateQueue = &mLayerUpdateQueue; -#else - info.renderer = mCanvas; -#endif info.runAnimations = false; node->prepareTree(info); SkRect ignore; @@ -697,41 +535,24 @@ void CanvasContext::buildLayer(RenderNode* node, TreeObserver* observer) { // purposes when the frame is actually drawn node->setPropertyFieldsDirty(RenderNode::GENERIC); -#if HWUI_NEW_OPS - static const std::vector< sp<RenderNode> > emptyNodeList; - auto& caches = Caches::getInstance(); - FrameBuilder frameBuilder(mLayerUpdateQueue, mLightGeometry, caches); - mLayerUpdateQueue.clear(); - BakedOpRenderer renderer(caches, mRenderThread.renderState(), - mOpaque, mLightInfo); - LOG_ALWAYS_FATAL_IF(renderer.didDraw(), "shouldn't draw in buildlayer case"); - frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer); -#else - mCanvas->markLayersAsBuildLayers(); - mCanvas->flushLayerUpdates(); -#endif + mRenderPipeline->renderLayers(mLightGeometry, &mLayerUpdateQueue, mOpaque, mLightInfo); node->incStrong(nullptr); mPrefetchedLayers.insert(node); } bool CanvasContext::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) { - layer->apply(); - return LayerRenderer::copyLayer(mRenderThread.renderState(), layer->backingLayer(), bitmap); + return mRenderPipeline->copyLayerInto(layer, bitmap); } void CanvasContext::destroyHardwareResources(TreeObserver* observer) { stopDrawing(); - if (mEglManager.hasEglContext()) { + if (mRenderPipeline->isContextReady()) { freePrefetchedLayers(observer); for (const sp<RenderNode>& node : mRenderNodes) { node->destroyHardwareResources(observer); } - Caches& caches = Caches::getInstance(); - // Make sure to release all the textures we were owning as there won't - // be another draw - caches.textureCache.resetMarkInUse(this); - mRenderThread.renderState().flush(Caches::FlushMode::Layers); + mRenderPipeline->onDestroyHardwareResources(); } } @@ -748,20 +569,8 @@ void CanvasContext::trimMemory(RenderThread& thread, int level) { } } -void CanvasContext::runWithGlContext(RenderTask* task) { - LOG_ALWAYS_FATAL_IF(!mEglManager.hasEglContext(), - "GL context not initialized!"); - task->run(); -} - -Layer* CanvasContext::createTextureLayer() { - mEglManager.initialize(); - return LayerRenderer::createTextureLayer(mRenderThread.renderState()); -} - -void CanvasContext::setTextureAtlas(RenderThread& thread, - const sp<GraphicBuffer>& buffer, int64_t* map, size_t mapSize) { - thread.eglManager().setTextureAtlas(buffer, map, mapSize); +DeferredLayerUpdater* CanvasContext::createTextureLayer() { + return mRenderPipeline->createTextureLayer(); } void CanvasContext::dumpFrames(int fd) { @@ -836,8 +645,8 @@ void CanvasContext::waitOnFences() { class CanvasContext::FuncTaskProcessor : public TaskProcessor<bool> { public: - FuncTaskProcessor(Caches& caches) - : TaskProcessor<bool>(&caches.tasks) {} + explicit FuncTaskProcessor(TaskManager* taskManager) + : TaskProcessor<bool>(taskManager) {} virtual void onProcess(const sp<Task<bool> >& task) override { FuncTask* t = static_cast<FuncTask*>(task.get()); @@ -848,7 +657,7 @@ public: void CanvasContext::enqueueFrameWork(std::function<void()>&& func) { if (!mFrameWorkProcessor.get()) { - mFrameWorkProcessor = new FuncTaskProcessor(Caches::getInstance()); + mFrameWorkProcessor = new FuncTaskProcessor(mRenderPipeline->getTaskManager()); } sp<FuncTask> task(new FuncTask()); task->func = func; @@ -864,6 +673,56 @@ int64_t CanvasContext::getFrameNumber() { return mFrameNumber; } +SkRect CanvasContext::computeDirtyRect(const Frame& frame, SkRect* dirty) { + if (frame.width() != mLastFrameWidth || frame.height() != mLastFrameHeight) { + // can't rely on prior content of window if viewport size changes + dirty->setEmpty(); + mLastFrameWidth = frame.width(); + mLastFrameHeight = frame.height(); + } else if (mHaveNewSurface || frame.bufferAge() == 0) { + // New surface needs a full draw + 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()); + dirty->setEmpty(); + } + profiler().unionDirty(dirty); + } + + if (dirty->isEmpty()) { + dirty->set(0, 0, frame.width(), frame.height()); + } + + // At this point dirty is the area of the window to update. However, + // the area of the frame we need to repaint is potentially different, so + // stash the screen area for later + SkRect windowDirty(*dirty); + + // If the buffer age is 0 we do a full-screen repaint (handled above) + // If the buffer age is 1 the buffer contents are the same as they were + // 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()) { + // 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()); + } else { + // At this point we haven't yet added the latest frame + // to the damage history (happens below) + // So we need to damage + for (int i = mSwapHistory.size() - 1; + i > ((int) mSwapHistory.size()) - frame.bufferAge(); i--) { + dirty->join(mSwapHistory[i].damage); + } + } + } + + return windowDirty; +} + } /* namespace renderthread */ } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h index e1821751b57f..0174b86d22d5 100644 --- a/libs/hwui/renderthread/CanvasContext.h +++ b/libs/hwui/renderthread/CanvasContext.h @@ -14,14 +14,17 @@ * limitations under the License. */ -#ifndef CANVASCONTEXT_H_ -#define CANVASCONTEXT_H_ +#pragma once +#include "BakedOpDispatcher.h" +#include "BakedOpRenderer.h" #include "DamageAccumulator.h" +#include "FrameBuilder.h" #include "FrameInfo.h" #include "FrameInfoVisualizer.h" #include "FrameMetricsReporter.h" #include "IContextFactory.h" +#include "IRenderPipeline.h" #include "LayerUpdateQueue.h" #include "RenderNode.h" #include "thread/Task.h" @@ -30,12 +33,6 @@ #include "renderthread/RenderTask.h" #include "renderthread/RenderThread.h" -#if HWUI_NEW_OPS -#include "BakedOpDispatcher.h" -#include "BakedOpRenderer.h" -#include "FrameBuilder.h" -#endif - #include <cutils/compiler.h> #include <EGL/egl.h> #include <SkBitmap.h> @@ -53,29 +50,71 @@ namespace uirenderer { class AnimationContext; class DeferredLayerUpdater; -class OpenGLRenderer; -class Rect; class Layer; +class Rect; class RenderState; namespace renderthread { class EglManager; - -enum SwapBehavior { - kSwap_default, - kSwap_discardBuffer, -}; +class Frame; // This per-renderer class manages the bridge between the global EGL context // and the render surface. // TODO: Rename to Renderer or some other per-window, top-level manager class CanvasContext : public IFrameCallback { public: - CanvasContext(RenderThread& thread, bool translucent, RenderNode* rootRenderNode, - IContextFactory* contextFactory); + static CanvasContext* create(RenderThread& thread, bool translucent, + RenderNode* rootRenderNode, IContextFactory* contextFactory); virtual ~CanvasContext(); + /** + * Update or create a layer specific for the provided RenderNode. The layer + * attached to the node will be specific to the RenderPipeline used by this + * context + * + * @return true if the layer has been created or updated + */ + bool createOrUpdateLayer(RenderNode* node, const DamageAccumulator& dmgAccumulator) { + return mRenderPipeline->createOrUpdateLayer(node, dmgAccumulator); + } + + /** + * Pin any mutable images to the GPU cache. A pinned images is guaranteed to + * remain in the cache until it has been unpinned. We leverage this feature + * to avoid making a CPU copy of the pixels. + * + * @return true if all images have been successfully pinned to the GPU cache + * and false otherwise (e.g. cache limits have been exceeded). + */ + bool pinImages(std::vector<SkImage*>& mutableImages) { + return mRenderPipeline->pinImages(mutableImages); + } + bool pinImages(LsaVector<sk_sp<Bitmap>>& images) { + return mRenderPipeline->pinImages(images); + } + + /** + * Unpin any image that had be previously pinned to the GPU cache + */ + void unpinImages() { mRenderPipeline->unpinImages(); } + + /** + * Destroy any layers that have been attached to the provided RenderNode removing + * any state that may have been set during createOrUpdateLayer(). + */ + static void destroyLayer(RenderNode* node); + + static void invokeFunctor(const RenderThread& thread, Functor* functor); + + static void prepareToDraw(const RenderThread& thread, Bitmap* bitmap); + + /* + * If Properties::isSkiaEnabled() is true then this will return the Skia + * grContext associated with the current RenderPipeline. + */ + GrContext* getGrContext() const { return mRenderThread.getGrContext(); } + // Won't take effect until next EGLSurface creation void setSwapBehavior(SwapBehavior swapBehavior); @@ -85,7 +124,7 @@ public: void setStopped(bool stopped); bool hasSurface() { return mNativeSurface.get(); } - void setup(int width, int height, float lightRadius, + void setup(float lightRadius, uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha); void setLightCenter(const Vector3& lightCenter); void setOpaque(bool opaque); @@ -106,14 +145,7 @@ public: void destroyHardwareResources(TreeObserver* observer); static void trimMemory(RenderThread& thread, int level); - static void invokeFunctor(RenderThread& thread, Functor* functor); - - void runWithGlContext(RenderTask* task); - - Layer* createTextureLayer(); - - ANDROID_API static void setTextureAtlas(RenderThread& thread, - const sp<GraphicBuffer>& buffer, int64_t* map, size_t mapSize); + DeferredLayerUpdater* createTextureLayer(); void stopDrawing(); void notifyFramePending(); @@ -171,6 +203,9 @@ public: void waitOnFences(); private: + CanvasContext(RenderThread& thread, bool translucent, RenderNode* rootRenderNode, + IContextFactory* contextFactory, std::unique_ptr<IRenderPipeline> renderPipeline); + friend class RegisterFrameCallbackTask; // TODO: Replace with something better for layer & other GL object // lifecycle tracking @@ -182,21 +217,20 @@ private: bool isSwapChainStuffed(); + SkRect computeDirtyRect(const Frame& frame, SkRect* dirty); + EGLint mLastFrameWidth = 0; EGLint mLastFrameHeight = 0; RenderThread& mRenderThread; - EglManager& mEglManager; sp<Surface> mNativeSurface; - EGLSurface mEglSurface = EGL_NO_SURFACE; // stopped indicates the CanvasContext will reject actual redraw operations, // and defer repaint until it is un-stopped bool mStopped = false; // CanvasContext is dirty if it has received an update that it has not // painted onto its surface. bool mIsDirty = false; - bool mBufferPreserved = false; - SwapBehavior mSwapBehavior = kSwap_default; + SwapBehavior mSwapBehavior = SwapBehavior::kSwap_default; struct SwapHistory { SkRect damage; nsecs_t vsyncTime; @@ -212,12 +246,8 @@ private: nsecs_t mLastDropVsync = 0; bool mOpaque; -#if HWUI_NEW_OPS BakedOpRenderer::LightInfo mLightInfo; FrameBuilder::LightGeometry mLightGeometry = { {0, 0, 0}, 0 }; -#else - OpenGLRenderer* mCanvas = nullptr; -#endif bool mHaveNewSurface = false; DamageAccumulator mDamageAccumulator; @@ -248,9 +278,9 @@ private: std::vector< sp<FuncTask> > mFrameFences; sp<TaskProcessor<bool> > mFrameWorkProcessor; + std::unique_ptr<IRenderPipeline> mRenderPipeline; }; } /* namespace renderthread */ } /* namespace uirenderer */ } /* namespace android */ -#endif /* CANVASCONTEXT_H_ */ diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp index e3b6dc6fd9fe..4ff54a5299f8 100644 --- a/libs/hwui/renderthread/DrawFrameTask.cpp +++ b/libs/hwui/renderthread/DrawFrameTask.cpp @@ -119,7 +119,7 @@ bool DrawFrameTask::syncFrameState(TreeInfo& info) { int64_t vsync = mFrameInfo[static_cast<int>(FrameInfoIndex::Vsync)]; mRenderThread->timeLord().vsyncReceived(vsync); bool canDraw = mContext->makeCurrent(); - Caches::getInstance().textureCache.resetMarkInUse(mContext); + mContext->unpinImages(); for (size_t i = 0; i < mLayers.size(); i++) { mLayers[i]->apply(); diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp index 99bc9a75e94b..7020be073ee0 100644 --- a/libs/hwui/renderthread/EglManager.cpp +++ b/libs/hwui/renderthread/EglManager.cpp @@ -24,10 +24,19 @@ #include "Caches.h" #include "DeviceInfo.h" +#include "Frame.h" #include "Properties.h" #include "RenderThread.h" #include "renderstate/RenderState.h" +#include "Texture.h" + #include <EGL/eglext.h> +#include <GrContextOptions.h> +#include <gl/GrGLInterface.h> + +#ifdef HWUI_GLES_WRAP_ENABLED +#include "debug/GlesDriver.h" +#endif #define GLES_VERSION 2 @@ -60,7 +69,7 @@ static const char* egl_error_str(EGLint error) { return "Unknown error"; } } -static const char* egl_error_str() { +const char* EglManager::eglErrorString() { return egl_error_str(eglGetError()); } @@ -69,33 +78,13 @@ static struct { bool setDamage = false; } EglExtensions; -void Frame::map(const SkRect& in, EGLint* out) const { - /* The rectangles are specified relative to the bottom-left of the surface - * and the x and y components of each rectangle specify the bottom-left - * position of that rectangle. - * - * HWUI does everything with 0,0 being top-left, so need to map - * the rect - */ - SkIRect idirty; - in.roundOut(&idirty); - EGLint y = mHeight - (idirty.y() + idirty.height()); - // layout: {x, y, width, height} - out[0] = idirty.x(); - out[1] = y; - out[2] = idirty.width(); - out[3] = idirty.height(); -} - EglManager::EglManager(RenderThread& thread) : mRenderThread(thread) , mEglDisplay(EGL_NO_DISPLAY) , mEglConfig(nullptr) , mEglContext(EGL_NO_CONTEXT) , mPBufferSurface(EGL_NO_SURFACE) - , mCurrentSurface(EGL_NO_SURFACE) - , mAtlasMap(nullptr) - , mAtlasMapSize(0) { + , mCurrentSurface(EGL_NO_SURFACE) { } void EglManager::initialize() { @@ -105,11 +94,11 @@ void EglManager::initialize() { mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); LOG_ALWAYS_FATAL_IF(mEglDisplay == EGL_NO_DISPLAY, - "Failed to get EGL_DEFAULT_DISPLAY! err=%s", egl_error_str()); + "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, egl_error_str()); + "Failed to initialize display %p! err=%s", mEglDisplay, eglErrorString()); ALOGI("Initialized EGL, version %d.%d", (int)major, (int)minor); @@ -130,7 +119,22 @@ void EglManager::initialize() { makeCurrent(mPBufferSurface); DeviceInfo::initialize(); mRenderThread.renderState().onGLContextCreated(); - initAtlas(); + + if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaGL) { +#ifdef HWUI_GLES_WRAP_ENABLED + debug::GlesDriver* driver = debug::GlesDriver::get(); + sk_sp<const GrGLInterface> glInterface(driver->getSkiaInterface()); +#else + sk_sp<const GrGLInterface> glInterface(GrGLCreateNativeInterface()); +#endif + LOG_ALWAYS_FATAL_IF(!glInterface.get()); + + GrContextOptions options; + options.fDisableDistanceFieldPaths = true; + options.fAllowPathMaskCaching = true; + mRenderThread.setGrContext(GrContext::Create(GrBackend::kOpenGL_GrBackend, + (GrBackendContext)glInterface.get(), options)); + } } void EglManager::initExtensions() { @@ -178,7 +182,7 @@ void EglManager::loadConfig() { loadConfig(); } else { // Failed to get a valid config - LOG_ALWAYS_FATAL("Failed to choose config, error = %s", egl_error_str()); + LOG_ALWAYS_FATAL("Failed to choose config, error = %s", eglErrorString()); } } } @@ -190,33 +194,7 @@ void EglManager::createContext() { }; mEglContext = eglCreateContext(mEglDisplay, mEglConfig, EGL_NO_CONTEXT, attribs); LOG_ALWAYS_FATAL_IF(mEglContext == EGL_NO_CONTEXT, - "Failed to create context, error = %s", egl_error_str()); -} - -void EglManager::setTextureAtlas(const sp<GraphicBuffer>& buffer, - int64_t* map, size_t mapSize) { - - // Already initialized - if (mAtlasBuffer.get()) { - ALOGW("Multiple calls to setTextureAtlas!"); - delete map; - return; - } - - mAtlasBuffer = buffer; - mAtlasMap = map; - mAtlasMapSize = mapSize; - - if (hasEglContext()) { - initAtlas(); - } -} - -void EglManager::initAtlas() { - if (mAtlasBuffer.get()) { - mRenderThread.renderState().assetAtlas().init(mAtlasBuffer, - mAtlasMap, mAtlasMapSize); - } + "Failed to create context, error = %s", eglErrorString()); } void EglManager::createPBufferSurface() { @@ -231,15 +209,24 @@ void EglManager::createPBufferSurface() { EGLSurface EglManager::createSurface(EGLNativeWindowType window) { initialize(); - EGLSurface surface = eglCreateWindowSurface(mEglDisplay, mEglConfig, window, nullptr); + + EGLint attribs[] = { +#ifdef ANDROID_ENABLE_LINEAR_BLENDING + EGL_GL_COLORSPACE_KHR, EGL_GL_COLORSPACE_SRGB_KHR, + EGL_COLORSPACE, EGL_COLORSPACE_sRGB, +#endif + EGL_NONE + }; + + EGLSurface surface = eglCreateWindowSurface(mEglDisplay, mEglConfig, window, attribs); LOG_ALWAYS_FATAL_IF(surface == EGL_NO_SURFACE, "Failed to create EGLSurface for window %p, eglErr = %s", - (void*) window, egl_error_str()); + (void*) window, eglErrorString()); if (mSwapBehavior != SwapBehavior::Preserved) { 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, egl_error_str()); + (void*) window, eglErrorString()); } return surface; @@ -250,13 +237,14 @@ void EglManager::destroySurface(EGLSurface surface) { makeCurrent(EGL_NO_SURFACE); } if (!eglDestroySurface(mEglDisplay, surface)) { - ALOGW("Failed to destroy surface %p, error=%s", (void*)surface, egl_error_str()); + ALOGW("Failed to destroy surface %p, error=%s", (void*)surface, eglErrorString()); } } void EglManager::destroy() { if (mEglDisplay == EGL_NO_DISPLAY) return; + mRenderThread.setGrContext(nullptr); mRenderThread.renderState().onGLContextDestroyed(); eglDestroyContext(mEglDisplay, mEglContext); eglDestroySurface(mEglDisplay, mPBufferSurface); @@ -284,7 +272,7 @@ bool EglManager::makeCurrent(EGLSurface surface, EGLint* errOut) { (void*)surface, egl_error_str(*errOut)); } else { LOG_ALWAYS_FATAL("Failed to make current on surface %p, error=%s", - (void*)surface, egl_error_str()); + (void*)surface, eglErrorString()); } } mCurrentSurface = surface; @@ -325,7 +313,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, egl_error_str()); + (void*)frame.mSurface, eglErrorString()); } } #endif @@ -379,14 +367,14 @@ bool EglManager::setPreserveBuffer(EGLSurface surface, bool preserve) { preserve ? EGL_BUFFER_PRESERVED : EGL_BUFFER_DESTROYED); if (!preserved) { ALOGW("Failed to set EGL_SWAP_BEHAVIOR on surface %p, error=%s", - (void*) surface, egl_error_str()); + (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, egl_error_str()); + (void*) surface, eglErrorString()); } } diff --git a/libs/hwui/renderthread/EglManager.h b/libs/hwui/renderthread/EglManager.h index 41047fecf960..025192511cd9 100644 --- a/libs/hwui/renderthread/EglManager.h +++ b/libs/hwui/renderthread/EglManager.h @@ -26,35 +26,14 @@ namespace android { namespace uirenderer { namespace renderthread { +class Frame; class RenderThread; -class EglManager; - -class Frame { -public: - EGLint width() const { return mWidth; } - EGLint height() const { return mHeight; } - - // See: https://www.khronos.org/registry/egl/extensions/EXT/EGL_EXT_buffer_age.txt - // for what this means - EGLint bufferAge() const { return mBufferAge; } - -private: - friend class EglManager; - - EGLSurface mSurface; - EGLint mWidth; - EGLint mHeight; - EGLint mBufferAge; - - // Maps from 0,0 in top-left to 0,0 in bottom-left - // If out is not an EGLint[4] you're going to have a bad time - void map(const SkRect& in, EGLint* out) const; -}; // This class contains the shared global EGL objects, such as EGLDisplay // and EGLConfig, which are re-used by CanvasContext class EglManager { public: + static const char* eglErrorString(); // Returns true on success, false on failure void initialize(); @@ -79,13 +58,10 @@ public: // Returns true iff the surface is now preserving buffers. bool setPreserveBuffer(EGLSurface surface, bool preserve); - void setTextureAtlas(const sp<GraphicBuffer>& buffer, int64_t* map, size_t mapSize); - void fence(); private: friend class RenderThread; - explicit EglManager(RenderThread& thread); // EglContext is never destroyed, method is purposely not implemented ~EglManager(); @@ -94,7 +70,6 @@ private: void createPBufferSurface(); void loadConfig(); void createContext(); - void initAtlas(); EGLint queryBufferAge(EGLSurface surface); RenderThread& mRenderThread; @@ -106,10 +81,6 @@ private: EGLSurface mCurrentSurface; - sp<GraphicBuffer> mAtlasBuffer; - int64_t* mAtlasMap; - size_t mAtlasMapSize; - enum class SwapBehavior { Discard, Preserved, diff --git a/libs/hwui/renderthread/Frame.cpp b/libs/hwui/renderthread/Frame.cpp new file mode 100644 index 000000000000..126bb093b4eb --- /dev/null +++ b/libs/hwui/renderthread/Frame.cpp @@ -0,0 +1,44 @@ +/* + * 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 "Frame.h" +#include <SkRect.h> + +namespace android { +namespace uirenderer { +namespace renderthread { + +void Frame::map(const SkRect& in, int32_t* out) const { + /* The rectangles are specified relative to the bottom-left of the surface + * and the x and y components of each rectangle specify the bottom-left + * position of that rectangle. + * + * HWUI does everything with 0,0 being top-left, so need to map + * the rect + */ + SkIRect idirty; + in.roundOut(&idirty); + int32_t y = mHeight - (idirty.y() + idirty.height()); + // layout: {x, y, width, height} + out[0] = idirty.x(); + out[1] = y; + out[2] = idirty.width(); + out[3] = idirty.height(); +} + +} /* namespace renderthread */ +} /* namespace uirenderer */ +} /* namespace android */ diff --git a/libs/hwui/renderthread/Frame.h b/libs/hwui/renderthread/Frame.h new file mode 100644 index 000000000000..99996fe40626 --- /dev/null +++ b/libs/hwui/renderthread/Frame.h @@ -0,0 +1,60 @@ +/* + * 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. + */ + +#pragma once + +#include <stdint.h> + +struct SkRect; +typedef void *EGLSurface; + +namespace android { +namespace uirenderer { +namespace renderthread { + +class Frame { +public: + Frame(int32_t width, int32_t height, int32_t bufferAge) + : mWidth(width) + , mHeight(height) + , mBufferAge(bufferAge) { } + + int32_t width() const { return mWidth; } + int32_t height() const { return mHeight; } + + // See: https://www.khronos.org/registry/egl/extensions/EXT/EGL_EXT_buffer_age.txt + // for what this means + int32_t bufferAge() const { return mBufferAge; } + +private: + Frame() {} + friend class EglManager; + + int32_t mWidth; + int32_t mHeight; + int32_t mBufferAge; + + EGLSurface mSurface; + + // Maps from 0,0 in top-left to 0,0 in bottom-left + // If out is not an int32_t[4] you're going to have a bad time + void map(const SkRect& in, int32_t* out) const; +}; + +} /* namespace renderthread */ +} /* namespace uirenderer */ +} /* namespace android */ + diff --git a/libs/hwui/renderthread/IRenderPipeline.h b/libs/hwui/renderthread/IRenderPipeline.h new file mode 100644 index 000000000000..45f6718a68fb --- /dev/null +++ b/libs/hwui/renderthread/IRenderPipeline.h @@ -0,0 +1,84 @@ +/* + * 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. + */ + +#pragma once + +#include "FrameInfoVisualizer.h" + +#include <SkRect.h> +#include <utils/RefBase.h> + +class GrContext; + +namespace android { + +class Surface; + +namespace uirenderer { + +class DeferredLayerUpdater; + +namespace renderthread { + +enum class SwapBehavior { + kSwap_default, + kSwap_discardBuffer, +}; + +enum class MakeCurrentResult { + AlreadyCurrent, + Failed, + Succeeded +}; + +class Frame; + +class IRenderPipeline { +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, + 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; + virtual bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) = 0; + virtual DeferredLayerUpdater* createTextureLayer() = 0; + virtual bool setSurface(Surface* window, SwapBehavior swapBehavior) = 0; + virtual void onStop() = 0; + virtual bool isSurfaceReady() = 0; + virtual bool isContextReady() = 0; + virtual void onDestroyHardwareResources() = 0; + virtual void renderLayers(const FrameBuilder::LightGeometry& lightGeometry, + LayerUpdateQueue* layerUpdateQueue, bool opaque, + const BakedOpRenderer::LightInfo& lightInfo) = 0; + virtual TaskManager* getTaskManager() = 0; + virtual bool createOrUpdateLayer(RenderNode* node, + const DamageAccumulator& damageAccumulator) = 0; + virtual bool pinImages(std::vector<SkImage*>& mutableImages) = 0; + virtual bool pinImages(LsaVector<sk_sp<Bitmap>>& images) = 0; + virtual void unpinImages() = 0; + + virtual ~IRenderPipeline() {} +}; + +} /* namespace renderthread */ +} /* namespace uirenderer */ +} /* namespace android */ diff --git a/libs/hwui/renderthread/OpenGLPipeline.cpp b/libs/hwui/renderthread/OpenGLPipeline.cpp new file mode 100644 index 000000000000..df40a44a16cb --- /dev/null +++ b/libs/hwui/renderthread/OpenGLPipeline.cpp @@ -0,0 +1,266 @@ +/* + * 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 "OpenGLPipeline.h" + +#include "DeferredLayerUpdater.h" +#include "EglManager.h" +#include "Frame.h" +#include "GlLayer.h" +#include "ProfileRenderer.h" +#include "renderstate/RenderState.h" +#include "OpenGLReadback.h" + +#include <android/native_window.h> +#include <cutils/properties.h> +#include <strings.h> + +namespace android { +namespace uirenderer { +namespace renderthread { + +OpenGLPipeline::OpenGLPipeline(RenderThread& thread) + : mEglManager(thread.eglManager()) + , mRenderThread(thread) { +} + +MakeCurrentResult OpenGLPipeline::makeCurrent() { + // TODO: Figure out why this workaround is needed, see b/13913604 + // In the meantime this matches the behavior of GLRenderer, so it is not a regression + EGLint error = 0; + bool haveNewSurface = mEglManager.makeCurrent(mEglSurface, &error); + + Caches::getInstance().textureCache.resetMarkInUse(this); + if (!haveNewSurface) { + return MakeCurrentResult::AlreadyCurrent; + } + return error ? MakeCurrentResult::Failed : MakeCurrentResult::Succeeded; +} + +Frame OpenGLPipeline::getFrame() { + LOG_ALWAYS_FATAL_IF(mEglSurface == EGL_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, + 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); + + frameBuilder.deferLayers(*layerUpdateQueue); + layerUpdateQueue->clear(); + + frameBuilder.deferRenderNodeScene(renderNodes, contentDrawBounds); + + BakedOpRenderer renderer(caches, mRenderThread.renderState(), + opaque, lightInfo); + frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer); + ProfileRenderer profileRenderer(renderer); + profiler->draw(profileRenderer); + drew = renderer.didDraw(); + + // post frame cleanup + caches.clearGarbage(); + caches.pathCache.trim(); + caches.tessellationCache.trim(); + +#if DEBUG_MEMORY_USAGE + caches.dumpMemoryUsage(); +#else + if (CC_UNLIKELY(Properties::debugLevel & kDebugMemory)) { + caches.dumpMemoryUsage(); + } +#endif + + return drew; +} + +bool OpenGLPipeline::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 + // metrics the frame was swapped at this point + currentFrameInfo->markSwapBuffers(); + + *requireSwap = drew || mEglManager.damageRequiresSwap(); + + if (*requireSwap && (CC_UNLIKELY(!mEglManager.swapBuffers(frame, screenDirty)))) { + return false; + } + + return *requireSwap; +} + +bool OpenGLPipeline::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) { + ATRACE_CALL(); + layer->apply(); + return OpenGLReadbackImpl::copyLayerInto(mRenderThread, + static_cast<GlLayer&>(*layer->backingLayer()), bitmap); +} + +DeferredLayerUpdater* OpenGLPipeline::createTextureLayer() { + mEglManager.initialize(); + GlLayer* layer = new GlLayer(mRenderThread.renderState(), 0, 0); + Caches::getInstance().textureState().activateTexture(0); + layer->generateTexture(); + + return new DeferredLayerUpdater(layer); +} + +void OpenGLPipeline::onStop() { + if (mEglManager.isCurrent(mEglSurface)) { + mEglManager.makeCurrent(EGL_NO_SURFACE); + } +} + +bool OpenGLPipeline::setSurface(Surface* surface, SwapBehavior swapBehavior) { + + if (mEglSurface != EGL_NO_SURFACE) { + mEglManager.destroySurface(mEglSurface); + mEglSurface = EGL_NO_SURFACE; + } + + if (surface) { + mEglSurface = mEglManager.createSurface(surface); + } + + if (mEglSurface != EGL_NO_SURFACE) { + const bool preserveBuffer = (swapBehavior != SwapBehavior::kSwap_discardBuffer); + mBufferPreserved = mEglManager.setPreserveBuffer(mEglSurface, preserveBuffer); + return true; + } + + return false; +} + +bool OpenGLPipeline::isSurfaceReady() { + return CC_UNLIKELY(mEglSurface != EGL_NO_SURFACE); +} + +bool OpenGLPipeline::isContextReady() { + return CC_LIKELY(mEglManager.hasEglContext()); +} + +void OpenGLPipeline::onDestroyHardwareResources() { + Caches& caches = Caches::getInstance(); + // Make sure to release all the textures we were owning as there won't + // be another draw + caches.textureCache.resetMarkInUse(this); + mRenderThread.renderState().flush(Caches::FlushMode::Layers); +} + +void OpenGLPipeline::renderLayers(const FrameBuilder::LightGeometry& lightGeometry, + LayerUpdateQueue* layerUpdateQueue, bool opaque, + const BakedOpRenderer::LightInfo& lightInfo) { + static const std::vector< sp<RenderNode> > emptyNodeList; + auto& caches = Caches::getInstance(); + FrameBuilder frameBuilder(*layerUpdateQueue, lightGeometry, caches); + layerUpdateQueue->clear(); + BakedOpRenderer renderer(caches, mRenderThread.renderState(), + opaque, lightInfo); + LOG_ALWAYS_FATAL_IF(renderer.didDraw(), "shouldn't draw in buildlayer case"); + frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer); +} + +TaskManager* OpenGLPipeline::getTaskManager() { + return &Caches::getInstance().tasks; +} + +static bool layerMatchesWH(OffscreenBuffer* layer, int width, int height) { + return layer->viewportWidth == (uint32_t)width && layer->viewportHeight == (uint32_t)height; +} + +bool OpenGLPipeline::createOrUpdateLayer(RenderNode* node, + const DamageAccumulator& damageAccumulator) { + RenderState& renderState = mRenderThread.renderState(); + OffscreenBufferPool& layerPool = renderState.layerPool(); + bool transformUpdateNeeded = false; + if (node->getLayer() == nullptr) { + node->setLayer(layerPool.get(renderState, node->getWidth(), node->getHeight())); + transformUpdateNeeded = true; + } else if (!layerMatchesWH(node->getLayer(), node->getWidth(), node->getHeight())) { + // TODO: remove now irrelevant, currently enqueued damage (respecting damage ordering) + // Or, ideally, maintain damage between frames on node/layer so ordering is always correct + if (node->properties().fitsOnLayer()) { + node->setLayer(layerPool.resize(node->getLayer(), node->getWidth(), node->getHeight())); + } else { + destroyLayer(node); + } + transformUpdateNeeded = true; + } + + if (transformUpdateNeeded && node->getLayer()) { + // update the transform in window of the layer to reset its origin wrt light source position + Matrix4 windowTransform; + damageAccumulator.computeCurrentTransform(&windowTransform); + node->getLayer()->setWindowTransform(windowTransform); + } + + return transformUpdateNeeded; +} + +bool OpenGLPipeline::pinImages(LsaVector<sk_sp<Bitmap>>& images) { + TextureCache& cache = Caches::getInstance().textureCache; + bool prefetchSucceeded = true; + for (auto& bitmapResource : images) { + prefetchSucceeded &= cache.prefetchAndMarkInUse(this, bitmapResource.get()); + } + return prefetchSucceeded; +} + +void OpenGLPipeline::unpinImages() { + Caches::getInstance().textureCache.resetMarkInUse(this); +} + +void OpenGLPipeline::destroyLayer(RenderNode* node) { + if (OffscreenBuffer* layer = node->getLayer()) { + layer->renderState.layerPool().putOrDelete(layer); + node->setLayer(nullptr); + } +} + +void OpenGLPipeline::prepareToDraw(const RenderThread& thread, Bitmap* bitmap) { + if (Caches::hasInstance() && thread.eglManager().hasEglContext()) { + ATRACE_NAME("Bitmap#prepareToDraw task"); + Caches::getInstance().textureCache.prefetch(bitmap); + } +} + +void OpenGLPipeline::invokeFunctor(const RenderThread& thread, Functor* functor) { + DrawGlInfo::Mode mode = DrawGlInfo::kModeProcessNoContext; + if (thread.eglManager().hasEglContext()) { + mode = DrawGlInfo::kModeProcess; + } + thread.renderState().invokeFunctor(functor, mode, nullptr); +} + +} /* namespace renderthread */ +} /* namespace uirenderer */ +} /* namespace android */ diff --git a/libs/hwui/renderthread/OpenGLPipeline.h b/libs/hwui/renderthread/OpenGLPipeline.h new file mode 100644 index 000000000000..6df8be477e9c --- /dev/null +++ b/libs/hwui/renderthread/OpenGLPipeline.h @@ -0,0 +1,74 @@ +/* + * 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. + */ + +#pragma once + +#include "CanvasContext.h" +#include "BakedOpDispatcher.h" +#include "BakedOpRenderer.h" +#include "FrameBuilder.h" +#include "IRenderPipeline.h" + +namespace android { +namespace uirenderer { +namespace renderthread { + +class OpenGLPipeline : public IRenderPipeline { +public: + OpenGLPipeline(RenderThread& thread); + virtual ~OpenGLPipeline() {} + + 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, + 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; + bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) override; + DeferredLayerUpdater* createTextureLayer() override; + bool setSurface(Surface* window, SwapBehavior swapBehavior) override; + void onStop() override; + bool isSurfaceReady() override; + bool isContextReady() override; + void onDestroyHardwareResources() override; + void renderLayers(const FrameBuilder::LightGeometry& lightGeometry, + LayerUpdateQueue* layerUpdateQueue, bool opaque, + const BakedOpRenderer::LightInfo& lightInfo) override; + TaskManager* getTaskManager() override; + bool createOrUpdateLayer(RenderNode* node, + const DamageAccumulator& damageAccumulator) override; + bool pinImages(std::vector<SkImage*>& mutableImages) override { return false; } + bool pinImages(LsaVector<sk_sp<Bitmap>>& images) override; + void unpinImages() override; + static void destroyLayer(RenderNode* node); + static void prepareToDraw(const RenderThread& thread, Bitmap* bitmap); + static void invokeFunctor(const RenderThread& thread, Functor* functor); + +private: + EglManager& mEglManager; + EGLSurface mEglSurface = EGL_NO_SURFACE; + bool mBufferPreserved = false; + RenderThread& mRenderThread; +}; + +} /* namespace renderthread */ +} /* namespace uirenderer */ +} /* namespace android */ diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp index a734401a2be6..022e871315a9 100644 --- a/libs/hwui/renderthread/RenderProxy.cpp +++ b/libs/hwui/renderthread/RenderProxy.cpp @@ -18,7 +18,6 @@ #include "DeferredLayerUpdater.h" #include "DisplayList.h" -#include "LayerRenderer.h" #include "Readback.h" #include "Rect.h" #include "renderthread/CanvasContext.h" @@ -28,6 +27,8 @@ #include "utils/Macros.h" #include "utils/TimeUtils.h" +#include <ui/GraphicBuffer.h> + namespace android { namespace uirenderer { namespace renderthread { @@ -59,7 +60,7 @@ namespace renderthread { CREATE_BRIDGE4(createContext, RenderThread* thread, bool translucent, RenderNode* rootRenderNode, IContextFactory* contextFactory) { - return new CanvasContext(*args->thread, args->translucent, + return CanvasContext::create(*args->thread, args->translucent, args->rootRenderNode, args->contextFactory); } @@ -184,19 +185,17 @@ void RenderProxy::setStopped(bool stopped) { postAndWait(task); } -CREATE_BRIDGE6(setup, CanvasContext* context, int width, int height, +CREATE_BRIDGE4(setup, CanvasContext* context, float lightRadius, uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha) { - args->context->setup(args->width, args->height, args->lightRadius, + args->context->setup(args->lightRadius, args->ambientShadowAlpha, args->spotShadowAlpha); return nullptr; } -void RenderProxy::setup(int width, int height, float lightRadius, +void RenderProxy::setup(float lightRadius, uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha) { SETUP_TASK(setup); args->context = mContext; - args->width = width; - args->height = height; args->lightRadius = lightRadius; args->ambientShadowAlpha = ambientShadowAlpha; args->spotShadowAlpha = spotShadowAlpha; @@ -271,22 +270,8 @@ void RenderProxy::invokeFunctor(Functor* functor, bool waitForCompletion) { } } -CREATE_BRIDGE2(runWithGlContext, CanvasContext* context, RenderTask* task) { - args->context->runWithGlContext(args->task); - return nullptr; -} - -void RenderProxy::runWithGlContext(RenderTask* gltask) { - SETUP_TASK(runWithGlContext); - args->context = mContext; - args->task = gltask; - postAndWait(task); -} - CREATE_BRIDGE1(createTextureLayer, CanvasContext* context) { - Layer* layer = args->context->createTextureLayer(); - if (!layer) return nullptr; - return new DeferredLayerUpdater(layer); + return args->context->createTextureLayer(); } DeferredLayerUpdater* RenderProxy::createTextureLayer() { @@ -460,6 +445,19 @@ void RenderProxy::resetProfileInfo() { postAndWait(task); } +CREATE_BRIDGE2(frameTimePercentile, RenderThread* thread, int percentile) { + return reinterpret_cast<void*>(static_cast<uintptr_t>( + args->thread->jankTracker().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))); +} + CREATE_BRIDGE2(dumpGraphicsMemory, int fd, RenderThread* thread) { args->thread->jankTracker().dump(args->fd); @@ -471,11 +469,7 @@ CREATE_BRIDGE2(dumpGraphicsMemory, int fd, RenderThread* thread) { } else { fprintf(file, "\nNo caches instance.\n"); } -#if HWUI_NEW_OPS fprintf(file, "\nPipeline=FrameBuilder\n"); -#else - fprintf(file, "\nPipeline=OpenGLRenderer\n"); -#endif fflush(file); return nullptr; } @@ -488,23 +482,6 @@ void RenderProxy::dumpGraphicsMemory(int fd) { staticPostAndWait(task); } -CREATE_BRIDGE4(setTextureAtlas, RenderThread* thread, GraphicBuffer* buffer, int64_t* map, - size_t size) { - CanvasContext::setTextureAtlas(*args->thread, args->buffer, args->map, args->size); - args->buffer->decStrong(nullptr); - return nullptr; -} - -void RenderProxy::setTextureAtlas(const sp<GraphicBuffer>& buffer, int64_t* map, size_t size) { - SETUP_TASK(setTextureAtlas); - args->thread = &mRenderThread; - args->buffer = buffer.get(); - args->buffer->incStrong(nullptr); - args->map = map; - args->size = size; - post(task); -} - CREATE_BRIDGE2(setProcessStatsBuffer, RenderThread* thread, int fd) { args->thread->jankTracker().switchStorageToAshmem(args->fd); close(args->fd); @@ -625,32 +602,31 @@ void RenderProxy::removeFrameMetricsObserver(FrameMetricsObserver* observer) { post(task); } -CREATE_BRIDGE3(copySurfaceInto, RenderThread* thread, - Surface* surface, SkBitmap* bitmap) { - return (void*) Readback::copySurfaceInto(*args->thread, - *args->surface, args->bitmap); +CREATE_BRIDGE4(copySurfaceInto, RenderThread* thread, + Surface* surface, Rect srcRect, SkBitmap* bitmap) { + return (void*)args->thread->readback().copySurfaceInto(*args->surface, + args->srcRect, args->bitmap); } -int RenderProxy::copySurfaceInto(sp<Surface>& surface, SkBitmap* bitmap) { +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) )); } -CREATE_BRIDGE2(prepareToDraw, RenderThread* thread, SkBitmap* bitmap) { - if (Caches::hasInstance() && args->thread->eglManager().hasEglContext()) { - ATRACE_NAME("Bitmap#prepareToDraw task"); - Caches::getInstance().textureCache.prefetch(args->bitmap); - } - delete args->bitmap; +CREATE_BRIDGE2(prepareToDraw, RenderThread* thread, Bitmap* bitmap) { + CanvasContext::prepareToDraw(*args->thread, args->bitmap); + args->bitmap->unref(); args->bitmap = nullptr; return nullptr; } -void RenderProxy::prepareToDraw(const SkBitmap& bitmap) { +void RenderProxy::prepareToDraw(Bitmap& bitmap) { // If we haven't spun up a hardware accelerated window yet, there's no // point in precaching these bitmaps as it can't impact jank. // We also don't know if we even will spin up a hardware-accelerated @@ -659,7 +635,8 @@ void RenderProxy::prepareToDraw(const SkBitmap& bitmap) { RenderThread* renderThread = &RenderThread::getInstance(); SETUP_TASK(prepareToDraw); args->thread = renderThread; - args->bitmap = new SkBitmap(bitmap); + bitmap.ref(); + args->bitmap = &bitmap; nsecs_t lastVsync = renderThread->timeLord().latestVsync(); nsecs_t estimatedNextVsync = lastVsync + renderThread->timeLord().frameIntervalNanos(); nsecs_t timeToNextVsync = estimatedNextVsync - systemTime(CLOCK_MONOTONIC); @@ -675,6 +652,31 @@ void RenderProxy::prepareToDraw(const SkBitmap& bitmap) { } } +CREATE_BRIDGE2(allocateHardwareBitmap, RenderThread* thread, SkBitmap* bitmap) { + sk_sp<Bitmap> hardwareBitmap = Bitmap::allocateHardwareBitmap(*args->thread, *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); +} + +int RenderProxy::copyGraphicBufferInto(GraphicBuffer* buffer, SkBitmap* bitmap) { + SETUP_TASK(copyGraphicBufferInto); + args->thread = &RenderThread::getInstance(); + args->bitmap = bitmap; + args->buffer = buffer; + return static_cast<int>(reinterpret_cast<intptr_t>(staticPostAndWait(task))); +} + void RenderProxy::post(RenderTask* task) { mRenderThread.queue(task); } diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h index bb111bd0de25..44a5a147b6da 100644 --- a/libs/hwui/renderthread/RenderProxy.h +++ b/libs/hwui/renderthread/RenderProxy.h @@ -35,6 +35,8 @@ #include "DrawFrameTask.h" namespace android { +class GraphicBuffer; + namespace uirenderer { class DeferredLayerUpdater; @@ -80,7 +82,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(int width, int height, float lightRadius, + 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); @@ -90,8 +92,6 @@ public: ANDROID_API static void invokeFunctor(Functor* functor, bool waitForCompletion); - ANDROID_API void runWithGlContext(RenderTask* task); - ANDROID_API DeferredLayerUpdater* createTextureLayer(); ANDROID_API void buildLayer(RenderNode* node, TreeObserver* observer); ANDROID_API bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap& bitmap); @@ -111,9 +111,9 @@ public: ANDROID_API void dumpProfileInfo(int fd, int dumpFlags); // Not exported, only used for testing void resetProfileInfo(); + uint32_t frameTimePercentile(int p); ANDROID_API static void dumpGraphicsMemory(int fd); - ANDROID_API void setTextureAtlas(const sp<GraphicBuffer>& buffer, int64_t* map, size_t size); ANDROID_API void setProcessStatsBuffer(int fd); ANDROID_API int getRenderThreadTid(); @@ -128,9 +128,13 @@ public: ANDROID_API void removeFrameMetricsObserver(FrameMetricsObserver* observer); ANDROID_API long getDroppedFrameReportCount(); - ANDROID_API static int copySurfaceInto(sp<Surface>& surface, SkBitmap* bitmap); - ANDROID_API static void prepareToDraw(const 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); + static int copyGraphicBufferInto(GraphicBuffer* buffer, SkBitmap* bitmap); private: RenderThread& mRenderThread; CanvasContext* mContext; diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp index 9c10c4fdbad1..e13d0ce6d688 100644 --- a/libs/hwui/renderthread/RenderThread.cpp +++ b/libs/hwui/renderthread/RenderThread.cpp @@ -17,9 +17,12 @@ #include "RenderThread.h" #include "../renderstate/RenderState.h" +#include "../pipeline/skia/SkiaOpenGLReadback.h" #include "CanvasContext.h" #include "EglManager.h" +#include "OpenGLReadback.h" #include "RenderProxy.h" +#include "VulkanManager.h" #include "utils/FatVector.h" #include <gui/DisplayEventReceiver.h> @@ -158,7 +161,8 @@ RenderThread::RenderThread() : Thread(true) , mFrameCallbackTaskPending(false) , mFrameCallbackTask(nullptr) , mRenderState(nullptr) - , mEglManager(nullptr) { + , mEglManager(nullptr) + , mVkManager(nullptr) { Properties::load(); mFrameCallbackTask = new DispatchFrameCallbacks(this); mLooper = new Looper(false); @@ -192,6 +196,31 @@ void RenderThread::initThreadLocals() { mEglManager = new EglManager(*this); mRenderState = new RenderState(*this); mJankTracker = new JankTracker(mDisplayInfo); + mVkManager = new VulkanManager(*this); +} + +Readback& RenderThread::readback() { + + if (!mReadback) { + auto renderType = Properties::getRenderPipelineType(); + switch (renderType) { + case RenderPipelineType::OpenGL: + 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; + default: + LOG_ALWAYS_FATAL("canvas context type %d not supported", (int32_t) renderType); + break; + } + } + + return *mReadback; } int RenderThread::displayEventReceiverCallback(int fd, int events, void* data) { diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h index 076e3d43a2c9..d121bcf5b084 100644 --- a/libs/hwui/renderthread/RenderThread.h +++ b/libs/hwui/renderthread/RenderThread.h @@ -22,6 +22,7 @@ #include "../JankTracker.h" #include "TimeLord.h" +#include <GrContext.h> #include <cutils/compiler.h> #include <ui/DisplayInfo.h> #include <utils/Looper.h> @@ -36,6 +37,7 @@ class DisplayEventReceiver; namespace uirenderer { +class Readback; class RenderState; class TestUtils; @@ -45,6 +47,7 @@ class CanvasContext; class DispatchFrameCallbacks; class EglManager; class RenderProxy; +class VulkanManager; class TaskQueue { public: @@ -88,12 +91,18 @@ public: void pushBackFrameCallback(IFrameCallback* callback); TimeLord& timeLord() { return mTimeLord; } - RenderState& renderState() { return *mRenderState; } - EglManager& eglManager() { return *mEglManager; } + RenderState& renderState() const { return *mRenderState; } + EglManager& eglManager() const { return *mEglManager; } JankTracker& jankTracker() { return *mJankTracker; } + Readback& readback(); const DisplayInfo& mainDisplayInfo() { return mDisplayInfo; } + GrContext* getGrContext() const { return mGrContext.get(); } + void setGrContext(GrContext* cxt) { mGrContext.reset(cxt); } + + VulkanManager& vulkanManager() { return *mVkManager; } + protected: virtual bool threadLoop() override; @@ -144,6 +153,10 @@ private: EglManager* mEglManager; JankTracker* mJankTracker = nullptr; + Readback* mReadback = nullptr; + + sk_sp<GrContext> mGrContext; + VulkanManager* mVkManager; }; } /* namespace renderthread */ diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp new file mode 100644 index 000000000000..454ce4d9e8ff --- /dev/null +++ b/libs/hwui/renderthread/VulkanManager.cpp @@ -0,0 +1,700 @@ +/* + * 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 "VulkanManager.h" + +#include "DeviceInfo.h" +#include "Properties.h" +#include "RenderThread.h" +#include "renderstate/RenderState.h" +#include "utils/FatVector.h" + +#include <GrContext.h> +#include <GrTypes.h> +#include <vk/GrVkTypes.h> + +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) + +VulkanManager::VulkanManager(RenderThread& thread) : mRenderThread(thread) { +} + +void VulkanManager::destroy() { + if (!hasVkContext()) return; + + mRenderThread.renderState().onVkContextDestroyed(); + mRenderThread.setGrContext(nullptr); + + if (VK_NULL_HANDLE != mCommandPool) { + mDestroyCommandPool(mBackendContext->fDevice, mCommandPool, nullptr); + mCommandPool = VK_NULL_HANDLE; + } + mBackendContext.reset(); +} + +void VulkanManager::initialize() { + if (hasVkContext()) { return; } + + auto canPresent = [](VkInstance, VkPhysicalDevice, uint32_t) { return true; }; + + mBackendContext.reset(GrVkBackendContext::Create(&mPresentQueueIndex, canPresent)); + + // Get all the addresses of needed vulkan functions + VkInstance instance = mBackendContext->fInstance; + VkDevice device = mBackendContext->fDevice; + GET_PROC(CreateAndroidSurfaceKHR); + GET_PROC(DestroySurfaceKHR); + GET_PROC(GetPhysicalDeviceSurfaceSupportKHR); + GET_PROC(GetPhysicalDeviceSurfaceCapabilitiesKHR); + GET_PROC(GetPhysicalDeviceSurfaceFormatsKHR); + GET_PROC(GetPhysicalDeviceSurfacePresentModesKHR); + GET_DEV_PROC(CreateSwapchainKHR); + GET_DEV_PROC(DestroySwapchainKHR); + GET_DEV_PROC(GetSwapchainImagesKHR); + GET_DEV_PROC(AcquireNextImageKHR); + GET_DEV_PROC(QueuePresentKHR); + GET_DEV_PROC(CreateCommandPool); + GET_DEV_PROC(DestroyCommandPool); + GET_DEV_PROC(AllocateCommandBuffers); + GET_DEV_PROC(FreeCommandBuffers); + GET_DEV_PROC(ResetCommandBuffer); + GET_DEV_PROC(BeginCommandBuffer); + GET_DEV_PROC(EndCommandBuffer); + GET_DEV_PROC(CmdPipelineBarrier); + GET_DEV_PROC(GetDeviceQueue); + GET_DEV_PROC(QueueSubmit); + GET_DEV_PROC(QueueWaitIdle); + GET_DEV_PROC(DeviceWaitIdle); + GET_DEV_PROC(CreateSemaphore); + GET_DEV_PROC(DestroySemaphore); + GET_DEV_PROC(CreateFence); + GET_DEV_PROC(DestroyFence); + GET_DEV_PROC(WaitForFences); + GET_DEV_PROC(ResetFences); + + // create the command pool for the command buffers + if (VK_NULL_HANDLE == mCommandPool) { + VkCommandPoolCreateInfo commandPoolInfo; + memset(&commandPoolInfo, 0, sizeof(VkCommandPoolCreateInfo)); + commandPoolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + // 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); + SkASSERT(VK_SUCCESS == res); + } + + mGetDeviceQueue(mBackendContext->fDevice, mPresentQueueIndex, 0, &mPresentQueue); + + mRenderThread.setGrContext(GrContext::Create(kVulkan_GrBackend, + (GrBackendContext) mBackendContext.get())); + DeviceInfo::initialize(mRenderThread.getGrContext()->caps()->maxRenderTargetSize()); + + if (Properties::enablePartialUpdates && Properties::useBufferAge) { + mSwapBehavior = SwapBehavior::BufferAge; + } + + mRenderThread.renderState().onVkContextCreated(); +} + +// Returns the next BackbufferInfo to use for the next draw. The function will make sure all +// previous uses have finished before returning. +VulkanSurface::BackbufferInfo* VulkanManager::getAvailableBackbuffer(VulkanSurface* surface) { + SkASSERT(surface->mBackbuffers); + + ++surface->mCurrentBackbufferIndex; + if (surface->mCurrentBackbufferIndex > surface->mImageCount) { + surface->mCurrentBackbufferIndex = 0; + } + + 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); + if (res != VK_SUCCESS) { + return nullptr; + } + + return backbuffer; +} + + +SkSurface* VulkanManager::getBackbufferSurface(VulkanSurface* surface) { + VulkanSurface::BackbufferInfo* backbuffer = getAvailableBackbuffer(surface); + SkASSERT(backbuffer); + + VkResult res; + + res = mResetFences(mBackendContext->fDevice, 2, backbuffer->mUsageFences); + SkASSERT(VK_SUCCESS == res); + + // 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); + + if (VK_ERROR_SURFACE_LOST_KHR == res) { + // need to figure out how to create a new vkSurface without the platformData* + // maybe use attach somehow? but need a Window + return nullptr; + } + if (VK_ERROR_OUT_OF_DATE_KHR == res) { + // tear swapchain down and try again + if (!createSwapchain(surface)) { + return nullptr; + } + backbuffer = getAvailableBackbuffer(surface); + res = mResetFences(mBackendContext->fDevice, 2, backbuffer->mUsageFences); + SkASSERT(VK_SUCCESS == res); + + // acquire the image + res = mAcquireNextImageKHR(mBackendContext->fDevice, surface->mSwapchain, UINT64_MAX, + backbuffer->mAcquireSemaphore, VK_NULL_HANDLE, &backbuffer->mImageIndex); + + if (VK_SUCCESS != res) { + return nullptr; + } + } + + // 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 dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_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 + }; + mResetCommandBuffer(backbuffer->mTransitionCmdBuffers[0], 0); + + VkCommandBufferBeginInfo info; + memset(&info, 0, sizeof(VkCommandBufferBeginInfo)); + info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + info.flags = 0; + mBeginCommandBuffer(backbuffer->mTransitionCmdBuffers[0], &info); + + mCmdPipelineBarrier(backbuffer->mTransitionCmdBuffers[0], srcStageMask, dstStageMask, 0, + 0, nullptr, 0, nullptr, 1, &imageMemoryBarrier); + + mEndCommandBuffer(backbuffer->mTransitionCmdBuffers[0]); + + VkPipelineStageFlags waitDstStageFlags = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + // insert the layout transfer into the queue and wait on the acquire + VkSubmitInfo submitInfo; + memset(&submitInfo, 0, sizeof(VkSubmitInfo)); + submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submitInfo.waitSemaphoreCount = 1; + // Wait to make sure aquire semaphore set above has signaled. + submitInfo.pWaitSemaphores = &backbuffer->mAcquireSemaphore; + submitInfo.pWaitDstStageMask = &waitDstStageFlags; + submitInfo.commandBufferCount = 1; + submitInfo.pCommandBuffers = &backbuffer->mTransitionCmdBuffers[0]; + submitInfo.signalSemaphoreCount = 0; + + // Attach first fence to submission here so we can track when the command buffer finishes. + mQueueSubmit(mBackendContext->fQueue, 1, &submitInfo, backbuffer->mUsageFences[0]); + + // We need to notify Skia that we changed the layout of the wrapped VkImage + GrVkImageInfo* imageInfo; + sk_sp<SkSurface> skSurface = surface->mImageInfos[backbuffer->mImageIndex].mSurface; + skSurface->getRenderTargetHandle((GrBackendObject*)&imageInfo, + SkSurface::kFlushRead_BackendHandleAccess); + imageInfo->updateImageLayout(VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + + surface->mBackbuffer = std::move(skSurface); + return surface->mBackbuffer.get(); +} + +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); + surface->mBackbuffers[i].mImageIndex = -1; + mDestroySemaphore(mBackendContext->fDevice, surface->mBackbuffers[i].mAcquireSemaphore, + nullptr); + mDestroySemaphore(mBackendContext->fDevice, surface->mBackbuffers[i].mRenderSemaphore, + nullptr); + mFreeCommandBuffers(mBackendContext->fDevice, mCommandPool, 2, + surface->mBackbuffers[i].mTransitionCmdBuffers); + mDestroyFence(mBackendContext->fDevice, surface->mBackbuffers[i].mUsageFences[0], 0); + mDestroyFence(mBackendContext->fDevice, surface->mBackbuffers[i].mUsageFences[1], 0); + } + } + + delete[] surface->mBackbuffers; + surface->mBackbuffers = nullptr; + delete[] surface->mImageInfos; + surface->mImageInfos = nullptr; + delete[] surface->mImages; + surface->mImages = nullptr; +} + +void VulkanManager::destroySurface(VulkanSurface* surface) { + // Make sure all submit commands have finished before starting to destroy objects. + if (VK_NULL_HANDLE != mPresentQueue) { + mQueueWaitIdle(mPresentQueue); + } + mDeviceWaitIdle(mBackendContext->fDevice); + + destroyBuffers(surface); + + if (VK_NULL_HANDLE != surface->mSwapchain) { + mDestroySwapchainKHR(mBackendContext->fDevice, surface->mSwapchain, nullptr); + surface->mSwapchain = VK_NULL_HANDLE; + } + + if (VK_NULL_HANDLE != surface->mVkSurface) { + mDestroySurfaceKHR(mBackendContext->fInstance, surface->mVkSurface, nullptr); + surface->mVkSurface = VK_NULL_HANDLE; + } + delete surface; +} + +void VulkanManager::createBuffers(VulkanSurface* surface, VkFormat format, VkExtent2D extent) { + mGetSwapchainImagesKHR(mBackendContext->fDevice, surface->mSwapchain, &surface->mImageCount, + nullptr); + SkASSERT(surface->mImageCount); + surface->mImages = new VkImage[surface->mImageCount]; + mGetSwapchainImagesKHR(mBackendContext->fDevice, surface->mSwapchain, + &surface->mImageCount, surface->mImages); + + SkSurfaceProps props(0, kUnknown_SkPixelGeometry); + + bool wantSRGB = VK_FORMAT_R8G8B8A8_SRGB == format; + GrPixelConfig config = wantSRGB ? kSRGBA_8888_GrPixelConfig : kRGBA_8888_GrPixelConfig; + + // set up initial image layouts and create surfaces + surface->mImageInfos = new VulkanSurface::ImageInfo[surface->mImageCount]; + for (uint32_t i = 0; i < surface->mImageCount; ++i) { + GrBackendRenderTargetDesc desc; + GrVkImageInfo info; + info.fImage = surface->mImages[i]; + info.fAlloc = { VK_NULL_HANDLE, 0, 0, 0 }; + info.fImageLayout = VK_IMAGE_LAYOUT_UNDEFINED; + info.fImageTiling = VK_IMAGE_TILING_OPTIMAL; + info.fFormat = format; + info.fLevelCount = 1; + + desc.fWidth = extent.width; + desc.fHeight = extent.height; + desc.fConfig = config; + desc.fOrigin = kTopLeft_GrSurfaceOrigin; + desc.fSampleCnt = 0; + desc.fStencilBits = 0; + desc.fRenderTargetHandle = (GrBackendObject) &info; + + VulkanSurface::ImageInfo& imageInfo = surface->mImageInfos[i]; + imageInfo.mSurface = SkSurface::MakeFromBackendRenderTarget(mRenderThread.getGrContext(), + desc, &props); + } + + SkASSERT(mCommandPool != VK_NULL_HANDLE); + + // set up the backbuffers + VkSemaphoreCreateInfo semaphoreInfo; + memset(&semaphoreInfo, 0, sizeof(VkSemaphoreCreateInfo)); + semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; + semaphoreInfo.pNext = nullptr; + semaphoreInfo.flags = 0; + VkCommandBufferAllocateInfo commandBuffersInfo; + memset(&commandBuffersInfo, 0, sizeof(VkCommandBufferAllocateInfo)); + commandBuffersInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + commandBuffersInfo.pNext = nullptr; + commandBuffersInfo.commandPool = mCommandPool; + commandBuffersInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + commandBuffersInfo.commandBufferCount = 2; + VkFenceCreateInfo fenceInfo; + memset(&fenceInfo, 0, sizeof(VkFenceCreateInfo)); + fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; + fenceInfo.pNext = nullptr; + fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT; + + // we create one additional backbuffer structure here, because we want to + // give the command buffers they contain a chance to finish before we cycle back + surface->mBackbuffers = new VulkanSurface::BackbufferInfo[surface->mImageCount + 1]; + 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]); + SkASSERT(VK_SUCCESS == res); + } + surface->mCurrentBackbufferIndex = surface->mImageCount; +} + +bool VulkanManager::createSwapchain(VulkanSurface* surface) { + // check for capabilities + VkSurfaceCapabilitiesKHR caps; + VkResult res = mGetPhysicalDeviceSurfaceCapabilitiesKHR(mBackendContext->fPhysicalDevice, + surface->mVkSurface, &caps); + if (VK_SUCCESS != res) { + return false; + } + + uint32_t surfaceFormatCount; + res = mGetPhysicalDeviceSurfaceFormatsKHR(mBackendContext->fPhysicalDevice, surface->mVkSurface, + &surfaceFormatCount, nullptr); + if (VK_SUCCESS != res) { + return false; + } + + FatVector<VkSurfaceFormatKHR, 4> surfaceFormats(surfaceFormatCount); + res = mGetPhysicalDeviceSurfaceFormatsKHR(mBackendContext->fPhysicalDevice, surface->mVkSurface, + &surfaceFormatCount, surfaceFormats.data()); + if (VK_SUCCESS != res) { + return false; + } + + uint32_t presentModeCount; + res = mGetPhysicalDeviceSurfacePresentModesKHR(mBackendContext->fPhysicalDevice, + 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()); + if (VK_SUCCESS != res) { + return false; + } + + VkExtent2D extent = caps.currentExtent; + // clamp width; to handle currentExtent of -1 and protect us from broken hints + if (extent.width < caps.minImageExtent.width) { + extent.width = caps.minImageExtent.width; + } + SkASSERT(extent.width <= caps.maxImageExtent.width); + // clamp height + if (extent.height < caps.minImageExtent.height) { + extent.height = caps.minImageExtent.height; + } + SkASSERT(extent.height <= caps.maxImageExtent.height); + + uint32_t imageCount = caps.minImageCount + 2; + if (caps.maxImageCount > 0 && imageCount > caps.maxImageCount) { + // Application must settle for fewer images than desired: + imageCount = caps.maxImageCount; + } + + // Currently Skia requires the images to be color attchments and support all transfer + // operations. + VkImageUsageFlags usageFlags = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | + VK_IMAGE_USAGE_TRANSFER_SRC_BIT | + 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)); + VkCompositeAlphaFlagBitsKHR composite_alpha = + (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; + VkColorSpaceKHR colorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR; + + bool wantSRGB = false; +#ifdef ANDROID_ENABLE_LINEAR_BLENDING + wantSRGB = true; +#endif + for (uint32_t i = 0; i < surfaceFormatCount; ++i) { + // We are assuming we can get either R8G8B8A8_UNORM or R8G8B8A8_SRGB + VkFormat desiredFormat = wantSRGB ? VK_FORMAT_R8G8B8A8_SRGB : VK_FORMAT_R8G8B8A8_UNORM; + if (desiredFormat == surfaceFormats[i].format) { + surfaceFormat = surfaceFormats[i].format; + colorSpace = surfaceFormats[i].colorSpace; + } + } + + if (VK_FORMAT_UNDEFINED == surfaceFormat) { + return false; + } + + // If mailbox mode is available, use it, as it is the lowest-latency non- + // tearing mode. If not, fall back to FIFO which is always available. + VkPresentModeKHR mode = VK_PRESENT_MODE_FIFO_KHR; + for (uint32_t i = 0; i < presentModeCount; ++i) { + // use mailbox + if (VK_PRESENT_MODE_MAILBOX_KHR == presentModes[i]) { + mode = presentModes[i]; + break; + } + } + + VkSwapchainCreateInfoKHR swapchainCreateInfo; + memset(&swapchainCreateInfo, 0, sizeof(VkSwapchainCreateInfoKHR)); + swapchainCreateInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; + swapchainCreateInfo.surface = surface->mVkSurface; + swapchainCreateInfo.minImageCount = imageCount; + swapchainCreateInfo.imageFormat = surfaceFormat; + swapchainCreateInfo.imageColorSpace = colorSpace; + swapchainCreateInfo.imageExtent = extent; + swapchainCreateInfo.imageArrayLayers = 1; + swapchainCreateInfo.imageUsage = usageFlags; + + uint32_t queueFamilies[] = { mBackendContext->fGraphicsQueueIndex, mPresentQueueIndex }; + if (mBackendContext->fGraphicsQueueIndex != mPresentQueueIndex) { + swapchainCreateInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT; + swapchainCreateInfo.queueFamilyIndexCount = 2; + swapchainCreateInfo.pQueueFamilyIndices = queueFamilies; + } else { + swapchainCreateInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; + swapchainCreateInfo.queueFamilyIndexCount = 0; + swapchainCreateInfo.pQueueFamilyIndices = nullptr; + } + + swapchainCreateInfo.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; + swapchainCreateInfo.compositeAlpha = composite_alpha; + swapchainCreateInfo.presentMode = mode; + swapchainCreateInfo.clipped = true; + swapchainCreateInfo.oldSwapchain = surface->mSwapchain; + + res = mCreateSwapchainKHR(mBackendContext->fDevice, &swapchainCreateInfo, nullptr, + &surface->mSwapchain); + if (VK_SUCCESS != res) { + return false; + } + + // destroy the old swapchain + if (swapchainCreateInfo.oldSwapchain != VK_NULL_HANDLE) { + mDeviceWaitIdle(mBackendContext->fDevice); + + destroyBuffers(surface); + + mDestroySwapchainKHR(mBackendContext->fDevice, swapchainCreateInfo.oldSwapchain, nullptr); + } + + createBuffers(surface, surfaceFormat, extent); + + return true; +} + + +VulkanSurface* VulkanManager::createSurface(ANativeWindow* window) { + initialize(); + + if (!window) { + return nullptr; + } + + VulkanSurface* surface = new VulkanSurface(); + + VkAndroidSurfaceCreateInfoKHR surfaceCreateInfo; + memset(&surfaceCreateInfo, 0, sizeof(VkAndroidSurfaceCreateInfoKHR)); + surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR; + surfaceCreateInfo.pNext = nullptr; + surfaceCreateInfo.flags = 0; + surfaceCreateInfo.window = window; + + 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); +); + + if (!createSwapchain(surface)) { + destroySurface(surface); + return nullptr; + } + + return surface; +} + +// Helper to know which src stage flags we need to set when transitioning to the present layout +static VkPipelineStageFlags layoutToPipelineStageFlags(const VkImageLayout layout) { + if (VK_IMAGE_LAYOUT_GENERAL == layout) { + return VK_PIPELINE_STAGE_ALL_COMMANDS_BIT; + } else if (VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL == layout || + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL == layout) { + return VK_PIPELINE_STAGE_TRANSFER_BIT; + } else if (VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL == layout || + VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL == layout || + VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL == layout || + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL == layout) { + return VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT; + } else if (VK_IMAGE_LAYOUT_PREINITIALIZED == layout) { + return VK_PIPELINE_STAGE_HOST_BIT; + } + + SkASSERT(VK_IMAGE_LAYOUT_UNDEFINED == layout); + return VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; +} + +// Helper to know which src access mask we need to set when transitioning to the present layout +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; + } else if (VK_IMAGE_LAYOUT_PREINITIALIZED == layout) { + flags = VK_ACCESS_HOST_WRITE_BIT; + } else if (VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL == layout) { + flags = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + } else if (VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL == layout) { + flags = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + } else if (VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL == layout) { + flags = VK_ACCESS_TRANSFER_WRITE_BIT; + } else if (VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL == layout) { + flags = VK_ACCESS_TRANSFER_READ_BIT; + } else if (VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL == layout) { + flags = VK_ACCESS_SHADER_READ_BIT; + } + return flags; +} + +void VulkanManager::swapBuffers(VulkanSurface* surface) { + VulkanSurface::BackbufferInfo* backbuffer = surface->mBackbuffers + + surface->mCurrentBackbufferIndex; + GrVkImageInfo* imageInfo; + SkSurface* skSurface = surface->mImageInfos[backbuffer->mImageIndex].mSurface.get(); + skSurface->getRenderTargetHandle((GrBackendObject*)&imageInfo, + SkSurface::kFlushRead_BackendHandleAccess); + // Check to make sure we never change the actually wrapped image + SkASSERT(imageInfo->fImage == surface->mImages[backbuffer->mImageIndex]); + + // We need to transition the image to VK_IMAGE_LAYOUT_PRESENT_SRC_KHR and make sure that all + // previous work is complete for before presenting. So we first add the necessary barrier here. + VkImageLayout layout = imageInfo->fImageLayout; + VkPipelineStageFlags srcStageMask = layoutToPipelineStageFlags(layout); + VkPipelineStageFlags dstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; + VkAccessFlags srcAccessMask = layoutToSrcAccessMask(layout); + 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 + }; + + mResetCommandBuffer(backbuffer->mTransitionCmdBuffers[1], 0); + VkCommandBufferBeginInfo info; + memset(&info, 0, sizeof(VkCommandBufferBeginInfo)); + 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); + mEndCommandBuffer(backbuffer->mTransitionCmdBuffers[1]); + + surface->mImageInfos[backbuffer->mImageIndex].mImageLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + + // insert the layout transfer into the queue and wait on the acquire + VkSubmitInfo submitInfo; + memset(&submitInfo, 0, sizeof(VkSubmitInfo)); + submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submitInfo.waitSemaphoreCount = 0; + submitInfo.pWaitDstStageMask = 0; + submitInfo.commandBufferCount = 1; + submitInfo.pCommandBuffers = &backbuffer->mTransitionCmdBuffers[1]; + submitInfo.signalSemaphoreCount = 1; + // When this command buffer finishes we will signal this semaphore so that we know it is now + // safe to present the image to the screen. + submitInfo.pSignalSemaphores = &backbuffer->mRenderSemaphore; + + // Attach second fence to submission here so we can track when the command buffer finishes. + mQueueSubmit(mBackendContext->fQueue, 1, &submitInfo, backbuffer->mUsageFences[1]); + + // 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 + }; + + mQueuePresentKHR(mPresentQueue, &presentInfo); + + surface->mBackbuffer.reset(); + surface->mImageInfos[backbuffer->mImageIndex].mLastUsed = surface->mCurrentTime; + surface->mImageInfos[backbuffer->mImageIndex].mInvalid = false; + surface->mCurrentTime++; +} + +int VulkanManager::getAge(VulkanSurface* surface) { + 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; + return surface->mCurrentTime - lastUsed; +} + +} /* namespace renderthread */ +} /* namespace uirenderer */ +} /* namespace android */ diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h new file mode 100644 index 000000000000..d225b3fc4ec0 --- /dev/null +++ b/libs/hwui/renderthread/VulkanManager.h @@ -0,0 +1,186 @@ +/* + * 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. + */ + +#ifndef VULKANMANAGER_H +#define VULKANMANAGER_H + +#include <SkSurface.h> +#include <vk/GrVkBackendContext.h> + +#include <vulkan/vulkan.h> + +namespace android { +namespace uirenderer { +namespace renderthread { + +class RenderThread; + +class VulkanSurface { +public: + VulkanSurface() {} + + sk_sp<SkSurface> getBackBufferSurface() { return mBackbuffer; } + +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 + // 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]; + }; + + struct ImageInfo { + VkImageLayout mImageLayout = VK_IMAGE_LAYOUT_UNDEFINED; + sk_sp<SkSurface> mSurface; + uint16_t mLastUsed = 0; + bool mInvalid = true; + }; + + sk_sp<SkSurface> mBackbuffer; + + VkSurfaceKHR mVkSurface = VK_NULL_HANDLE; + VkSwapchainKHR mSwapchain = VK_NULL_HANDLE; + + BackbufferInfo* mBackbuffers; + uint32_t mCurrentBackbufferIndex; + + uint32_t mImageCount; + VkImage* mImages; + ImageInfo* mImageInfos; + uint16_t mCurrentTime = 0; +}; + +// This class contains the shared global Vulkan objects, such as VkInstance, VkDevice and VkQueue, +// which are re-used by CanvasContext. This class is created once and should be used by all vulkan +// windowing contexts. The VulkanManager must be initialized before use. +class VulkanManager { +public: + // Sets up the vulkan context that is shared amonst all clients of the VulkanManager. This must + // be call once before use of the VulkanManager. Multiple calls after the first will simiply + // return. + void initialize(); + + // Quick check to see if the VulkanManager has been initialized. + bool hasVkContext() { return mBackendContext.get() != nullptr; } + + // Given a window this creates a new VkSurfaceKHR and VkSwapchain and stores them inside a new + // VulkanSurface object which is returned. + VulkanSurface* createSurface(ANativeWindow* window); + + // Destroy the VulkanSurface and all associated vulkan objects. + void destroySurface(VulkanSurface* surface); + + // Cleans up all the global state in the VulkanManger. + void destroy(); + + // No work is needed to make a VulkanSurface current, and all functions require that a + // VulkanSurface is passed into them so we just return true here. + bool isCurrent(VulkanSurface* surface) { return true; } + + int getAge(VulkanSurface* surface); + + // Returns an SkSurface which wraps the next image returned from vkAcquireNextImageKHR. It also + // will transition the VkImage from a present layout to color attachment so that it can be used + // by the client for drawing. + SkSurface* getBackbufferSurface(VulkanSurface* surface); + + // Presents the current VkImage. + void swapBuffers(VulkanSurface* surface); + +private: + friend class RenderThread; + + explicit VulkanManager(RenderThread& thread); + ~VulkanManager() { destroy(); } + + void destroyBuffers(VulkanSurface* surface); + + bool createSwapchain(VulkanSurface* surface); + void createBuffers(VulkanSurface* surface, VkFormat format, VkExtent2D extent); + + VulkanSurface::BackbufferInfo* getAvailableBackbuffer(VulkanSurface* surface); + + // simple wrapper class that exists only to initialize a pointer to NULL + template <typename FNPTR_TYPE> class VkPtr { + public: + VkPtr() : fPtr(NULL) {} + VkPtr operator=(FNPTR_TYPE ptr) { fPtr = ptr; return *this; } + operator FNPTR_TYPE() const { return fPtr; } + private: + FNPTR_TYPE fPtr; + }; + + // WSI interface functions + VkPtr<PFN_vkCreateAndroidSurfaceKHR> mCreateAndroidSurfaceKHR; + VkPtr<PFN_vkDestroySurfaceKHR> mDestroySurfaceKHR; + VkPtr<PFN_vkGetPhysicalDeviceSurfaceSupportKHR> mGetPhysicalDeviceSurfaceSupportKHR; + VkPtr<PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR> mGetPhysicalDeviceSurfaceCapabilitiesKHR; + VkPtr<PFN_vkGetPhysicalDeviceSurfaceFormatsKHR> mGetPhysicalDeviceSurfaceFormatsKHR; + VkPtr<PFN_vkGetPhysicalDeviceSurfacePresentModesKHR> mGetPhysicalDeviceSurfacePresentModesKHR; + + VkPtr<PFN_vkCreateSwapchainKHR> mCreateSwapchainKHR; + VkPtr<PFN_vkDestroySwapchainKHR> mDestroySwapchainKHR; + VkPtr<PFN_vkGetSwapchainImagesKHR> mGetSwapchainImagesKHR; + VkPtr<PFN_vkAcquireNextImageKHR> mAcquireNextImageKHR; + VkPtr<PFN_vkQueuePresentKHR> mQueuePresentKHR; + VkPtr<PFN_vkCreateSharedSwapchainsKHR> mCreateSharedSwapchainsKHR; + + // Additional vulkan functions + VkPtr<PFN_vkCreateCommandPool> mCreateCommandPool; + VkPtr<PFN_vkDestroyCommandPool> mDestroyCommandPool; + VkPtr<PFN_vkAllocateCommandBuffers> mAllocateCommandBuffers; + VkPtr<PFN_vkFreeCommandBuffers> mFreeCommandBuffers; + VkPtr<PFN_vkResetCommandBuffer> mResetCommandBuffer; + VkPtr<PFN_vkBeginCommandBuffer> mBeginCommandBuffer; + VkPtr<PFN_vkEndCommandBuffer> mEndCommandBuffer; + VkPtr<PFN_vkCmdPipelineBarrier> mCmdPipelineBarrier; + + VkPtr<PFN_vkGetDeviceQueue> mGetDeviceQueue; + VkPtr<PFN_vkQueueSubmit> mQueueSubmit; + VkPtr<PFN_vkQueueWaitIdle> mQueueWaitIdle; + VkPtr<PFN_vkDeviceWaitIdle> mDeviceWaitIdle; + + VkPtr<PFN_vkCreateSemaphore> mCreateSemaphore; + VkPtr<PFN_vkDestroySemaphore> mDestroySemaphore; + VkPtr<PFN_vkCreateFence> mCreateFence; + VkPtr<PFN_vkDestroyFence> mDestroyFence; + VkPtr<PFN_vkWaitForFences> mWaitForFences; + VkPtr<PFN_vkResetFences> mResetFences; + + RenderThread& mRenderThread; + + sk_sp<const GrVkBackendContext> mBackendContext; + uint32_t mPresentQueueIndex; + VkQueue mPresentQueue = VK_NULL_HANDLE; + VkCommandPool mCommandPool = VK_NULL_HANDLE; + + enum class SwapBehavior { + Discard, + BufferAge, + }; + SwapBehavior mSwapBehavior = SwapBehavior::Discard; +}; + +} /* namespace renderthread */ +} /* namespace uirenderer */ +} /* namespace android */ + +#endif /* VULKANMANAGER_H */ + diff --git a/libs/hwui/tests/common/BitmapAllocationTestUtils.h b/libs/hwui/tests/common/BitmapAllocationTestUtils.h new file mode 100644 index 000000000000..4892179f0d48 --- /dev/null +++ b/libs/hwui/tests/common/BitmapAllocationTestUtils.h @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2015 The Android Open 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 "TestScene.h" + +#include <SkBitmap.h> +#include <string> + +namespace android { +namespace uirenderer { +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> 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); + sk_sp<Bitmap> heapBitmap(Bitmap::allocateHeapBitmap(&skBitmap, nullptr)); + setup(skBitmap); + return Bitmap::allocateHardwareBitmap(skBitmap); + } + + 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&) { + return new T(allocator); + } + + template <class BaseScene> + 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> + }); + return true; + } +}; + +} // namespace test +} // namespace uirenderer +} // namespace android
\ No newline at end of file diff --git a/libs/hwui/tests/common/LeakChecker.cpp b/libs/hwui/tests/common/LeakChecker.cpp new file mode 100644 index 000000000000..d935382cc9a4 --- /dev/null +++ b/libs/hwui/tests/common/LeakChecker.cpp @@ -0,0 +1,94 @@ +/* + * 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 "LeakChecker.h" + +#include "Caches.h" +#include "TestUtils.h" + +#include <cstdio> +#include <iostream> +#include <memunreachable/memunreachable.h> +#include <unistd.h> +#include <unordered_set> + +using namespace std; + +namespace android { +namespace uirenderer { +namespace test { + +static void logUnreachable(initializer_list<UnreachableMemoryInfo> infolist) { + // merge them all + UnreachableMemoryInfo merged; + unordered_set<uintptr_t> addrs; + merged.allocation_bytes = 0; + merged.leak_bytes = 0; + merged.num_allocations = 0; + merged.num_leaks = 0; + for (auto& info : infolist) { + // We'll be a little hazzy about these ones and just hope the biggest + // is the most accurate + 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); + } + } + } + + // Now log the result + if (merged.num_leaks) { + cout << endl << "Leaked memory!" << endl; + if (!merged.leaks[0].backtrace.num_frames) { + cout << "Re-run with 'setprop libc.debug.malloc.program hwui_unit_test'" + << endl << "and 'setprop libc.debug.malloc.options backtrace=8'" + << " to get backtraces" << endl; + } + cout << merged.ToString(false); + } +} + +void LeakChecker::checkForLeaks() { + // TODO: Until we can shutdown the RT thread we need to do this in + // two passes as GetUnreachableMemory has limited insight into + // thread-local caches so some leaks will not be properly tagged as leaks + UnreachableMemoryInfo rtMemInfo; + TestUtils::runOnRenderThread([&rtMemInfo](renderthread::RenderThread& thread) { + if (Caches::hasInstance()) { + Caches::getInstance().tasks.stop(); + } + // Check for leaks + if (!GetUnreachableMemory(rtMemInfo)) { + cerr << "Failed to get unreachable memory!" << endl; + return; + } + }); + UnreachableMemoryInfo uiMemInfo; + if (!GetUnreachableMemory(uiMemInfo)) { + cerr << "Failed to get unreachable memory!" << endl; + return; + } + logUnreachable({rtMemInfo, uiMemInfo}); +} + +} /* namespace test */ +} /* namespace uirenderer */ +} /* namespace android */ diff --git a/libs/hwui/tests/common/LeakChecker.h b/libs/hwui/tests/common/LeakChecker.h new file mode 100644 index 000000000000..cdf47d6bda80 --- /dev/null +++ b/libs/hwui/tests/common/LeakChecker.h @@ -0,0 +1,29 @@ +/* + * 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. + */ +#pragma once + +namespace android { +namespace uirenderer { +namespace test { + +class LeakChecker { +public: + static void checkForLeaks(); +}; // class TestUtils + +} /* namespace test */ +} /* namespace uirenderer */ +} /* namespace android */
\ No newline at end of file diff --git a/libs/hwui/tests/common/TestContext.cpp b/libs/hwui/tests/common/TestContext.cpp index 99569755205f..5e937f3239ff 100644 --- a/libs/hwui/tests/common/TestContext.cpp +++ b/libs/hwui/tests/common/TestContext.cpp @@ -61,20 +61,53 @@ TestContext::TestContext() { TestContext::~TestContext() {} sp<Surface> TestContext::surface() { - if (!mSurfaceControl.get()) { - mSurfaceControl = mSurfaceComposerClient->createSurface(String8("HwuiTest"), - gDisplay.w, gDisplay.h, PIXEL_FORMAT_RGBX_8888); - - SurfaceComposerClient::openGlobalTransaction(); - mSurfaceControl->setLayer(0x7FFFFFF); - mSurfaceControl->show(); - SurfaceComposerClient::closeGlobalTransaction(); + if (!mSurface.get()) { + createSurface(); } + return mSurface; +} + +void TestContext::createSurface() { + if (mRenderOffscreen) { + createOffscreenSurface(); + } else { + createWindowSurface(); + } +} - return mSurfaceControl->getSurface(); +void TestContext::createWindowSurface() { + mSurfaceControl = mSurfaceComposerClient->createSurface(String8("HwuiTest"), + gDisplay.w, gDisplay.h, PIXEL_FORMAT_RGBX_8888); + + SurfaceComposerClient::openGlobalTransaction(); + mSurfaceControl->setLayer(0x7FFFFFF); + mSurfaceControl->show(); + SurfaceComposerClient::closeGlobalTransaction(); + mSurface = mSurfaceControl->getSurface(); +} + +void TestContext::createOffscreenSurface() { + sp<IGraphicBufferProducer> producer; + sp<IGraphicBufferConsumer> consumer; + BufferQueue::createBufferQueue(&producer, &consumer); + producer->setMaxDequeuedBufferCount(3); + producer->setAsyncMode(true); + mConsumer = new BufferItemConsumer(consumer, GRALLOC_USAGE_HW_COMPOSER, 4); + mConsumer->setDefaultBufferSize(gDisplay.w, gDisplay.h); + mSurface = new Surface(producer); } void TestContext::waitForVsync() { + if (mConsumer.get()) { + BufferItem buffer; + if (mConsumer->acquireBuffer(&buffer, 0, false) == OK) { + // We assume the producer is internally ordered enough such that + // it is unneccessary to set a release fence + mConsumer->releaseBuffer(buffer); + } + // We running free, go go go! + return; + } #if !HWUI_NULL_GPU // Request vsync mDisplayEventReceiver.requestNextVsync(); diff --git a/libs/hwui/tests/common/TestContext.h b/libs/hwui/tests/common/TestContext.h index 2bbe5dffd9b8..312988b968de 100644 --- a/libs/hwui/tests/common/TestContext.h +++ b/libs/hwui/tests/common/TestContext.h @@ -19,12 +19,16 @@ #include <gui/DisplayEventReceiver.h> #include <gui/ISurfaceComposer.h> +#include <gui/BufferItemConsumer.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> + namespace android { namespace uirenderer { namespace test { @@ -39,15 +43,29 @@ public: TestContext(); ~TestContext(); + // Must be called before surface(); + void setRenderOffscreen(bool renderOffscreen) { + LOG_ALWAYS_FATAL_IF(mSurface.get(), + "Must be called before surface is created"); + mRenderOffscreen = renderOffscreen; + } + sp<Surface> surface(); void waitForVsync(); private: + void createSurface(); + void createWindowSurface(); + void createOffscreenSurface(); + sp<SurfaceComposerClient> mSurfaceComposerClient; sp<SurfaceControl> mSurfaceControl; + sp<BufferItemConsumer> mConsumer; DisplayEventReceiver mDisplayEventReceiver; sp<Looper> mLooper; + sp<Surface> mSurface; + bool mRenderOffscreen; }; } // namespace test diff --git a/libs/hwui/tests/common/TestListViewSceneBase.cpp b/libs/hwui/tests/common/TestListViewSceneBase.cpp new file mode 100644 index 000000000000..6d2e85996444 --- /dev/null +++ b/libs/hwui/tests/common/TestListViewSceneBase.cpp @@ -0,0 +1,77 @@ +/* + * 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 "TestListViewSceneBase.h" + +#include "TestContext.h" +#include "TestUtils.h" + +#include <utils/Color.h> + +namespace android { +namespace uirenderer { +namespace test { + +void TestListViewSceneBase::createContent(int width, int height, Canvas& canvas) { + srand(0); + mItemHeight = dp(60); + mItemSpacing = dp(16); + mItemWidth = std::min((height - mItemSpacing * 2), (int)dp(300)); + mItemLeft = (width - mItemWidth) / 2; + int heightWithSpacing = mItemHeight + mItemSpacing; + 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); + 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()); + } + }); + + canvas.drawColor(Color::Grey_500, SkBlendMode::kSrcOver); + canvas.drawRenderNode(mListView.get()); +} + +void TestListViewSceneBase::doFrame(int frameNr) { + int scrollPx = dp(frameNr) * 3; + 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())); + for (size_t ci = 0; ci < mListItems.size(); ci++) { + // update item position + auto listItem = mListItems[(ci + itemIndexOffset) % mListItems.size()]; + int top = ((int)ci) * (mItemSpacing + mItemHeight) + pxOffset; + listItem->mutateStagingProperties().setLeftTopRightBottom( + mItemLeft, top, mItemLeft + mItemWidth, top + mItemHeight); + listItem->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y); + + // draw it to parent DisplayList + canvas->drawRenderNode(mListItems[ci].get()); + } + mListView->setStagingDisplayList(canvas->finishRecording(), nullptr); +} + +} // namespace test +} // namespace uirenderer +} // namespace android diff --git a/libs/hwui/tests/common/TestListViewSceneBase.h b/libs/hwui/tests/common/TestListViewSceneBase.h new file mode 100644 index 000000000000..ed6867ab3750 --- /dev/null +++ b/libs/hwui/tests/common/TestListViewSceneBase.h @@ -0,0 +1,44 @@ +/* + * 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. + */ +#pragma once + +#include "TestScene.h" +#include <RenderNode.h> +#include <RenderProperties.h> + +namespace android { +namespace uirenderer { +namespace test { + +class TestListViewSceneBase : public TestScene { +public: + 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; + + void createContent(int width, int height, Canvas& canvas) override; + void doFrame(int frameNr) override; +}; + +} // namespace test +} // namespace uirenderer +} // namespace android diff --git a/libs/hwui/tests/common/TestScene.h b/libs/hwui/tests/common/TestScene.h index 060984ea9f32..f6f7c62a4f63 100644 --- a/libs/hwui/tests/common/TestScene.h +++ b/libs/hwui/tests/common/TestScene.h @@ -13,23 +13,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#ifndef TESTS_TESTSCENE_H -#define TESTS_TESTSCENE_H + +#pragma once #include <string> #include <unordered_map> namespace android { + +class Canvas; + namespace uirenderer { class RenderNode; - -#if HWUI_NEW_OPS class RecordingCanvas; -typedef RecordingCanvas TestCanvas; -#else -class DisplayListCanvas; -typedef DisplayListCanvas TestCanvas; -#endif namespace test { @@ -38,6 +34,7 @@ public: struct Options { int count = 0; int reportFrametimeWeight = 0; + bool renderOffscreen = true; }; template <class T> @@ -65,7 +62,7 @@ public: }; virtual ~TestScene() {} - virtual void createContent(int width, int height, TestCanvas& renderer) = 0; + virtual void createContent(int width, int height, Canvas& renderer) = 0; virtual void doFrame(int frameNr) = 0; static std::unordered_map<std::string, Info>& testMap(); @@ -75,5 +72,3 @@ public: } // namespace test } // namespace uirenderer } // namespace android - -#endif /* TESTS_TESTSCENE_H */ diff --git a/libs/hwui/tests/common/TestUtils.cpp b/libs/hwui/tests/common/TestUtils.cpp index c762eed616e4..275ce166728b 100644 --- a/libs/hwui/tests/common/TestUtils.cpp +++ b/libs/hwui/tests/common/TestUtils.cpp @@ -18,7 +18,16 @@ #include "hwui/Paint.h" #include "DeferredLayerUpdater.h" -#include "LayerRenderer.h" + +#include <renderthread/EglManager.h> +#include <renderthread/OpenGLPipeline.h> +#include <pipeline/skia/SkiaOpenGLPipeline.h> +#include <pipeline/skia/SkiaVulkanPipeline.h> +#include <renderthread/VulkanManager.h> +#include <utils/Unicode.h> +#include <SkClipStack.h> + +#include <SkGlyphCache.h> namespace android { namespace uirenderer { @@ -41,22 +50,30 @@ SkColor TestUtils::interpolateColor(float fraction, SkColor start, SkColor end) } sp<DeferredLayerUpdater> TestUtils::createTextureLayerUpdater( + renderthread::RenderThread& renderThread) { + android::uirenderer::renderthread::IRenderPipeline* pipeline; + if (Properties::getRenderPipelineType() == RenderPipelineType::OpenGL) { + pipeline = new renderthread::OpenGLPipeline(renderThread); + } else if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaGL) { + pipeline = new skiapipeline::SkiaOpenGLPipeline(renderThread); + } else { + pipeline = new skiapipeline::SkiaVulkanPipeline(renderThread); + } + sp<DeferredLayerUpdater> layerUpdater = pipeline->createTextureLayer(); + delete pipeline; + return layerUpdater; +} + +sp<DeferredLayerUpdater> TestUtils::createTextureLayerUpdater( renderthread::RenderThread& renderThread, uint32_t width, uint32_t height, const SkMatrix& transform) { - Layer* layer = LayerRenderer::createTextureLayer(renderThread.renderState()); - layer->getTransform().load(transform); - - sp<DeferredLayerUpdater> layerUpdater = new DeferredLayerUpdater(layer); + sp<DeferredLayerUpdater> layerUpdater = createTextureLayerUpdater(renderThread); + layerUpdater->backingLayer()->getTransform().load(transform); layerUpdater->setSize(width, height); layerUpdater->setTransform(&transform); // updateLayer so it's ready to draw - bool isOpaque = true; - bool forceFilter = true; - GLenum renderTarget = GL_TEXTURE_EXTERNAL_OES; - LayerRenderer::updateTextureLayer(layer, width, height, isOpaque, forceFilter, - renderTarget, Matrix4::identity().data); - + layerUpdater->updateLayer(true, GL_TEXTURE_EXTERNAL_OES, Matrix4::identity().data); return layerUpdater; } @@ -68,7 +85,10 @@ void TestUtils::layoutTextUnscaled(const SkPaint& paint, const char* text, SkSurfaceProps surfaceProps(0, kUnknown_SkPixelGeometry); SkAutoGlyphCacheNoGamma autoCache(paint, &surfaceProps, &SkMatrix::I()); while (*text != '\0') { - SkUnichar unichar = SkUTF8_NextUnichar(&text); + size_t nextIndex = 0; + int32_t unichar = utf32_from_utf8_at(text, 4, 0, &nextIndex); + text += nextIndex; + glyph_t glyph = autoCache.getCache()->unicharToGlyph(unichar); autoCache.getCache()->unicharToGlyph(unichar); @@ -107,12 +127,21 @@ void TestUtils::drawUtf8ToCanvas(Canvas* canvas, const char* text, void TestUtils::TestTask::run() { // RenderState only valid once RenderThread is running, so queried here - RenderState& renderState = renderthread::RenderThread::getInstance().renderState(); + renderthread::RenderThread& renderThread = renderthread::RenderThread::getInstance(); + if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) { + renderThread.vulkanManager().initialize(); + } else { + renderThread.eglManager().initialize(); + } - renderState.onGLContextCreated(); - rtCallback(renderthread::RenderThread::getInstance()); - renderState.flush(Caches::FlushMode::Full); - renderState.onGLContextDestroyed(); + rtCallback(renderThread); + + if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) { + renderThread.vulkanManager().destroy(); + } else { + renderThread.renderState().flush(Caches::FlushMode::Full); + renderThread.eglManager().destroy(); + } } std::unique_ptr<uint16_t[]> TestUtils::asciiToUtf16(const char* str) { @@ -124,5 +153,62 @@ std::unique_ptr<uint16_t[]> TestUtils::asciiToUtf16(const char* str) { return utf16; } +SkColor TestUtils::getColor(const sk_sp<SkSurface>& surface, int x, int y) { + SkPixmap pixmap; + if (!surface->peekPixels(&pixmap)) { + return 0; + } + switch (pixmap.colorType()) { + case kGray_8_SkColorType: { + const uint8_t* addr = pixmap.addr8(x, y); + return SkColorSetRGB(*addr, *addr, *addr); + } + case kAlpha_8_SkColorType: { + const uint8_t* addr = pixmap.addr8(x, y); + return SkColorSetA(0, addr[0]); + } + case kRGB_565_SkColorType: { + const uint16_t* addr = pixmap.addr16(x, y); + return SkPixel16ToColor(addr[0]); + } + case kARGB_4444_SkColorType: { + const uint16_t* addr = pixmap.addr16(x, y); + SkPMColor c = SkPixel4444ToPixel32(addr[0]); + return SkUnPreMultiply::PMColorToColor(c); + } + case kBGRA_8888_SkColorType: { + const uint32_t* addr = pixmap.addr32(x, y); + SkPMColor c = SkSwizzle_BGRA_to_PMColor(addr[0]); + return SkUnPreMultiply::PMColorToColor(c); + } + case kRGBA_8888_SkColorType: { + const uint32_t* addr = pixmap.addr32(x, y); + SkPMColor c = SkSwizzle_RGBA_to_PMColor(addr[0]); + return SkUnPreMultiply::PMColorToColor(c); + } + default: + return 0; + } + return 0; +} + +SkRect TestUtils::getClipBounds(const SkCanvas* canvas) { + SkClipStack::BoundsType boundType; + SkRect clipBounds; + canvas->getClipStack()->getBounds(&clipBounds, &boundType); + return clipBounds; +} + +SkRect TestUtils::getLocalClipBounds(const SkCanvas* canvas) { + SkMatrix invertedTotalMatrix; + if (!canvas->getTotalMatrix().invert(&invertedTotalMatrix)) { + return SkRect::MakeEmpty(); + } + SkRect outlineInDeviceCoord = TestUtils::getClipBounds(canvas); + SkRect outlineInLocalCoord; + invertedTotalMatrix.mapRect(&outlineInLocalCoord, outlineInDeviceCoord); + return outlineInLocalCoord; +} + } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/tests/common/TestUtils.h b/libs/hwui/tests/common/TestUtils.h index 5a4ab99012b9..8b287de3220e 100644 --- a/libs/hwui/tests/common/TestUtils.h +++ b/libs/hwui/tests/common/TestUtils.h @@ -13,37 +13,29 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#ifndef TEST_UTILS_H -#define TEST_UTILS_H + +#pragma once #include <DeviceInfo.h> #include <DisplayList.h> #include <Matrix.h> +#include <Properties.h> #include <Rect.h> #include <RenderNode.h> +#include <hwui/Bitmap.h> +#include <pipeline/skia/SkiaRecordingCanvas.h> #include <renderstate/RenderState.h> #include <renderthread/RenderThread.h> #include <Snapshot.h> -#if HWUI_NEW_OPS #include <RecordedOp.h> #include <RecordingCanvas.h> -#else -#include <DisplayListOp.h> -#include <DisplayListCanvas.h> -#endif #include <memory> namespace android { namespace uirenderer { -#if HWUI_NEW_OPS -typedef RecordingCanvas TestCanvas; -#else -typedef DisplayListCanvas TestCanvas; -#endif - #define EXPECT_MATRIX_APPROX_EQ(a, b) \ EXPECT_TRUE(TestUtils::matricesAreApproxEqual(a, b)) @@ -60,6 +52,31 @@ typedef DisplayListCanvas TestCanvas; } 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); \ + }; + +/** + * 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()) \ + 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)) + /** * Like gtest's TEST, but runs on the RenderThread, and 'renderThread' is passed, in top level scope * (for e.g. accessing its RenderState) @@ -69,9 +86,32 @@ typedef DisplayListCanvas TestCanvas; public: \ static void doTheThing(renderthread::RenderThread& renderThread); \ }; \ - TEST(test_case_name, test_name) { \ - TestUtils::runOnRenderThread(test_case_name##_##test_name##_RenderThreadTest::doTheThing); \ + INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, OpenGL); \ + INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, SkiaGL); \ + 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: \ + 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) + +/** + * 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); \ + INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, SkiaVulkan); \ void test_case_name##_##test_name##_RenderThreadTest::doTheThing(renderthread::RenderThread& renderThread) /** @@ -127,22 +167,28 @@ public: static std::unique_ptr<Snapshot> makeSnapshot(const Matrix4& transform, const Rect& clip) { std::unique_ptr<Snapshot> snapshot(new Snapshot()); - snapshot->clip(clip, SkRegion::kReplace_Op); // store clip first, so it isn't transformed + // store clip first, so it isn't transformed + snapshot->setClip(clip.left, clip.top, clip.right, clip.bottom); *(snapshot->transform) = transform; return snapshot; } - static SkBitmap createSkBitmap(int width, int height, + static sk_sp<Bitmap> createBitmap(int width, int height, SkColorType colorType = kN32_SkColorType) { - SkBitmap bitmap; - SkImageInfo info = SkImageInfo::Make(width, height, - colorType, kPremul_SkAlphaType); - bitmap.setInfo(info); - bitmap.allocPixels(info); - return bitmap; + SkImageInfo info = SkImageInfo::Make(width, height, colorType, kPremul_SkAlphaType); + return Bitmap::allocateHeapBitmap(info); + } + + static sk_sp<Bitmap> createBitmap(int width, int height, SkBitmap* outBitmap) { + SkImageInfo info = SkImageInfo::Make(width, height, kN32_SkColorType, kPremul_SkAlphaType); + outBitmap->setInfo(info); + return Bitmap::allocateHeapBitmap(outBitmap, nullptr); } static sp<DeferredLayerUpdater> createTextureLayerUpdater( + renderthread::RenderThread& renderThread); + + static sp<DeferredLayerUpdater> createTextureLayerUpdater( renderthread::RenderThread& renderThread, uint32_t width, uint32_t height, const SkMatrix& transform); @@ -155,7 +201,7 @@ public: } static sp<RenderNode> createNode(int left, int top, int right, int bottom, - std::function<void(RenderProperties& props, TestCanvas& canvas)> setup) { + 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 // DeviceInfo::maxTextureSize() affects layer property @@ -166,7 +212,29 @@ public: RenderProperties& props = node->mutateStagingProperties(); props.setLeftTopRightBottom(left, top, right, bottom); if (setup) { - TestCanvas 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(), nullptr); + } + node->setPropertyFieldsDirty(0xFFFFFFFF); + return node; + } + + 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 + // DeviceInfo::maxTextureSize() affects layer property + DeviceInfo::initialize(); +#endif + + sp<RenderNode> node = new RenderNode(); + RenderProperties& props = node->mutateStagingProperties(); + props.setLeftTopRightBottom(left, top, right, bottom); + if (setup) { + RecordingCanvasType canvas(props.getWidth(), props.getHeight()); setup(props, canvas); node->setStagingDisplayList(canvas.finishRecording(), nullptr); } @@ -175,11 +243,40 @@ public: } static void recordNode(RenderNode& node, - std::function<void(TestCanvas&)> contentCallback) { - TestCanvas canvas(node.stagingProperties().getWidth(), - node.stagingProperties().getHeight()); - contentCallback(canvas); - node.setStagingDisplayList(canvas.finishRecording(), nullptr); + 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(), nullptr); + } + + 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 RenderNodes are being sync'd/used, device info will be needed, since + // DeviceInfo::maxTextureSize() affects layer property + DeviceInfo::initialize(); + #endif + sp<RenderNode> node = new RenderNode(); + if (name) { + node->setName(name); + } + RenderProperties& props = node->mutateStagingProperties(); + props.setLeftTopRightBottom(left, top, right, bottom); + if (displayList) { + node->setStagingDisplayList(displayList, nullptr); + } + if (setup) { + std::unique_ptr<skiapipeline::SkiaRecordingCanvas> canvas( + new skiapipeline::SkiaRecordingCanvas(nullptr, + props.getWidth(), props.getHeight())); + setup(props, *canvas.get()); + node->setStagingDisplayList(canvas->finishRecording(), nullptr); + } + node->setPropertyFieldsDirty(0xFFFFFFFF); + TestUtils::syncHierarchyPropertiesAndDisplayList(node); + return node; } /** @@ -235,6 +332,22 @@ public: 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; + }; + + static SkColor getColor(const sk_sp<SkSurface>& surface, int x, int y); + + static SkRect getClipBounds(const SkCanvas* canvas); + static SkRect getLocalClipBounds(const SkCanvas* canvas); + private: static void syncHierarchyPropertiesAndDisplayListImpl(RenderNode* node) { node->syncProperties(); @@ -251,5 +364,3 @@ private: } /* namespace uirenderer */ } /* namespace android */ - -#endif /* TEST_UTILS_H */ diff --git a/libs/hwui/tests/common/scenes/BitmapFillrate.cpp b/libs/hwui/tests/common/scenes/BitmapFillrate.cpp new file mode 100644 index 000000000000..be58d09b7f4d --- /dev/null +++ b/libs/hwui/tests/common/scenes/BitmapFillrate.cpp @@ -0,0 +1,74 @@ +/* + * 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 "TestSceneBase.h" +#include "tests/common/BitmapAllocationTestUtils.h" +#include "utils/Color.h" + +#include <SkBitmap.h> + +using namespace android; +using namespace android::uirenderer; + +class BitmapFillrate; + +static bool _BitmapFillrate( + BitmapAllocationTestUtils::registerBitmapAllocationScene<BitmapFillrate>( + "bitmapFillrate", "Draws multiple large half transparent bitmaps.")); + +class BitmapFillrate : public TestScene { +public: + BitmapFillrate(BitmapAllocationTestUtils::BitmapAllocator allocator) + : TestScene() + , mAllocator(allocator) { } + + void createContent(int width, int height, Canvas& canvas) override { + canvas.drawColor(Color::White, SkBlendMode::kSrcOver); + createNode(canvas, 0x909C27B0, 0, 0, width, height); + 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, 0xA0FFC107, width / 6, 0, width, height); + } + + void doFrame(int frameNr) override { + for (size_t ci = 0; ci < mNodes.size(); ci++) { + mNodes[ci]->mutateStagingProperties().setTranslationX(frameNr % 200); + mNodes[ci]->mutateStagingProperties().setTranslationY(frameNr % 200); + mNodes[ci]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y); + } + } +private: + 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, + [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); + }); + canvas.drawRenderNode(card.get()); + mNodes.push_back(card); + } + + BitmapAllocationTestUtils::BitmapAllocator mAllocator; + 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 new file mode 100644 index 000000000000..e03c9e84b739 --- /dev/null +++ b/libs/hwui/tests/common/scenes/BitmapShaders.cpp @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2015 The Android Open 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 "utils/Color.h" +#include "tests/common/BitmapAllocationTestUtils.h" +#include <SkImagePriv.h> + +class BitmapShaders; + +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) { } + + 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); + }); + + SkBitmap bitmap; + SkPaint paint; + hwuiBitmap->getSkBitmapForShaders(&bitmap); + + sk_sp<SkShader> repeatShader = SkMakeBitmapShader(bitmap, + SkShader::TileMode::kRepeat_TileMode, + SkShader::TileMode::kRepeat_TileMode, + nullptr, + kNever_SkCopyPixelsMode, + nullptr); + paint.setShader(std::move(repeatShader)); + canvas.drawRoundRect(0, 0, 500, 500, 50.0f, 50.0f, paint); + + sk_sp<SkShader> mirrorShader = SkMakeBitmapShader(bitmap, + SkShader::TileMode::kMirror_TileMode, + SkShader::TileMode::kMirror_TileMode, + nullptr, + kNever_SkCopyPixelsMode, + nullptr); + paint.setShader(std::move(mirrorShader)); + canvas.drawRoundRect(0, 600, 500, 1100, 50.0f, 50.0f, paint); + } + + 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 a5fd71266314..f47e05a1f3e6 100644 --- a/libs/hwui/tests/common/scenes/ClippingAnimation.cpp +++ b/libs/hwui/tests/common/scenes/ClippingAnimation.cpp @@ -28,18 +28,18 @@ static TestScene::Registrar _RectGrid(TestScene::Info{ class ClippingAnimation : public TestScene { public: sp<RenderNode> card; - void createContent(int width, int height, TestCanvas& canvas) override { - canvas.drawColor(Color::White, SkXfermode::kSrcOver_Mode); + void createContent(int width, int height, Canvas& canvas) override { + canvas.drawColor(Color::White, SkBlendMode::kSrcOver); card = TestUtils::createNode(0, 0, 200, 400, - [](RenderProperties& props, TestCanvas& canvas) { + [](RenderProperties& props, Canvas& canvas) { canvas.save(SaveFlags::MatrixClip); { - canvas.clipRect(0, 0, 200, 200, SkRegion::kIntersect_Op); + canvas.clipRect(0, 0, 200, 200, SkClipOp::kIntersect); canvas.translate(100, 100); canvas.rotate(45); canvas.translate(-100, -100); - canvas.clipRect(0, 0, 200, 200, SkRegion::kIntersect_Op); - canvas.drawColor(Color::Blue_500, SkXfermode::kSrcOver_Mode); + canvas.clipRect(0, 0, 200, 200, SkClipOp::kIntersect); + canvas.drawColor(Color::Blue_500, SkBlendMode::kSrcOver); } canvas.restore(); @@ -47,8 +47,8 @@ public: { SkPath clipCircle; clipCircle.addCircle(100, 300, 100); - canvas.clipPath(&clipCircle, SkRegion::kIntersect_Op); - canvas.drawColor(Color::Red_500, SkXfermode::kSrcOver_Mode); + canvas.clipPath(&clipCircle, SkClipOp::kIntersect); + canvas.drawColor(Color::Red_500, SkBlendMode::kSrcOver); } canvas.restore(); diff --git a/libs/hwui/tests/common/scenes/GlyphStressAnimation.cpp b/libs/hwui/tests/common/scenes/GlyphStressAnimation.cpp index f184411b4139..c0d9450ebace 100644 --- a/libs/hwui/tests/common/scenes/GlyphStressAnimation.cpp +++ b/libs/hwui/tests/common/scenes/GlyphStressAnimation.cpp @@ -17,8 +17,8 @@ #include "TestSceneBase.h" #include "utils/Color.h" -#include <minikin/Layout.h> #include <hwui/Paint.h> +#include <minikin/Layout.h> #include <cstdio> @@ -33,11 +33,11 @@ static TestScene::Registrar _GlyphStress(TestScene::Info{ class GlyphStressAnimation : public TestScene { public: sp<RenderNode> container; - void createContent(int width, int height, TestCanvas& canvas) override { + void createContent(int width, int height, Canvas& canvas) override { container = TestUtils::createNode(0, 0, width, height, nullptr); doFrame(0); // update container - canvas.drawColor(Color::White, SkXfermode::kSrcOver_Mode); + canvas.drawColor(Color::White, SkBlendMode::kSrcOver); canvas.drawRenderNode(container.get()); } @@ -46,19 +46,20 @@ public: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"); ssize_t textLength = 26 * 2; - TestCanvas canvas( + std::unique_ptr<Canvas> canvas(Canvas::create_recording_canvas( container->stagingProperties().getWidth(), - container->stagingProperties().getHeight()); + 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), kBidi_Force_LTR, paint, nullptr); + canvas->drawText(text.get(), 0, textLength, textLength, + 0, 100 * (i + 2), minikin::kBidi_Force_LTR, paint, nullptr); } - container->setStagingDisplayList(canvas.finishRecording(), nullptr); + container->setStagingDisplayList(canvas->finishRecording(), nullptr); } }; diff --git a/libs/hwui/tests/common/scenes/HwBitmap565.cpp b/libs/hwui/tests/common/scenes/HwBitmap565.cpp new file mode 100644 index 000000000000..18fea3d1cb81 --- /dev/null +++ b/libs/hwui/tests/common/scenes/HwBitmap565.cpp @@ -0,0 +1,49 @@ +/* + * 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 "TestSceneBase.h" +#include "utils/Color.h" +#include "tests/common/BitmapAllocationTestUtils.h" + +class HwBitmap565; + +static TestScene::Registrar _HwBitmap565(TestScene::Info{ + "hwBitmap565", + "Draws composite shader with hardware bitmap", + TestScene::simpleCreateScene<HwBitmap565> +}); + +class HwBitmap565 : public TestScene { +public: + sp<RenderNode> card; + 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); + }); + canvas.drawBitmap(*hardwareBitmap, 10.0f, 10.0f, nullptr); + } + + 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 new file mode 100644 index 000000000000..83b01e906427 --- /dev/null +++ b/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp @@ -0,0 +1,91 @@ +/* + * 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 "TestSceneBase.h" +#include "utils/Color.h" + +#include <gui/IGraphicBufferAlloc.h> +#include <gui/ISurfaceComposer.h> +#include <private/gui/ComposerService.h> +#include <binder/IServiceManager.h> +#include <ui/PixelFormat.h> +#include <SkGradientShader.h> +#include <SkImagePriv.h> + +class HwBitmapInCompositeShader; + +static TestScene::Registrar _HwBitmapInCompositeShader(TestScene::Info{ + "hwbitmapcompositeshader", + "Draws composite shader with hardware bitmap", + TestScene::simpleCreateScene<HwBitmapInCompositeShader> +}); + +class HwBitmapInCompositeShader : public TestScene { +public: + sp<RenderNode> card; + void createContent(int width, int height, Canvas& canvas) override { + canvas.drawColor(Color::Red_500, SkBlendMode::kSrcOver); + + status_t error; + sp<ISurfaceComposer> composer(ComposerService::getComposerService()); + sp<IGraphicBufferAlloc> alloc(composer->createGraphicBufferAlloc()); + uint32_t usage = GraphicBuffer::USAGE_HW_TEXTURE + | GraphicBuffer::USAGE_SW_READ_NEVER + | GRALLOC_USAGE_SW_WRITE_RARELY; + sp<GraphicBuffer> buffer = alloc->createGraphicBuffer(400, 200, PIXEL_FORMAT_RGBA_8888, 1, + usage, &error); + + unsigned char* pixels = nullptr; + buffer->lock(GraphicBuffer::USAGE_SW_WRITE_RARELY, ((void**)&pixels)); + 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; + pixels[4000 + 4 * i + 1] = 255; + pixels[4000 + 4 * i + 2] = 0; + pixels[4000 + 4 * i + 3] = 255; + } + buffer->unlock(); + sk_sp<Bitmap> hardwareBitmap(Bitmap::createFrom(buffer)); + sk_sp<SkShader> hardwareShader(createBitmapShader(*hardwareBitmap)); + + SkPoint center; + center.set(50, 50); + 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> compositeShader( + SkShader::MakeComposeShader(hardwareShader, gradientShader, SkBlendMode::kDstATop)); + + SkPaint paint; + paint.setShader(std::move(compositeShader)); + canvas.drawRoundRect(0, 0, 400, 200, 10.0f, 10.0f, paint); + } + + void doFrame(int frameNr) override { } + + sk_sp<SkShader> createBitmapShader(Bitmap& bitmap) { + SkBitmap skBitmap; + bitmap.getSkBitmapForShaders(&skBitmap); + sk_sp<SkImage> image = SkMakeImageFromRasterBitmap(skBitmap, kNever_SkCopyPixelsMode); + return image->makeShader(SkShader::TileMode::kClamp_TileMode, + SkShader::TileMode::kClamp_TileMode); + } +};
\ No newline at end of file diff --git a/libs/hwui/tests/common/scenes/HwLayerAnimation.cpp b/libs/hwui/tests/common/scenes/HwLayerAnimation.cpp index c212df4f978d..3a230ae6e8b7 100644 --- a/libs/hwui/tests/common/scenes/HwLayerAnimation.cpp +++ b/libs/hwui/tests/common/scenes/HwLayerAnimation.cpp @@ -28,13 +28,13 @@ static TestScene::Registrar _HwLayer(TestScene::Info{ class HwLayerAnimation : public TestScene { public: sp<RenderNode> card; - void createContent(int width, int height, TestCanvas& canvas) override { + void createContent(int width, int height, Canvas& canvas) override { card = TestUtils::createNode(0, 0, 200, 200, - [](RenderProperties& props, TestCanvas& canvas) { + [](RenderProperties& props, Canvas& canvas) { props.mutateLayerProperties().setType(LayerType::RenderLayer); - canvas.drawColor(0xFF0000FF, SkXfermode::kSrcOver_Mode); + canvas.drawColor(0xFF0000FF, SkBlendMode::kSrcOver); }); - canvas.drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode); // background + canvas.drawColor(0xFFFFFFFF, SkBlendMode::kSrcOver); // background canvas.drawRenderNode(card.get()); } void doFrame(int frameNr) override { diff --git a/libs/hwui/tests/common/scenes/ListOfFadedTextAnimation.cpp b/libs/hwui/tests/common/scenes/ListOfFadedTextAnimation.cpp new file mode 100644 index 000000000000..b7357e179bfe --- /dev/null +++ b/libs/hwui/tests/common/scenes/ListOfFadedTextAnimation.cpp @@ -0,0 +1,59 @@ +/* + * 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 "TestSceneBase.h" +#include "tests/common/TestListViewSceneBase.h" + +#include <SkGradientShader.h> + +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> +}); + +class ListOfFadedTextAnimation : public TestListViewSceneBase { + 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); + SkPaint textPaint; + textPaint.setTextSize(dp(20)); + textPaint.setAntiAlias(true); + TestUtils::drawUtf8ToCanvas(&canvas, "not that long long text", textPaint, dp(10), dp(30)); + + SkPoint pts[2]; + pts[0].set(0, 0); + 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)); + + SkMatrix matrix; + matrix.setScale(1, length); + matrix.postRotate(-90); + SkPaint fadingPaint; + fadingPaint.setShader(s->makeWithLocalMatrix(matrix)); + fadingPaint.setBlendMode(SkBlendMode::kDstOut); + canvas.drawRect(0, 0, length, itemHeight, fadingPaint); + canvas.restore(); + } +}; diff --git a/libs/hwui/tests/common/scenes/ListViewAnimation.cpp b/libs/hwui/tests/common/scenes/ListViewAnimation.cpp index 8035dc45f23c..c1144be5b57c 100644 --- a/libs/hwui/tests/common/scenes/ListViewAnimation.cpp +++ b/libs/hwui/tests/common/scenes/ListViewAnimation.cpp @@ -15,7 +15,7 @@ */ #include "TestSceneBase.h" -#include "utils/Color.h" +#include "tests/common/TestListViewSceneBase.h" #include <cstdio> @@ -28,61 +28,12 @@ static TestScene::Registrar _ListView(TestScene::Info{ TestScene::simpleCreateScene<ListViewAnimation> }); -class ListViewAnimation : public TestScene { -public: - int cardHeight; - int cardSpacing; - int cardWidth; - int cardLeft; - sp<RenderNode> listView; - std::vector< sp<RenderNode> > cards; - void createContent(int width, int height, TestCanvas& canvas) override { - srand(0); - cardHeight = dp(60); - cardSpacing = dp(16); - cardWidth = std::min((height - cardSpacing * 2), (int)dp(300)); - cardLeft = (width - cardWidth) / 2; - - for (int y = 0; y < height + (cardHeight + cardSpacing - 1); y += (cardHeight + cardSpacing)) { - cards.push_back(createCard(cards.size(), y)); - } - listView = TestUtils::createNode(0, 0, width, height, - [this](RenderProperties& props, TestCanvas& canvas) { - for (size_t ci = 0; ci < cards.size(); ci++) { - canvas.drawRenderNode(cards[ci].get()); - } - }); - - canvas.drawColor(Color::Grey_500, SkXfermode::kSrcOver_Mode); - canvas.drawRenderNode(listView.get()); - } - - void doFrame(int frameNr) override { - int scrollPx = dp(frameNr) * 3; - int cardIndexOffset = scrollPx / (cardSpacing + cardHeight); - int pxOffset = -(scrollPx % (cardSpacing + cardHeight)); - - TestCanvas canvas( - listView->stagingProperties().getWidth(), - listView->stagingProperties().getHeight()); - for (size_t ci = 0; ci < cards.size(); ci++) { - // update card position - auto card = cards[(ci + cardIndexOffset) % cards.size()]; - int top = ((int)ci) * (cardSpacing + cardHeight) + pxOffset; - card->mutateStagingProperties().setLeftTopRightBottom( - cardLeft, top, cardLeft + cardWidth, top + cardHeight); - card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y); - - // draw it to parent DisplayList - canvas.drawRenderNode(cards[ci].get()); - } - listView->setStagingDisplayList(canvas.finishRecording(), nullptr); - } -private: - SkBitmap createRandomCharIcon() { +class ListViewAnimation : public TestListViewSceneBase { + sk_sp<Bitmap> createRandomCharIcon(int cardHeight) { + SkBitmap skBitmap; int size = cardHeight - (dp(10) * 2); - SkBitmap bitmap = TestUtils::createSkBitmap(size, size); - SkCanvas canvas(bitmap); + sk_sp<Bitmap> bitmap(TestUtils::createBitmap(size, size, &skBitmap)); + SkCanvas canvas(skBitmap); canvas.clear(0); SkPaint paint; @@ -97,15 +48,19 @@ private: paint.setTextAlign(SkPaint::kCenter_Align); paint.setTextSize(size / 2); char charToShow = 'A' + (rand() % 26); - canvas.drawText(&charToShow, 1, size / 2, /*approximate centering*/ size * 0.7, paint); + const SkPoint pos[] = {{ + SkIntToScalar(size / 2), + /*approximate centering*/ SkFloatToScalar(size * 0.7f)}}; + canvas.drawPosText(&charToShow, 1, pos, paint); return bitmap; } - static SkBitmap createBoxBitmap(bool filled) { + static sk_sp<Bitmap> createBoxBitmap(bool filled) { int size = dp(20); int stroke = dp(2); - SkBitmap bitmap = TestUtils::createSkBitmap(size, size); - SkCanvas canvas(bitmap); + SkBitmap skBitmap; + auto bitmap = TestUtils::createBitmap(size, size, &skBitmap); + SkCanvas canvas(skBitmap); canvas.clear(Color::Transparent); SkPaint paint; @@ -117,34 +72,32 @@ private: return bitmap; } - sp<RenderNode> createCard(int cardId, int top) { - return TestUtils::createNode(cardLeft, top, cardLeft + cardWidth, top + cardHeight, - [this, cardId](RenderProperties& props, TestCanvas& canvas) { - static SkBitmap filledBox = createBoxBitmap(true); - static SkBitmap strokedBox = createBoxBitmap(false); - - // TODO: switch to using round rect clipping, once merging correctly handles that - SkPaint roundRectPaint; - roundRectPaint.setAntiAlias(true); - roundRectPaint.setColor(Color::White); - canvas.drawRoundRect(0, 0, cardWidth, cardHeight, 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); - char buf[256]; - snprintf(buf, sizeof(buf), "This card is #%d", cardId); - TestUtils::drawUtf8ToCanvas(&canvas, buf, textPaint, cardHeight, dp(25)); - textPaint.setTextSize(dp(15)); - TestUtils::drawUtf8ToCanvas(&canvas, "This is some more text on the card", textPaint, - cardHeight, dp(45)); - - canvas.drawBitmap(createRandomCharIcon(), dp(10), dp(10), nullptr); - - const SkBitmap& boxBitmap = rand() % 2 ? filledBox : strokedBox; - canvas.drawBitmap(boxBitmap, cardWidth - dp(10) - boxBitmap.width(), dp(10), nullptr); - }); + 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 + SkPaint roundRectPaint; + roundRectPaint.setAntiAlias(true); + roundRectPaint.setColor(Color::White); + 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); + char buf[256]; + snprintf(buf, sizeof(buf), "This card is #%d", cardId); + 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)); + + auto randomIcon = createRandomCharIcon(itemHeight); + canvas.drawBitmap(*randomIcon, dp(10), dp(10), nullptr); + + auto box = rand() % 2 ? filledBox : strokedBox; + canvas.drawBitmap(*box, itemWidth - dp(10) - box->width(), dp(10), nullptr); } }; diff --git a/libs/hwui/tests/common/scenes/OpPropAnimation.cpp b/libs/hwui/tests/common/scenes/OpPropAnimation.cpp index 5dfb2b4a8b33..68051d63e855 100644 --- a/libs/hwui/tests/common/scenes/OpPropAnimation.cpp +++ b/libs/hwui/tests/common/scenes/OpPropAnimation.cpp @@ -41,9 +41,9 @@ public: sp<CanvasPropertyPrimitive> mCircleRadius = new CanvasPropertyPrimitive(0); sp<RenderNode> content; - void createContent(int width, int height, TestCanvas& canvas) override { + void createContent(int width, int height, Canvas& canvas) override { content = TestUtils::createNode(0, 0, width, height, - [this, width, height](RenderProperties& props, TestCanvas& canvas) { + [this, width, height](RenderProperties& props, Canvas& canvas) { mPaint->value.setAntiAlias(true); mPaint->value.setColor(Color::Blue_500); @@ -53,7 +53,7 @@ public: mCircleX->value = width * 0.75; mCircleY->value = height * 0.75; - canvas.drawColor(Color::White, SkXfermode::Mode::kSrcOver_Mode); + canvas.drawColor(Color::White, SkBlendMode::kSrcOver); canvas.drawRoundRect(mRoundRectLeft.get(), mRoundRectTop.get(), mRoundRectRight.get(), mRoundRectBottom.get(), mRoundRectRx.get(), mRoundRectRy.get(), mPaint.get()); diff --git a/libs/hwui/tests/common/scenes/OvalAnimation.cpp b/libs/hwui/tests/common/scenes/OvalAnimation.cpp index e56f2f97007e..d6fd60494812 100644 --- a/libs/hwui/tests/common/scenes/OvalAnimation.cpp +++ b/libs/hwui/tests/common/scenes/OvalAnimation.cpp @@ -28,10 +28,10 @@ static TestScene::Registrar _Oval(TestScene::Info{ class OvalAnimation : public TestScene { public: sp<RenderNode> card; - void createContent(int width, int height, TestCanvas& canvas) override { - canvas.drawColor(Color::White, SkXfermode::kSrcOver_Mode); + void createContent(int width, int height, Canvas& canvas) override { + canvas.drawColor(Color::White, SkBlendMode::kSrcOver); card = TestUtils::createNode(0, 0, 200, 200, - [](RenderProperties& props, TestCanvas& canvas) { + [](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 84265a4774a5..bc04d81296df 100644 --- a/libs/hwui/tests/common/scenes/PartialDamageAnimation.cpp +++ b/libs/hwui/tests/common/scenes/PartialDamageAnimation.cpp @@ -29,7 +29,7 @@ static TestScene::Registrar _PartialDamage(TestScene::Info{ class PartialDamageAnimation : public TestScene { public: std::vector< sp<RenderNode> > cards; - void createContent(int width, int height, TestCanvas& canvas) override { + void createContent(int width, int height, Canvas& canvas) override { static SkColor COLORS[] = { 0xFFF44336, 0xFF9C27B0, @@ -37,15 +37,15 @@ public: 0xFF4CAF50, }; - canvas.drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode); + canvas.drawColor(0xFFFFFFFF, SkBlendMode::kSrcOver); 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, TestCanvas& canvas) { - canvas.drawColor(color, SkXfermode::kSrcOver_Mode); + [color](RenderProperties& props, Canvas& canvas) { + canvas.drawColor(color, SkBlendMode::kSrcOver); }); canvas.drawRenderNode(card.get()); cards.push_back(card); @@ -58,10 +58,10 @@ public: cards[0]->mutateStagingProperties().setTranslationY(curFrame); cards[0]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y); - TestUtils::recordNode(*cards[0], [curFrame](TestCanvas& canvas) { + TestUtils::recordNode(*cards[0], [curFrame](Canvas& canvas) { SkColor color = TestUtils::interpolateColor( curFrame / 150.0f, 0xFFF44336, 0xFFF8BBD0); - canvas.drawColor(color, SkXfermode::kSrcOver_Mode); + canvas.drawColor(color, SkBlendMode::kSrcOver); }); } }; diff --git a/libs/hwui/tests/common/scenes/ReadbackFromHardwareBitmap.cpp b/libs/hwui/tests/common/scenes/ReadbackFromHardwareBitmap.cpp new file mode 100644 index 000000000000..bc6fc6452e90 --- /dev/null +++ b/libs/hwui/tests/common/scenes/ReadbackFromHardwareBitmap.cpp @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2015 The Android Open 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" + +class ReadbackFromHardware; + +static TestScene::Registrar _SaveLayer(TestScene::Info{ + "readbackFromHBitmap", + "Allocates hardware bitmap and readback data from it.", + TestScene::simpleCreateScene<ReadbackFromHardware> +}); + +class ReadbackFromHardware : public TestScene { +public: + static sk_sp<Bitmap> createHardwareBitmap() { + SkBitmap skBitmap; + SkImageInfo info = SkImageInfo::Make(400, 400, kN32_SkColorType, kPremul_SkAlphaType); + skBitmap.allocPixels(info); + skBitmap.eraseColor(Color::Red_500); + SkCanvas canvas(skBitmap); + SkPaint paint; + paint.setColor(Color::Blue_500); + canvas.drawRect(SkRect::MakeXYWH(30, 30, 30, 150), paint); + canvas.drawRect(SkRect::MakeXYWH(30, 30, 100, 30), paint); + canvas.drawRect(SkRect::MakeXYWH(30, 100, 70, 30), paint); + return Bitmap::allocateHardwareBitmap(skBitmap); + } + + void createContent(int width, int height, Canvas& canvas) override { + canvas.drawColor(Color::White, SkBlendMode::kSrcOver); // background + + sk_sp<Bitmap> hardwareBitmap(createHardwareBitmap()); + + SkBitmap readback; + hardwareBitmap->getSkBitmap(&readback); + + SkBitmap canvasBitmap; + sk_sp<Bitmap> heapBitmap(TestUtils::createBitmap(hardwareBitmap->width(), + hardwareBitmap->height(), &canvasBitmap)); + + SkCanvas skCanvas(canvasBitmap); + skCanvas.drawBitmap(readback, 0, 0); + canvas.drawBitmap(*heapBitmap, 0, 0, nullptr); + + canvas.drawBitmap(*hardwareBitmap, 0, 500, nullptr); + } + + void doFrame(int frameNr) override { } +}; diff --git a/libs/hwui/tests/common/scenes/RecentsAnimation.cpp b/libs/hwui/tests/common/scenes/RecentsAnimation.cpp index 6509edd4077f..825602466f92 100644 --- a/libs/hwui/tests/common/scenes/RecentsAnimation.cpp +++ b/libs/hwui/tests/common/scenes/RecentsAnimation.cpp @@ -28,7 +28,7 @@ static TestScene::Registrar _Recents(TestScene::Info{ class RecentsAnimation : public TestScene { public: - void createContent(int width, int height, TestCanvas& renderer) override { + void createContent(int width, int height, Canvas& renderer) override { static SkColor COLORS[] = { Color::Red_500, Color::Purple_500, @@ -39,18 +39,20 @@ public: thumbnailSize = std::min(std::min(width, height) / 2, 720); int cardsize = std::min(width, height) - dp(64); - renderer.drawColor(Color::White, SkXfermode::kSrcOver_Mode); + renderer.drawColor(Color::White, SkBlendMode::kSrcOver); renderer.insertReorderBarrier(true); int x = dp(32); for (int i = 0; i < 4; i++) { int y = (height / 4) * i; - SkBitmap thumb = TestUtils::createSkBitmap(thumbnailSize, thumbnailSize); - thumb.eraseColor(COLORS[i]); - sp<RenderNode> card = createCard(x, y, cardsize, cardsize, thumb); + SkBitmap bitmap; + sk_sp<Bitmap> thumb(TestUtils::createBitmap(thumbnailSize, thumbnailSize, &bitmap)); + + bitmap.eraseColor(COLORS[i]); + sp<RenderNode> card = createCard(x, y, cardsize, cardsize, *thumb); card->mutateStagingProperties().setElevation(i * dp(8)); renderer.drawRenderNode(card.get()); - mThumbnail = thumb; + mThumbnail = bitmap; mCards.push_back(card); } @@ -68,15 +70,14 @@ public: } private: - sp<RenderNode> createCard(int x, int y, int width, int height, - const SkBitmap& thumb) { + sp<RenderNode> createCard(int x, int y, int width, int height, Bitmap& thumb) { return TestUtils::createNode(x, y, x + width, y + height, - [&thumb, width, height](RenderProperties& props, TestCanvas& canvas) { + [&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); - canvas.drawColor(Color::Grey_200, SkXfermode::kSrcOver_Mode); + canvas.drawColor(Color::Grey_200, SkBlendMode::kSrcOver); canvas.drawBitmap(thumb, 0, 0, thumb.width(), thumb.height(), 0, 0, width, height, nullptr); }); diff --git a/libs/hwui/tests/common/scenes/RectGridAnimation.cpp b/libs/hwui/tests/common/scenes/RectGridAnimation.cpp index a9293ab244dd..668eec69c2d0 100644 --- a/libs/hwui/tests/common/scenes/RectGridAnimation.cpp +++ b/libs/hwui/tests/common/scenes/RectGridAnimation.cpp @@ -29,13 +29,13 @@ static TestScene::Registrar _RectGrid(TestScene::Info{ class RectGridAnimation : public TestScene { public: sp<RenderNode> card; - void createContent(int width, int height, TestCanvas& canvas) override { - canvas.drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode); + void createContent(int width, int height, Canvas& canvas) override { + canvas.drawColor(0xFFFFFFFF, SkBlendMode::kSrcOver); canvas.insertReorderBarrier(true); card = TestUtils::createNode(50, 50, 250, 250, - [](RenderProperties& props, TestCanvas& canvas) { - canvas.drawColor(0xFFFF00FF, SkXfermode::kSrcOver_Mode); + [](RenderProperties& props, Canvas& canvas) { + canvas.drawColor(0xFFFF00FF, SkBlendMode::kSrcOver); SkRegion region; for (int xOffset = 0; xOffset < 200; xOffset+=2) { diff --git a/libs/hwui/tests/common/scenes/RoundRectClippingAnimation.cpp b/libs/hwui/tests/common/scenes/RoundRectClippingAnimation.cpp new file mode 100644 index 000000000000..4b6632d244f5 --- /dev/null +++ b/libs/hwui/tests/common/scenes/RoundRectClippingAnimation.cpp @@ -0,0 +1,77 @@ +/* + * 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 "TestSceneBase.h" + +#include <vector> + +class RoundRectClippingAnimation : public TestScene { +public: + int mSpacing, mSize; + + RoundRectClippingAnimation(int spacing, int size) + : mSpacing(spacing), mSize(size) {} + + std::vector< sp<RenderNode> > cards; + void createContent(int width, int height, Canvas& canvas) override { + canvas.drawColor(0xFFFFFFFF, SkBlendMode::kSrcOver); + canvas.insertReorderBarrier(true); + int ci = 0; + + 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); + }); + canvas.drawRenderNode(card.get()); + cards.push_back(card); + } + } + + canvas.insertReorderBarrier(false); + } + void doFrame(int frameNr) override { + int curFrame = frameNr % 50; + if (curFrame > 25) curFrame = 50 - curFrame; + for (auto& card : cards) { + card->mutateStagingProperties().setTranslationX(curFrame); + card->mutateStagingProperties().setTranslationY(curFrame); + card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y); + } + } +}; + +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)); + } +}); + +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)); + } +}); diff --git a/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp b/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp index 6904bec304e3..7e8a7d9d14f2 100644 --- a/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp +++ b/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp @@ -28,17 +28,17 @@ static TestScene::Registrar _SaveLayer(TestScene::Info{ class SaveLayerAnimation : public TestScene { public: sp<RenderNode> card; - void createContent(int width, int height, TestCanvas& canvas) override { - canvas.drawColor(Color::White, SkXfermode::kSrcOver_Mode); // background + void createContent(int width, int height, Canvas& canvas) override { + canvas.drawColor(Color::White, SkBlendMode::kSrcOver); // background card = TestUtils::createNode(0, 0, 400, 800, - [](RenderProperties& props, TestCanvas& canvas) { + [](RenderProperties& props, Canvas& canvas) { // nested clipped saveLayers canvas.saveLayerAlpha(0, 0, 400, 400, 200, SaveFlags::ClipToLayer); - canvas.drawColor(Color::Green_700, SkXfermode::kSrcOver_Mode); - canvas.clipRect(50, 50, 350, 350, SkRegion::kIntersect_Op); + canvas.drawColor(Color::Green_700, SkBlendMode::kSrcOver); + canvas.clipRect(50, 50, 350, 350, SkClipOp::kIntersect); canvas.saveLayerAlpha(100, 100, 300, 300, 128, SaveFlags::ClipToLayer); - canvas.drawColor(Color::Blue_500, SkXfermode::kSrcOver_Mode); + canvas.drawColor(Color::Blue_500, SkBlendMode::kSrcOver); canvas.restore(); canvas.restore(); diff --git a/libs/hwui/tests/common/scenes/ShadowGrid2Animation.cpp b/libs/hwui/tests/common/scenes/ShadowGrid2Animation.cpp index d3249b8f585a..0a69b62fe615 100644 --- a/libs/hwui/tests/common/scenes/ShadowGrid2Animation.cpp +++ b/libs/hwui/tests/common/scenes/ShadowGrid2Animation.cpp @@ -28,8 +28,8 @@ static TestScene::Registrar _ShadowGrid2(TestScene::Info{ class ShadowGrid2Animation : public TestScene { public: std::vector< sp<RenderNode> > cards; - void createContent(int width, int height, TestCanvas& canvas) override { - canvas.drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode); + void createContent(int width, int height, Canvas& canvas) override { + canvas.drawColor(0xFFFFFFFF, SkBlendMode::kSrcOver); canvas.insertReorderBarrier(true); for (int x = dp(8); x < (width - dp(58)); x += dp(58)) { @@ -53,11 +53,11 @@ public: 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, TestCanvas& canvas) { + [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, SkXfermode::kSrcOver_Mode); + canvas.drawColor(0xFFEEEEEE, SkBlendMode::kSrcOver); }); } }; diff --git a/libs/hwui/tests/common/scenes/ShadowGridAnimation.cpp b/libs/hwui/tests/common/scenes/ShadowGridAnimation.cpp index 5ffedf09bc70..4a024295cb25 100644 --- a/libs/hwui/tests/common/scenes/ShadowGridAnimation.cpp +++ b/libs/hwui/tests/common/scenes/ShadowGridAnimation.cpp @@ -28,8 +28,8 @@ static TestScene::Registrar _ShadowGrid(TestScene::Info{ class ShadowGridAnimation : public TestScene { public: std::vector< sp<RenderNode> > cards; - void createContent(int width, int height, TestCanvas& canvas) override { - canvas.drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode); + void createContent(int width, int height, Canvas& canvas) override { + canvas.drawColor(0xFFFFFFFF, SkBlendMode::kSrcOver); canvas.insertReorderBarrier(true); for (int x = dp(16); x < (width - dp(116)); x += dp(116)) { @@ -53,11 +53,11 @@ public: 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, TestCanvas& canvas) { + [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, SkXfermode::kSrcOver_Mode); + canvas.drawColor(0xFFEEEEEE, SkBlendMode::kSrcOver); }); } }; diff --git a/libs/hwui/tests/common/scenes/ShapeAnimation.cpp b/libs/hwui/tests/common/scenes/ShapeAnimation.cpp index 6d27c9d61a29..09e70ebf7b5f 100644 --- a/libs/hwui/tests/common/scenes/ShapeAnimation.cpp +++ b/libs/hwui/tests/common/scenes/ShapeAnimation.cpp @@ -30,17 +30,17 @@ static TestScene::Registrar _Shapes(TestScene::Info{ class ShapeAnimation : public TestScene { public: sp<RenderNode> card; - void createContent(int width, int height, TestCanvas& canvas) override { + void createContent(int width, int height, Canvas& canvas) override { card = TestUtils::createNode(0, 0, width, height, - [width](RenderProperties& props, TestCanvas& canvas) { - std::function<void(TestCanvas&, float, const SkPaint&)> ops[] = { - [](TestCanvas& canvas, float size, const SkPaint& paint) { + [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); }, - [](TestCanvas& canvas, float size, const SkPaint& paint) { + [](Canvas& canvas, float size, const SkPaint& paint) { canvas.drawOval(0, 0, size, size, paint); }, - [](TestCanvas& canvas, float size, const SkPaint& paint) { + [](Canvas& canvas, float size, const SkPaint& paint) { SkPath diamondPath; diamondPath.moveTo(size / 2, 0); diamondPath.lineTo(size, size / 2); @@ -49,18 +49,18 @@ public: diamondPath.close(); canvas.drawPath(diamondPath, paint); }, - [](TestCanvas& canvas, float size, const SkPaint& 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); }, - [](TestCanvas& canvas, float size, const SkPaint& 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); }, - [](TestCanvas& canvas, float size, const SkPaint& paint) { + [](Canvas& canvas, float size, const SkPaint& paint) { canvas.drawRect(0, 0, size, size, paint); }, - [](TestCanvas& canvas, float size, const SkPaint& paint) { + [](Canvas& canvas, float size, const SkPaint& paint) { float rad = size / 4; canvas.drawRoundRect(0, 0, size, size, rad, rad, paint); } @@ -82,8 +82,8 @@ public: int middleCount = canvas.save(SaveFlags::MatrixClip); for (auto op : ops) { int innerCount = canvas.save(SaveFlags::MatrixClip); - canvas.clipRect(0, 0, cellSize, cellSize, SkRegion::kIntersect_Op); - canvas.drawColor(Color::White, SkXfermode::Mode::kSrcOver_Mode); + 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); @@ -94,7 +94,7 @@ public: } canvas.restoreToCount(outerCount); }); - canvas.drawColor(Color::Grey_500, SkXfermode::Mode::kSrcOver_Mode); + canvas.drawColor(Color::Grey_500, SkBlendMode::kSrcOver); canvas.drawRenderNode(card.get()); } diff --git a/libs/hwui/tests/common/scenes/TestSceneBase.h b/libs/hwui/tests/common/scenes/TestSceneBase.h index 935ddcf9212d..792312a6a7a4 100644 --- a/libs/hwui/tests/common/scenes/TestSceneBase.h +++ b/libs/hwui/tests/common/scenes/TestSceneBase.h @@ -13,10 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#ifndef TESTS_SCENES_TESTSCENEBASE_H -#define TESTS_SCENES_TESTSCENEBASE_H -#include "DisplayListCanvas.h" +#pragma once + #include "RecordingCanvas.h" #include "RenderNode.h" #include "tests/common/TestContext.h" @@ -30,5 +29,3 @@ using namespace android; using namespace android::uirenderer; using namespace android::uirenderer::renderthread; using namespace android::uirenderer::test; - -#endif /* TESTS_SCENES_TESTSCENEBASE_H_ */ diff --git a/libs/hwui/tests/common/scenes/TextAnimation.cpp b/libs/hwui/tests/common/scenes/TextAnimation.cpp index be8f48b9fd17..438f877deb3e 100644 --- a/libs/hwui/tests/common/scenes/TextAnimation.cpp +++ b/libs/hwui/tests/common/scenes/TextAnimation.cpp @@ -28,10 +28,10 @@ static TestScene::Registrar _Text(TestScene::Info{ class TextAnimation : public TestScene { public: sp<RenderNode> card; - void createContent(int width, int height, TestCanvas& canvas) override { - canvas.drawColor(Color::White, SkXfermode::kSrcOver_Mode); + void createContent(int width, int height, Canvas& canvas) override { + canvas.drawColor(Color::White, SkBlendMode::kSrcOver); card = TestUtils::createNode(0, 0, width, height, - [](RenderProperties& props, TestCanvas& canvas) { + [](RenderProperties& props, Canvas& canvas) { SkPaint paint; paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); paint.setAntiAlias(true); diff --git a/libs/hwui/tests/macrobench/TestSceneRunner.cpp b/libs/hwui/tests/macrobench/TestSceneRunner.cpp index 6533c2eae305..396e896a7517 100644 --- a/libs/hwui/tests/macrobench/TestSceneRunner.cpp +++ b/libs/hwui/tests/macrobench/TestSceneRunner.cpp @@ -22,6 +22,7 @@ #include "renderthread/RenderProxy.h" #include "renderthread/RenderTask.h" +#include <benchmark/benchmark.h> #include <gui/Surface.h> #include <log/log.h> #include <ui/PixelFormat.h> @@ -41,7 +42,7 @@ public: template<class T> class ModifiedMovingAverage { public: - ModifiedMovingAverage(int weight) : mWeight(weight) {} + explicit ModifiedMovingAverage(int weight) : mWeight(weight) {} T add(T today) { if (!mHasValue) { @@ -62,13 +63,63 @@ private: T mAverage; }; -void run(const TestScene::Info& info, const TestScene::Options& opts) { +void outputBenchmarkReport(const TestScene::Info& info, const TestScene::Options& opts, + benchmark::BenchmarkReporter* reporter, RenderProxy* proxy, + double durationInS) { + using namespace benchmark; + + struct ReportInfo { + int percentile; + const char* suffix; + }; + + static std::array<ReportInfo, 4> REPORTS = { + 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 + // otherwise the BenchmarkReporter will automatically compute + // mean and stddev which doesn't make sense for our usage + std::vector<BenchmarkReporter::Run> reports; + BenchmarkReporter::Run report; + report.benchmark_name = info.name; + report.iterations = static_cast<int64_t>(opts.count); + report.real_accumulated_time = durationInS; + report.cpu_accumulated_time = durationInS; + report.items_per_second = opts.count / durationInS; + reports.push_back(report); + reporter->ReportRuns(reports); + + // Pretend the percentiles are single-iteration runs of the test + // If rendering offscreen skip this as it's fps that's more interesting + // in that test case than percentiles. + if (!opts.renderOffscreen) { + for (auto& ri : REPORTS) { + reports[0].benchmark_name = info.name; + reports[0].benchmark_name += ri.suffix; + durationInS = proxy->frameTimePercentile(ri.percentile) / 1000.0; + reports[0].real_accumulated_time = durationInS; + reports[0].cpu_accumulated_time = durationInS; + reports[0].iterations = 1; + reports[0].items_per_second = 0; + reporter->ReportRuns(reports); + } + } +} + +void run(const TestScene::Info& info, const TestScene::Options& opts, + 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); // create the native surface const int width = gDisplay.w; @@ -76,7 +127,7 @@ void run(const TestScene::Info& info, const TestScene::Options& opts) { sp<Surface> surface = testContext.surface(); sp<RenderNode> rootNode = TestUtils::createNode(0, 0, width, height, - [&scene, width, height](RenderProperties& props, TestCanvas& canvas) { + [&scene, width, height](RenderProperties& props, Canvas& canvas) { props.setClipToBounds(false); scene->createContent(width, height, canvas); }); @@ -87,11 +138,16 @@ void run(const TestScene::Info& info, const TestScene::Options& opts) { proxy->loadSystemProperties(); proxy->initialize(surface); float lightX = width / 2.0; - proxy->setup(width, height, dp(800.0f), 255 * 0.075, 255 * 0.15); + proxy->setup(dp(800.0f), 255 * 0.075, 255 * 0.15); proxy->setLightCenter((Vector3){lightX, dp(-200.0f), dp(800.0f)}); // Do a few cold runs then reset the stats so that the caches are all hot - for (int i = 0; i < 5; i++) { + int warmupFrameCount = 5; + if (opts.renderOffscreen) { + // Do a few more warmups to try and boost the clocks up + warmupFrameCount = 10; + } + for (int i = 0; i < warmupFrameCount; i++) { testContext.waitForVsync(); nsecs_t vsync = systemTime(CLOCK_MONOTONIC); UiFrameInfoBuilder(proxy->frameInfo()).setVsync(vsync, vsync); @@ -103,6 +159,7 @@ void run(const TestScene::Info& info, const TestScene::Options& opts) { ModifiedMovingAverage<double> avgMs(opts.reportFrametimeWeight); + nsecs_t start = systemTime(CLOCK_MONOTONIC); for (int i = 0; i < opts.count; i++) { testContext.waitForVsync(); nsecs_t vsync = systemTime(CLOCK_MONOTONIC); @@ -121,6 +178,13 @@ void run(const TestScene::Info& info, const TestScene::Options& opts) { } } } + proxy->fence(); + nsecs_t end = systemTime(CLOCK_MONOTONIC); - proxy->dumpProfileInfo(STDOUT_FILENO, DumpFlags::JankStats); + if (reporter) { + 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/how_to_run.txt b/libs/hwui/tests/macrobench/how_to_run.txt index b051768f3262..3c3d36a8977f 100644 --- a/libs/hwui/tests/macrobench/how_to_run.txt +++ b/libs/hwui/tests/macrobench/how_to_run.txt @@ -1,5 +1,5 @@ mmm -j8 frameworks/base/libs/hwui/ && - adb push $OUT/data/local/tmp/hwuitest /data/local/tmp/hwuitest && - adb shell /data/local/tmp/hwuitest +adb push $OUT/data/benchmarktest/hwuimacro/hwuimacro /data/benchmarktest/hwuimacro/hwuimacro && +adb shell /data/benchmarktest/hwuimacro/hwuimacro shadowgrid2 --onscreen Pass --help to get help diff --git a/libs/hwui/tests/macrobench/main.cpp b/libs/hwui/tests/macrobench/main.cpp index 02a39501e647..1f5622252f6a 100644 --- a/libs/hwui/tests/macrobench/main.cpp +++ b/libs/hwui/tests/macrobench/main.cpp @@ -14,11 +14,15 @@ * limitations under the License. */ +#include "tests/common/LeakChecker.h" #include "tests/common/TestScene.h" +#include "hwui/Typeface.h" #include "protos/hwui.pb.h" #include "Properties.h" +#include <benchmark/benchmark.h> +#include <../src/sysinfo.h> #include <getopt.h> #include <stdio.h> #include <string> @@ -39,12 +43,14 @@ using namespace android::uirenderer::test; static int gRepeatCount = 1; static std::vector<TestScene::Info> gRunTests; static TestScene::Options gOpts; +std::unique_ptr<benchmark::BenchmarkReporter> gBenchmarkReporter; -void run(const TestScene::Info& info, const TestScene::Options& opts); +void run(const TestScene::Info& info, const TestScene::Options& opts, + benchmark::BenchmarkReporter* reporter); static void printHelp() { printf(R"( -USAGE: hwuitest [OPTIONS] <TESTNAME> +USAGE: hwuimacro [OPTIONS] <TESTNAME> OPTIONS: -c, --count=NUM NUM loops a test should run (example, number of frames) @@ -58,6 +64,10 @@ OPTIONS: moving average frametime. Weight is optional, default is 10 --cpuset=name Adds the test to the specified cpuset before running Not supported on all devices and needs root + --offscreen Render tests off device screen. This option is on by default + --onscreen Render tests on device screen. By default tests + are offscreen rendered + --benchmark_format Set output format. Possible values are tabular, json, csv )"); } @@ -121,6 +131,20 @@ static void moveToCpuSet(const char* cpusetName) { close(fd); } +static bool setBenchmarkFormat(const char* format) { + if (!strcmp(format, "tabular")) { + gBenchmarkReporter.reset(new benchmark::ConsoleReporter()); + } else if (!strcmp(format, "json")) { + gBenchmarkReporter.reset(new benchmark::JSONReporter()); + } else if (!strcmp(format, "csv")) { + gBenchmarkReporter.reset(new benchmark::CSVReporter()); + } else { + fprintf(stderr, "Unknown format '%s'", format); + 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 { @@ -130,6 +154,9 @@ enum { WaitForGpu, ReportFrametime, CpuSet, + BenchmarkFormat, + Onscreen, + Offscreen, }; } @@ -141,6 +168,9 @@ static const struct option LONG_OPTIONS[] = { { "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 } }; @@ -214,6 +244,24 @@ void parseOptions(int argc, char* argv[]) { moveToCpuSet(optarg); break; + case LongOpts::BenchmarkFormat: + if (!optarg) { + error = true; + break; + } + if (!setBenchmarkFormat(optarg)) { + error = true; + } + break; + + case LongOpts::Onscreen: + gOpts.renderOffscreen = false; + break; + + case LongOpts::Offscreen: + gOpts.renderOffscreen = true; + break; + case 'h': printHelp(); exit(EXIT_SUCCESS); @@ -246,7 +294,9 @@ void parseOptions(int argc, char* argv[]) { } } while (optind < argc); } else { - gRunTests.push_back(TestScene::testMap()["shadowgrid"]); + for (auto& iter : TestScene::testMap()) { + gRunTests.push_back(iter.second); + } } } @@ -254,13 +304,39 @@ int main(int argc, char* argv[]) { // set defaults gOpts.count = 150; + Typeface::setRobotoTypefaceForTest(); + parseOptions(argc, argv); + if (!gBenchmarkReporter && gOpts.renderOffscreen) { + gBenchmarkReporter.reset(new benchmark::ConsoleReporter()); + } + + if (gBenchmarkReporter) { + size_t name_field_width = 10; + for (auto&& test : gRunTests) { + name_field_width = std::max<size_t>(name_field_width, test.name.size()); + } + // _50th, _90th, etc... + name_field_width += 5; + + benchmark::BenchmarkReporter::Context context; + context.num_cpus = benchmark::NumCPUs(); + context.mhz_per_cpu = benchmark::CyclesPerSecond() / 1000000.0f; + context.cpu_scaling_enabled = benchmark::CpuScalingEnabled(); + context.name_field_width = name_field_width; + gBenchmarkReporter->ReportContext(context); + } for (int i = 0; i < gRepeatCount; i++) { for (auto&& test : gRunTests) { - run(test, gOpts); + run(test, gOpts, gBenchmarkReporter.get()); } } - printf("Success!\n"); + + if (gBenchmarkReporter) { + gBenchmarkReporter->Finalize(); + } + + LeakChecker::checkForLeaks(); return 0; } diff --git a/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp b/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp index 06b68d1dea8f..f166c5ceb974 100644 --- a/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp +++ b/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp @@ -17,22 +17,12 @@ #include <benchmark/benchmark.h> #include "DisplayList.h" -#if HWUI_NEW_OPS #include "RecordingCanvas.h" -#else -#include "DisplayListCanvas.h" -#endif #include "tests/common/TestUtils.h" using namespace android; using namespace android::uirenderer; -#if HWUI_NEW_OPS -typedef RecordingCanvas TestCanvas; -#else -typedef DisplayListCanvas TestCanvas; -#endif - void BM_DisplayList_alloc(benchmark::State& benchState) { while (benchState.KeepRunning()) { auto displayList = new DisplayList(); @@ -52,42 +42,42 @@ void BM_DisplayList_alloc_theoretical(benchmark::State& benchState) { BENCHMARK(BM_DisplayList_alloc_theoretical); void BM_DisplayListCanvas_record_empty(benchmark::State& benchState) { - TestCanvas canvas(100, 100); - delete canvas.finishRecording(); + std::unique_ptr<Canvas> canvas(Canvas::create_recording_canvas(100, 100)); + delete canvas->finishRecording(); while (benchState.KeepRunning()) { - canvas.resetRecording(100, 100); - benchmark::DoNotOptimize(&canvas); - delete canvas.finishRecording(); + canvas->resetRecording(100, 100); + benchmark::DoNotOptimize(canvas.get()); + delete canvas->finishRecording(); } } BENCHMARK(BM_DisplayListCanvas_record_empty); void BM_DisplayListCanvas_record_saverestore(benchmark::State& benchState) { - TestCanvas canvas(100, 100); - delete canvas.finishRecording(); + std::unique_ptr<Canvas> canvas(Canvas::create_recording_canvas(100, 100)); + delete canvas->finishRecording(); while (benchState.KeepRunning()) { - canvas.resetRecording(100, 100); - canvas.save(SaveFlags::MatrixClip); - canvas.save(SaveFlags::MatrixClip); - benchmark::DoNotOptimize(&canvas); - canvas.restore(); - canvas.restore(); - delete canvas.finishRecording(); + canvas->resetRecording(100, 100); + canvas->save(SaveFlags::MatrixClip); + canvas->save(SaveFlags::MatrixClip); + benchmark::DoNotOptimize(canvas.get()); + canvas->restore(); + canvas->restore(); + delete canvas->finishRecording(); } } BENCHMARK(BM_DisplayListCanvas_record_saverestore); void BM_DisplayListCanvas_record_translate(benchmark::State& benchState) { - TestCanvas canvas(100, 100); - delete canvas.finishRecording(); + std::unique_ptr<Canvas> canvas(Canvas::create_recording_canvas(100, 100)); + delete canvas->finishRecording(); while (benchState.KeepRunning()) { - canvas.resetRecording(100, 100); - canvas.scale(10, 10); - benchmark::DoNotOptimize(&canvas); - delete canvas.finishRecording(); + canvas->resetRecording(100, 100); + canvas->scale(10, 10); + benchmark::DoNotOptimize(canvas.get()); + delete canvas->finishRecording(); } } BENCHMARK(BM_DisplayListCanvas_record_translate); @@ -99,27 +89,27 @@ BENCHMARK(BM_DisplayListCanvas_record_translate); * View system frequently produces unneeded save/restores. */ void BM_DisplayListCanvas_record_simpleBitmapView(benchmark::State& benchState) { - TestCanvas canvas(100, 100); - delete canvas.finishRecording(); + std::unique_ptr<Canvas> canvas(Canvas::create_recording_canvas(100, 100)); + delete canvas->finishRecording(); SkPaint rectPaint; - SkBitmap iconBitmap = TestUtils::createSkBitmap(80, 80); + sk_sp<Bitmap> iconBitmap(TestUtils::createBitmap(80, 80)); while (benchState.KeepRunning()) { - canvas.resetRecording(100, 100); + canvas->resetRecording(100, 100); { - canvas.save(SaveFlags::MatrixClip); - canvas.drawRect(0, 0, 100, 100, rectPaint); - canvas.restore(); + canvas->save(SaveFlags::MatrixClip); + canvas->drawRect(0, 0, 100, 100, rectPaint); + canvas->restore(); } { - canvas.save(SaveFlags::MatrixClip); - canvas.translate(10, 10); - canvas.drawBitmap(iconBitmap, 0, 0, nullptr); - canvas.restore(); + canvas->save(SaveFlags::MatrixClip); + canvas->translate(10, 10); + canvas->drawBitmap(*iconBitmap, 0, 0, nullptr); + canvas->restore(); } - benchmark::DoNotOptimize(&canvas); - delete canvas.finishRecording(); + benchmark::DoNotOptimize(canvas.get()); + delete canvas->finishRecording(); } } BENCHMARK(BM_DisplayListCanvas_record_simpleBitmapView); @@ -169,3 +159,37 @@ 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) { + canvas.drawColor(0xFFFFFFFF, SkBlendMode::kSrcOver); + }); + + std::unique_ptr<Canvas> canvas(Canvas::create_recording_canvas(100, 100)); + delete canvas->finishRecording(); + + while (benchState.KeepRunning()) { + canvas->resetRecording(200, 200); + canvas->setHighContrastText(false); + canvas->translate(0, 0); // mScrollX, mScrollY + + // Clip to padding + // Can expect ~25% of views to have clip to padding with a non-null padding + int clipRestoreCount = canvas->save(SaveFlags::MatrixClip); + canvas->clipRect(1, 1, 199, 199, SkClipOp::kIntersect); + + canvas->insertReorderBarrier(true); + + // Draw child loop + for (int i = 0; i < benchState.range(0); i++) { + canvas->drawRenderNode(child.get()); + } + + canvas->insertReorderBarrier(false); + canvas->restoreToCount(clipRestoreCount); + + delete canvas->finishRecording(); + } +} +BENCHMARK(BM_DisplayListCanvas_basicViewGroupDraw)->Arg(1)->Arg(5)->Arg(10); diff --git a/libs/hwui/tests/microbench/FrameBuilderBench.cpp b/libs/hwui/tests/microbench/FrameBuilderBench.cpp index 362890b52b61..398e7a89be1e 100644 --- a/libs/hwui/tests/microbench/FrameBuilderBench.cpp +++ b/libs/hwui/tests/microbench/FrameBuilderBench.cpp @@ -39,9 +39,9 @@ const FrameBuilder::LightGeometry sLightGeometry = { {100, 100, 100}, 50}; const BakedOpRenderer::LightInfo sLightInfo = { 128, 128 }; static sp<RenderNode> createTestNode() { - auto node = TestUtils::createNode(0, 0, 200, 200, + auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200, [](RenderProperties& props, RecordingCanvas& canvas) { - SkBitmap bitmap = TestUtils::createSkBitmap(10, 10); + sk_sp<Bitmap> bitmap(TestUtils::createBitmap(10, 10)); SkPaint paint; // Alternate between drawing rects and bitmaps, with bitmaps overlapping rects. @@ -50,7 +50,7 @@ static sp<RenderNode> createTestNode() { 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.drawBitmap(*bitmap, 5, 0, nullptr); } canvas.restore(); }); @@ -98,8 +98,8 @@ static sp<RenderNode> getSyncedSceneNode(const char* sceneName) { TestScene::Options opts; std::unique_ptr<TestScene> scene(TestScene::testMap()[sceneName].createScene(opts)); - sp<RenderNode> rootNode = TestUtils::createNode(0, 0, gDisplay.w, gDisplay.h, - [&scene](RenderProperties& props, TestCanvas& 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); }); diff --git a/libs/hwui/tests/microbench/RenderNodeBench.cpp b/libs/hwui/tests/microbench/RenderNodeBench.cpp new file mode 100644 index 000000000000..a5bed0026b1c --- /dev/null +++ b/libs/hwui/tests/microbench/RenderNodeBench.cpp @@ -0,0 +1,33 @@ +/* + * 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 <benchmark/benchmark.h> + +#include "RenderNode.h" + +using namespace android; +using namespace android::uirenderer; + +void BM_RenderNode_create(benchmark::State& state) { + while (state.KeepRunning()) { + auto node = new RenderNode(); + node->incStrong(0); + benchmark::DoNotOptimize(node); + node->decStrong(0); + } +} +BENCHMARK(BM_RenderNode_create); + diff --git a/libs/hwui/tests/microbench/TaskManagerBench.cpp b/libs/hwui/tests/microbench/TaskManagerBench.cpp index c6b9f3bca55f..cf47f273c144 100644 --- a/libs/hwui/tests/microbench/TaskManagerBench.cpp +++ b/libs/hwui/tests/microbench/TaskManagerBench.cpp @@ -29,7 +29,7 @@ class TrivialTask : public Task<char> {}; class TrivialProcessor : public TaskProcessor<char> { public: - TrivialProcessor(TaskManager* manager) + explicit TrivialProcessor(TaskManager* manager) : TaskProcessor(manager) {} virtual ~TrivialProcessor() {} virtual void onProcess(const sp<Task<char> >& task) override { diff --git a/libs/hwui/tests/microbench/how_to_run.txt b/libs/hwui/tests/microbench/how_to_run.txt index e6f80b278276..915fe5d959f9 100755 --- a/libs/hwui/tests/microbench/how_to_run.txt +++ b/libs/hwui/tests/microbench/how_to_run.txt @@ -1,4 +1,3 @@ mmm -j8 frameworks/base/libs/hwui && -adb push $ANDROID_PRODUCT_OUT/data/local/tmp/hwuimicro \ - /data/local/tmp/hwuimicro && - adb shell /data/local/tmp/hwuimicro +adb push $OUT/data/benchmarktest/hwuimicro/hwuimicro /data/benchmarktest/hwuimicro/hwuimicro && +adb shell /data/benchmarktest/hwuimicro/hwuimicro diff --git a/libs/hwui/tests/microbench/main.cpp b/libs/hwui/tests/microbench/main.cpp index a0157bc4f9ef..b5abf5bc5efa 100644 --- a/libs/hwui/tests/microbench/main.cpp +++ b/libs/hwui/tests/microbench/main.cpp @@ -14,6 +14,22 @@ * limitations under the License. */ +#include "debug/GlesDriver.h" +#include "debug/NullGlesDriver.h" + +#include "hwui/Typeface.h" + #include <benchmark/benchmark.h> -BENCHMARK_MAIN(); +#include <memory> + +using namespace android; +using namespace android::uirenderer; + +int main(int argc, char** argv) { + debug::GlesDriver::replace(std::make_unique<debug::NullGlesDriver>()); + benchmark::Initialize(&argc, argv); + Typeface::setRobotoTypefaceForTest(); + benchmark::RunSpecifiedBenchmarks(); + return 0; +} diff --git a/libs/hwui/tests/scripts/prep_buller.sh b/libs/hwui/tests/scripts/prep_buller.sh new file mode 100755 index 000000000000..65292c3a8b9c --- /dev/null +++ b/libs/hwui/tests/scripts/prep_buller.sh @@ -0,0 +1,64 @@ +#buller is bullhead & angler (☞゚ヮ゚)☞ + +nr=$(adb shell cat /proc/cpuinfo | grep processor | wc -l) +cpubase=/sys/devices/system/cpu +gov=cpufreq/scaling_governor + +adb root +adb wait-for-device +adb shell stop thermal-engine +adb shell stop perfd + +# LITTLE cores +# 384000 460800 600000 672000 787200 864000 960000 1248000 1440000 +# BIG cores +# 384000 480000 633600 768000 864000 960000 1248000 1344000 1440000 +# 1536000 1632000 1689600 1824000 + +cpu=0 +S=960000 +while [ $((cpu < 4)) -eq 1 ]; do + echo "Setting cpu $cpu to $S hz" + adb shell "echo 1 > $cpubase/cpu${cpu}/online" + adb shell "echo userspace > $cpubase/cpu${cpu}/$gov" + adb shell "echo $S > $cpubase/cpu${cpu}/cpufreq/scaling_max_freq" + adb shell "echo $S > $cpubase/cpu${cpu}/cpufreq/scaling_min_freq" + adb shell "echo $S > $cpubase/cpu${cpu}/cpufreq/scaling_setspeed" + 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" + +# angler: 0 762 1144 1525 2288 3509 4173 5271 5928 7904 9887 11863 +adb shell "echo 11863 > /sys/class/devfreq/qcom,gpubw.70/min_freq" &> /dev/null +# bullhead: 0 762 1144 1525 2288 3509 4173 5271 5928 7102 +adb shell "echo 7102 > /sys/class/devfreq/qcom,gpubw.19/min_freq" &> /dev/null + + +board=$(adb shell "getprop ro.product.board") +freq=0 +if [ "$board" = "bullhead" ] +then + #600000000 490000000 450000000 367000000 300000000 180000000 + freq=300000000 +else + #600000000 510000000 450000000 390000000 305000000 180000000 + freq=305000000 +fi +echo "performance mode, $freq Hz" +adb shell "echo performance > /sys/class/kgsl/kgsl-3d0/devfreq/governor" +adb shell "echo $freq > /sys/class/kgsl/kgsl-3d0/devfreq/min_freq" +adb shell "echo $freq > /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/prep_fugu.sh b/libs/hwui/tests/scripts/prep_fugu.sh new file mode 100755 index 000000000000..04a3203c49a7 --- /dev/null +++ b/libs/hwui/tests/scripts/prep_fugu.sh @@ -0,0 +1,48 @@ +#!/bin/bash + +cpubase=/sys/devices/system/cpu +gov=cpufreq/scaling_governor + +adb root +adb wait-for-device +thermal=$(adb shell "getprop persist.service.thermal") +echo "thermal status: $thermal" +if [ $thermal -eq 1 ] +then + echo "Trying to setprop persist.service.thermal 0 and reboot" + adb shell "setprop persist.service.thermal 0" + adb reboot + adb wait-for-device + thermal=$(adb shell "getprop persist.service.thermal") + if [ $thermal -eq 1 ] + then + echo "thermal property is still 1. Abort." + exit -1 + fi + echo "Successfully setup persist.service.thermal to 0" +fi + +adb shell stop perfprod + +# cores +# 1833000 1750000 1666000 1583000 1500000 1416000 1333000 1250000 +# 1166000 1083000 1000000 916000 833000 750000 666000 583000 500000 + +cpu=0 +S=1166000 +while [ $((cpu < 3)) -eq 1 ]; do + echo "Setting cpu ${cpu} & $(($cpu + 1)) cluster to $S hz" + # cpu0/online doesn't exist, because you can't turned it off, so ignore results of this command + adb shell "echo 1 > $cpubase/cpu${cpu}/online" &> /dev/null + adb shell "echo userspace > $cpubase/cpu${cpu}/$gov" + adb shell "echo $S > $cpubase/cpu${cpu}/cpufreq/scaling_max_freq" + adb shell "echo $S > $cpubase/cpu${cpu}/cpufreq/scaling_min_freq" + adb shell "echo $S > $cpubase/cpu${cpu}/cpufreq/scaling_setspeed" + cpu=$(($cpu + 2)) +done + +#/sys/class/devfreq/dfrgx/available_frequencies is empty, so set to min +echo "performance mode, 457 MHz" +adb shell "echo performance > /sys/class/devfreq/dfrgx/governor" +adb shell "echo 457000 > /sys/class/devfreq/dfrgx/min_freq" +adb shell "echo 457000 > /sys/class/devfreq/dfrgx/max_freq" diff --git a/libs/hwui/tests/scripts/prep_marlfish.sh b/libs/hwui/tests/scripts/prep_marlfish.sh new file mode 100755 index 000000000000..f3c14d52fad5 --- /dev/null +++ b/libs/hwui/tests/scripts/prep_marlfish.sh @@ -0,0 +1,45 @@ +#marlfish is marlin & sailfish (☞゚ヮ゚)☞ + +cpubase=/sys/devices/system/cpu + +adb root +adb wait-for-device +adb shell stop thermal-engine +adb shell stop perfd + +# silver cores +#307200 384000 460800 537600 614400 691200 768000 844800 902400 979200 +#1056000 1132800 1209600 1286400 1363200 1440000 1516800 1593600 +# gold cores +#307200 384000 460800 537600 614400 691200 748800 825600 902400 979200 +#1056000 1132800 1209600 1286400 1363200 1440000 1516800 1593600 1670400 +#1747200 1824000 1900800 1977600 2054400 2150400 + +S=979200 +cpu=0 +# Changing governor and frequency in one core will be automatically applied +# to other cores in the cluster +while [ $((cpu < 3)) -eq 1 ]; do + adb shell "echo userspace > $cpubase/cpu2/cpufreq/scaling_governor" + echo "Setting cpu ${cpu} & $(($cpu + 1)) cluster to $S hz" + adb shell "echo $S > $cpubase/cpu${cpu}/cpufreq/scaling_max_freq" + adb shell "echo $S > $cpubase/cpu${cpu}/cpufreq/scaling_min_freq" + cpu=$(($cpu + 2)) +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 13763 > /sys/class/devfreq/soc:qcom,gpubw/min_freq" &> /dev/null + +#133000000 214000000 315000000 401800000 510000000 560000000 624000000 +echo "performance mode, 315 MHz" +adb shell "echo performance > /sys/class/kgsl/kgsl-3d0/devfreq/governor" +adb shell "echo 315000000 > /sys/class/kgsl/kgsl-3d0/devfreq/min_freq" +adb shell "echo 315000000 > /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/prep_ryu.sh b/libs/hwui/tests/scripts/prep_ryu.sh new file mode 100644 index 000000000000..7c6c0df8d58e --- /dev/null +++ b/libs/hwui/tests/scripts/prep_ryu.sh @@ -0,0 +1,31 @@ +adb root +adb wait-for-device +adb shell stop thermal-engine +adb shell stop perfd + +# 51000 102000 204000 306000 408000 510000 612000 714000 816000 918000 +# 1020000 1122000 1224000 1326000 1428000 1530000 1632000 1734000 1836000 1912500 +S=1326000 +echo "set cpu to $S hz"; +adb shell "echo userspace > /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor" +adb shell "echo $S > /sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq" +adb shell "echo $S > /sys/devices/system/cpu/cpu0/cpufreq/scaling_min_freq" + +#01: core 76 MHz emc 408 MHz +#02: core 153 MHz emc 665 MHz +#03: core 230 MHz emc 800 MHz * +#04: core 307 MHz emc 1065 MHz +#05: core 384 MHz emc 1331 MHz +#06: core 460 MHz emc 1600 MHz +#07: core 537 MHz emc 1600 MHz +#08: core 614 MHz emc 1600 MHz +#09: core 691 MHz emc 1600 MHz +#0a: core 768 MHz emc 1600 MHz +#0b: core 844 MHz emc 1600 MHz +#0c: core 921 MHz emc 1600 MHz +#0d: core 998 MHz emc 1600 MHz +#AC: core 230 MHz emc 800 MHz a A d D + +echo "set gpu to core 307 MHz emc 1065 MHz" +# it will lock gpu until you touch a screen +adb shell "echo 04 > /sys/devices/57000000.gpu/pstate" diff --git a/libs/hwui/tests/scripts/prep_volantis.sh b/libs/hwui/tests/scripts/prep_volantis.sh index 0572ee55c9b9..6407844afa90 100755 --- a/libs/hwui/tests/scripts/prep_volantis.sh +++ b/libs/hwui/tests/scripts/prep_volantis.sh @@ -16,16 +16,8 @@ adb root adb wait-for-device -adb shell stop mpdecision adb shell stop perfd -adb shell stop -for pid in $( adb shell ps | awk '{ if ( $9 == "surfaceflinger" ) { print $2 } }' ); do - adb shell kill $pid -done -adb shell setprop debug.egl.traceGpuCompletion 1 -adb shell daemonize surfaceflinger -sleep 3 -adb shell setprop service.bootanim.exit 1 +adb shell stop thermal-engine # cpu possible frequencies # 204000 229500 255000 280500 306000 331500 357000 382500 408000 433500 459000 diff --git a/libs/hwui/tests/scripts/stopruntime.sh b/libs/hwui/tests/scripts/stopruntime.sh new file mode 100755 index 000000000000..0a796183eaf9 --- /dev/null +++ b/libs/hwui/tests/scripts/stopruntime.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +# 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. + +adb root +adb wait-for-device +adb shell stop + +for pid in $( adb shell ps | awk '{ if ( $9 == "surfaceflinger" ) { print $2 } }' ); do + adb shell kill $pid +done +adb shell setprop debug.egl.traceGpuCompletion 1 +adb shell setprop debug.sf.nobootanimation 1 +# Daemonize command is available only in eng builds. +adb shell daemonize surfaceflinger diff --git a/libs/hwui/tests/unit/BakedOpDispatcherTests.cpp b/libs/hwui/tests/unit/BakedOpDispatcherTests.cpp index 6b7b721eaec6..9a3b81cc0138 100644 --- a/libs/hwui/tests/unit/BakedOpDispatcherTests.cpp +++ b/libs/hwui/tests/unit/BakedOpDispatcherTests.cpp @@ -27,6 +27,7 @@ #include <SkBlurDrawLooper.h> #include <SkDashPathEffect.h> +#include <SkPath.h> using namespace android::uirenderer; @@ -79,15 +80,13 @@ static void testUnmergedGlopDispatch(renderthread::RenderThread& renderThread, R << "Glop(s) expected"; } -RENDERTHREAD_TEST(BakedOpDispatcher, pathTexture_positionOvalArc) { +RENDERTHREAD_OPENGL_PIPELINE_TEST(BakedOpDispatcher, pathTexture_positionOvalArc) { SkPaint strokePaint; strokePaint.setStyle(SkPaint::kStroke_Style); strokePaint.setStrokeWidth(4); float intervals[] = {1.0f, 1.0f}; - auto dashEffect = SkDashPathEffect::Create(intervals, 2, 0); - strokePaint.setPathEffect(dashEffect); - dashEffect->unref(); + strokePaint.setPathEffect(SkDashPathEffect::Make(intervals, 2, 0)); auto textureGlopVerifier = [] (const Glop& glop) { // validate glop produced by renderPathTexture (so texture, unit quad) @@ -114,7 +113,7 @@ RENDERTHREAD_TEST(BakedOpDispatcher, pathTexture_positionOvalArc) { testUnmergedGlopDispatch(renderThread, &ovalOp, textureGlopVerifier); } -RENDERTHREAD_TEST(BakedOpDispatcher, onLayerOp_bufferless) { +RENDERTHREAD_OPENGL_PIPELINE_TEST(BakedOpDispatcher, onLayerOp_bufferless) { SkPaint layerPaint; layerPaint.setAlpha(128); OffscreenBuffer* buffer = nullptr; // no providing a buffer, should hit rect fallback case @@ -132,7 +131,7 @@ static int getGlopTransformFlags(renderthread::RenderThread& renderThread, Recor return result; } -RENDERTHREAD_TEST(BakedOpDispatcher, offsetFlags) { +RENDERTHREAD_OPENGL_PIPELINE_TEST(BakedOpDispatcher, offsetFlags) { Rect bounds(10, 15, 20, 25); SkPaint paint; SkPaint aaPaint; @@ -158,15 +157,15 @@ RENDERTHREAD_TEST(BakedOpDispatcher, offsetFlags) { << "Expect an offset for non-AA lines."; } -RENDERTHREAD_TEST(BakedOpDispatcher, renderTextWithShadow) { - auto node = TestUtils::createNode(0, 0, 100, 100, - [](RenderProperties& props, TestCanvas& canvas) { +RENDERTHREAD_OPENGL_PIPELINE_TEST(BakedOpDispatcher, renderTextWithShadow) { + auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100, + [](RenderProperties& props, RecordingCanvas& canvas) { android::Paint shadowPaint; shadowPaint.setColor(SK_ColorRED); SkScalar sigma = Blur::convertRadiusToSigma(5); - shadowPaint.setLooper(SkBlurDrawLooper::Create(SK_ColorWHITE, sigma, 3, 3))->unref(); + shadowPaint.setLooper(SkBlurDrawLooper::Make(SK_ColorWHITE, sigma, 3, 3)); TestUtils::drawUtf8ToCanvas(&canvas, "A", shadowPaint, 25, 25); TestUtils::drawUtf8ToCanvas(&canvas, "B", shadowPaint, 50, 50); @@ -196,13 +195,13 @@ RENDERTHREAD_TEST(BakedOpDispatcher, renderTextWithShadow) { static void validateLayerDraw(renderthread::RenderThread& renderThread, std::function<void(const Glop& glop)> validator) { - auto node = TestUtils::createNode(0, 0, 100, 100, - [](RenderProperties& props, TestCanvas& canvas) { + 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(SkXfermode::Mode::kSrc_Mode); - canvas.drawColor(Color::Black, SkXfermode::Mode::kSrcOver_Mode); + props.mutateLayerProperties().setXferMode(SkBlendMode::kSrc); + canvas.drawColor(Color::Black, SkBlendMode::kSrcOver); }); OffscreenBuffer** layerHandle = node->getLayerHandle(); @@ -233,7 +232,7 @@ static FloatColor makeFloatColor(uint32_t color) { return c; } -RENDERTHREAD_TEST(BakedOpDispatcher, layerUpdateProperties) { +RENDERTHREAD_OPENGL_PIPELINE_TEST(BakedOpDispatcher, layerUpdateProperties) { for (bool debugOverdraw : { false, true }) { for (bool debugLayersUpdates : { false, true }) { ScopedProperty<bool> ovdProp(Properties::debugOverdraw, debugOverdraw); @@ -273,3 +272,17 @@ RENDERTHREAD_TEST(BakedOpDispatcher, layerUpdateProperties) { } } } + +RENDERTHREAD_OPENGL_PIPELINE_TEST(BakedOpDispatcher, pathTextureSnapping) { + Rect bounds(10, 15, 20, 25); + SkPaint paint; + 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) { + auto texture = glop.fill.texture.texture; + ASSERT_NE(nullptr, texture); + EXPECT_EQ(1, reinterpret_cast<PathTexture*>(texture)->left); + EXPECT_EQ(3, reinterpret_cast<PathTexture*>(texture)->top); + }); +} diff --git a/libs/hwui/tests/unit/BakedOpRendererTests.cpp b/libs/hwui/tests/unit/BakedOpRendererTests.cpp index 59bd75ef6f62..380062a36d45 100644 --- a/libs/hwui/tests/unit/BakedOpRendererTests.cpp +++ b/libs/hwui/tests/unit/BakedOpRendererTests.cpp @@ -23,7 +23,7 @@ using namespace android::uirenderer; const BakedOpRenderer::LightInfo sLightInfo = { 128, 128 }; -RENDERTHREAD_TEST(BakedOpRenderer, startRepaintLayer_clear) { +RENDERTHREAD_OPENGL_PIPELINE_TEST(BakedOpRenderer, startRepaintLayer_clear) { BakedOpRenderer renderer(Caches::getInstance(), renderThread.renderState(), true, sLightInfo); OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 200u, 200u); diff --git a/libs/hwui/tests/unit/BitmapTests.cpp b/libs/hwui/tests/unit/BitmapTests.cpp new file mode 100644 index 000000000000..8c7e08183a1e --- /dev/null +++ b/libs/hwui/tests/unit/BitmapTests.cpp @@ -0,0 +1,44 @@ +/* + * 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 <gtest/gtest.h> + +#include "hwui/Bitmap.h" + +#include <SkBitmap.h> +#include <SkColorTable.h> +#include <SkImageInfo.h> + +#include <tests/common/TestUtils.h> + +using namespace android; +using namespace android::uirenderer; + +TEST(Bitmap, colorTableRefCounting) { + const SkPMColor c[] = { SkPackARGB32(0x80, 0x80, 0, 0) }; + SkColorTable* ctable = new SkColorTable(c, SK_ARRAY_COUNT(c)); + + SkBitmap* bm = new SkBitmap(); + bm->allocPixels(SkImageInfo::Make(1, 1, kIndex_8_SkColorType, kPremul_SkAlphaType), + nullptr, ctable); + sk_sp<Bitmap> bitmap = Bitmap::allocateHeapBitmap(bm, ctable); + EXPECT_FALSE(ctable->unique()); + delete bm; + bitmap.reset(); + EXPECT_TRUE(ctable->unique()); + ctable->unref(); +} + diff --git a/libs/hwui/tests/unit/CanvasContextTests.cpp b/libs/hwui/tests/unit/CanvasContextTests.cpp new file mode 100644 index 000000000000..42ba3dbd2362 --- /dev/null +++ b/libs/hwui/tests/unit/CanvasContextTests.cpp @@ -0,0 +1,55 @@ +/* + * 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 <gtest/gtest.h> + +#include "AnimationContext.h" +#include "IContextFactory.h" +#include "renderthread/CanvasContext.h" +#include "tests/common/TestUtils.h" + +using namespace android; +using namespace android::uirenderer; +using namespace android::uirenderer::renderthread; + +class ContextFactory : public IContextFactory { +public: + virtual AnimationContext* createAnimationContext(renderthread::TimeLord& clock) override { + return new AnimationContext(clock); + } +}; + +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)); + + ASSERT_FALSE(canvasContext->hasSurface()); + + canvasContext->destroy(nullptr); +} + +RENDERTHREAD_TEST(CanvasContext, invokeFunctor) { + TestUtils::MockFunctor functor; + CanvasContext::invokeFunctor(renderThread, &functor); + if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) { + // we currently don't support OpenGL WebViews on the Vulkan backend + ASSERT_EQ(functor.getLastMode(), DrawGlInfo::kModeProcessNoContext); + } else { + ASSERT_EQ(functor.getLastMode(), DrawGlInfo::kModeProcess); + } +} diff --git a/libs/hwui/tests/unit/CanvasStateTests.cpp b/libs/hwui/tests/unit/CanvasStateTests.cpp index 0afabd83f5cc..43974f650084 100644 --- a/libs/hwui/tests/unit/CanvasStateTests.cpp +++ b/libs/hwui/tests/unit/CanvasStateTests.cpp @@ -23,7 +23,7 @@ #include <gtest/gtest.h> #include <SkPath.h> -#include <SkRegion.h> +#include <SkClipOp.h> namespace android { namespace uirenderer { @@ -68,13 +68,13 @@ TEST(CanvasState, simpleClipping) { state.initializeSaveStack(200, 200, 0, 0, 200, 200, Vector3()); - state.clipRect(0, 0, 100, 100, SkRegion::kIntersect_Op); + state.clipRect(0, 0, 100, 100, SkClipOp::kIntersect); ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(100, 100)); - state.clipRect(10, 10, 200, 200, SkRegion::kIntersect_Op); + state.clipRect(10, 10, 200, 200, SkClipOp::kIntersect); ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(10, 10, 100, 100)); - state.clipRect(50, 50, 150, 150, SkRegion::kReplace_Op); + state.clipRect(50, 50, 150, 150, SkClipOp::kReplace); ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(50, 50, 150, 150)); } @@ -88,7 +88,7 @@ TEST(CanvasState, complexClipping) { // rotated clip causes complex clip state.rotate(10); EXPECT_TRUE(state.clipIsSimple()); - state.clipRect(0, 0, 200, 200, SkRegion::kIntersect_Op); + state.clipRect(0, 0, 200, 200, SkClipOp::kIntersect); EXPECT_FALSE(state.clipIsSimple()); } state.restore(); @@ -97,7 +97,7 @@ TEST(CanvasState, complexClipping) { { // subtracted clip causes complex clip EXPECT_TRUE(state.clipIsSimple()); - state.clipRect(50, 50, 150, 150, SkRegion::kDifference_Op); + state.clipRect(50, 50, 150, 150, SkClipOp::kDifference); EXPECT_FALSE(state.clipIsSimple()); } state.restore(); @@ -108,7 +108,7 @@ TEST(CanvasState, complexClipping) { SkPath path; path.addOval(SkRect::MakeWH(200, 200)); EXPECT_TRUE(state.clipIsSimple()); - state.clipPath(&path, SkRegion::kDifference_Op); + state.clipPath(&path, SkClipOp::kDifference); EXPECT_FALSE(state.clipIsSimple()); } state.restore(); @@ -121,7 +121,7 @@ TEST(CanvasState, saveAndRestore) { state.save(SaveFlags::Clip); { - state.clipRect(0, 0, 10, 10, SkRegion::kIntersect_Op); + state.clipRect(0, 0, 10, 10, SkClipOp::kIntersect); ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(10, 10)); } state.restore(); @@ -145,7 +145,7 @@ TEST(CanvasState, saveAndRestoreButNotTooMuch) { state.save(SaveFlags::Matrix); // NOTE: clip not saved { - state.clipRect(0, 0, 10, 10, SkRegion::kIntersect_Op); + state.clipRect(0, 0, 10, 10, SkClipOp::kIntersect); ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(10, 10)); } state.restore(); diff --git a/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp b/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp new file mode 100644 index 000000000000..1ef9dba07c6a --- /dev/null +++ b/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp @@ -0,0 +1,59 @@ +/* + * 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 "DeferredLayerUpdater.h" +#include "GlLayer.h" +#include "Properties.h" + +#include "tests/common/TestUtils.h" + +#include <gtest/gtest.h> + +using namespace android; +using namespace android::uirenderer; + +RENDERTHREAD_TEST(DeferredLayerUpdater, updateLayer) { + sp<DeferredLayerUpdater> layerUpdater = TestUtils::createTextureLayerUpdater(renderThread); + layerUpdater->setSize(100, 100); + layerUpdater->setBlend(true); + + // updates are deferred so the backing layer should still be in its default state + if (layerUpdater->backingLayer()->getApi() == Layer::Api::OpenGL) { + GlLayer* glLayer = static_cast<GlLayer*>(layerUpdater->backingLayer()); + EXPECT_EQ((uint32_t)GL_NONE, glLayer->getRenderTarget()); + } + EXPECT_EQ(0u, layerUpdater->backingLayer()->getWidth()); + EXPECT_EQ(0u, layerUpdater->backingLayer()->getHeight()); + EXPECT_FALSE(layerUpdater->backingLayer()->getForceFilter()); + EXPECT_FALSE(layerUpdater->backingLayer()->isBlend()); + EXPECT_EQ(Matrix4::identity(), layerUpdater->backingLayer()->getTexTransform()); + + // push the deferred updates to the layer + Matrix4 scaledMatrix; + scaledMatrix.loadScale(0.5, 0.5, 0.0); + layerUpdater->updateLayer(true, GL_TEXTURE_EXTERNAL_OES, scaledMatrix.data); + + // the backing layer should now have all the properties applied. + if (layerUpdater->backingLayer()->getApi() == Layer::Api::OpenGL) { + GlLayer* glLayer = static_cast<GlLayer*>(layerUpdater->backingLayer()); + EXPECT_EQ((uint32_t)GL_TEXTURE_EXTERNAL_OES, glLayer->getRenderTarget()); + } + EXPECT_EQ(100u, layerUpdater->backingLayer()->getWidth()); + EXPECT_EQ(100u, layerUpdater->backingLayer()->getHeight()); + EXPECT_TRUE(layerUpdater->backingLayer()->getForceFilter()); + EXPECT_TRUE(layerUpdater->backingLayer()->isBlend()); + EXPECT_EQ(scaledMatrix, layerUpdater->backingLayer()->getTexTransform()); +} diff --git a/libs/hwui/tests/unit/DeviceInfoTests.cpp b/libs/hwui/tests/unit/DeviceInfoTests.cpp index 17236bdf0bf7..af37938915e5 100644 --- a/libs/hwui/tests/unit/DeviceInfoTests.cpp +++ b/libs/hwui/tests/unit/DeviceInfoTests.cpp @@ -17,11 +17,12 @@ #include <DeviceInfo.h> #include <gtest/gtest.h> +#include "tests/common/TestUtils.h" using namespace android; using namespace android::uirenderer; -TEST(DeviceInfo, basic) { +OPENGL_PIPELINE_TEST(DeviceInfo, basic) { // can't assert state before init - another test may have initialized the singleton DeviceInfo::initialize(); const DeviceInfo* di = DeviceInfo::get(); diff --git a/libs/hwui/tests/unit/FatalTestCanvas.h b/libs/hwui/tests/unit/FatalTestCanvas.h new file mode 100644 index 000000000000..4831722b93e6 --- /dev/null +++ b/libs/hwui/tests/unit/FatalTestCanvas.h @@ -0,0 +1,143 @@ +/* + * 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. + */ + +#pragma once + +#include <gtest/gtest.h> +#include <SkCanvas.h> + +namespace { + +class TestCanvasBase : public SkCanvas { +public: + 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"; + } + void onDrawDRRect(const SkRRect&, const SkRRect&, const SkPaint&) { + ADD_FAILURE() << "onDrawDRRect not expected in this test"; + } + void onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y, + 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) { + ADD_FAILURE() << "onDrawPosText not expected in this test"; + } + void onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[], SkScalar constY, + 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) { + ADD_FAILURE() << "onDrawTextOnPath not expected in this test"; + } + void onDrawTextRSXform(const void* text, size_t byteLength, const SkRSXform[], + 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) { + ADD_FAILURE() << "onDrawPatch 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"; + } + void onDrawRegion(const SkRegion& region, const SkPaint& paint) { + ADD_FAILURE() << "onDrawRegion not expected in this test"; + } + void onDrawOval(const SkRect&, const SkPaint&) { + ADD_FAILURE() << "onDrawOval not expected in this test"; + } + void onDrawArc(const SkRect&, SkScalar startAngle, SkScalar sweepAngle, bool useCenter, + const SkPaint&) { + ADD_FAILURE() << "onDrawArc not expected in this test"; + } + void onDrawRRect(const SkRRect&, const SkPaint&) { + ADD_FAILURE() << "onDrawRRect not expected in this test"; + } + void onDrawPoints(PointMode, size_t count, const SkPoint pts[], const SkPaint&) { + ADD_FAILURE() << "onDrawPoints not expected in this test"; + } + void onDrawVertices(VertexMode, int vertexCount, const SkPoint vertices[], const SkPoint texs[], + const SkColor colors[], SkBlendMode, const uint16_t indices[], int indexCount, + const SkPaint&) { + 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*) { + ADD_FAILURE() << "onDrawAtlas not expected in this test"; + } + void onDrawPath(const SkPath&, const SkPaint&) { + ADD_FAILURE() << "onDrawPath not expected in this test"; + } + void onDrawImage(const SkImage*, SkScalar dx, SkScalar dy, const SkPaint*) { + ADD_FAILURE() << "onDrawImage not expected in this test"; + } + void onDrawImageRect(const SkImage*, const SkRect*, const SkRect&, const SkPaint*, + 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*) { + 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) { + ADD_FAILURE() << "onDrawBitmapRect not expected in this test"; + } + void onDrawBitmapNine(const SkBitmap&, const SkIRect& center, const SkRect& dst, + const SkPaint*) { + ADD_FAILURE() << "onDrawBitmapNine not expected in this test"; + } + void onDrawBitmapLattice(const SkBitmap&, const Lattice& lattice, const SkRect& dst, + const SkPaint*) { + ADD_FAILURE() << "onDrawBitmapLattice not expected in this test"; + } + void onClipRRect(const SkRRect& rrect, SkClipOp, ClipEdgeStyle) { + ADD_FAILURE() << "onClipRRect not expected in this test"; + } + void onClipPath(const SkPath& path, SkClipOp, ClipEdgeStyle) { + ADD_FAILURE() << "onClipPath not expected in this test"; + } + 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 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 +}; + +}
\ No newline at end of file diff --git a/libs/hwui/tests/unit/FontRendererTests.cpp b/libs/hwui/tests/unit/FontRendererTests.cpp index 99080ac938e7..ee202367d73e 100644 --- a/libs/hwui/tests/unit/FontRendererTests.cpp +++ b/libs/hwui/tests/unit/FontRendererTests.cpp @@ -28,7 +28,7 @@ static bool isZero(uint8_t* data, int size) { return true; } -RENDERTHREAD_TEST(FontRenderer, renderDropShadow) { +RENDERTHREAD_OPENGL_PIPELINE_TEST(FontRenderer, renderDropShadow) { SkPaint paint; paint.setTextSize(10); paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); diff --git a/libs/hwui/tests/unit/FrameBuilderTests.cpp b/libs/hwui/tests/unit/FrameBuilderTests.cpp index e2dc3a0a2c66..6f3ed9cf9e2f 100644 --- a/libs/hwui/tests/unit/FrameBuilderTests.cpp +++ b/libs/hwui/tests/unit/FrameBuilderTests.cpp @@ -19,6 +19,7 @@ #include <BakedOpState.h> #include <DeferredLayerUpdater.h> #include <FrameBuilder.h> +#include <GlLayer.h> #include <LayerUpdateQueue.h> #include <RecordedOp.h> #include <RecordingCanvas.h> @@ -108,7 +109,7 @@ public: class FailRenderer : public TestRendererBase {}; -RENDERTHREAD_TEST(FrameBuilder, simple) { +RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, simple) { class SimpleTestRenderer : public TestRendererBase { public: void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override { @@ -127,11 +128,11 @@ RENDERTHREAD_TEST(FrameBuilder, simple) { } }; - auto node = TestUtils::createNode(0, 0, 100, 200, + auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 200, [](RenderProperties& props, RecordingCanvas& canvas) { - SkBitmap bitmap = TestUtils::createSkBitmap(25, 25); + sk_sp<Bitmap> bitmap(TestUtils::createBitmap(25, 25)); canvas.drawRect(0, 0, 100, 200, SkPaint()); - canvas.drawBitmap(bitmap, 10, 10, nullptr); + canvas.drawBitmap(*bitmap, 10, 10, nullptr); }); FrameBuilder frameBuilder(SkRect::MakeWH(100, 200), 100, 200, sLightGeometry, Caches::getInstance()); @@ -142,7 +143,7 @@ RENDERTHREAD_TEST(FrameBuilder, simple) { EXPECT_EQ(4, renderer.getIndex()); // 2 ops + start + end } -RENDERTHREAD_TEST(FrameBuilder, simpleStroke) { +RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, simpleStroke) { class SimpleStrokeTestRenderer : public TestRendererBase { public: void onPointsOp(const PointsOp& op, const BakedOpState& state) override { @@ -155,7 +156,7 @@ RENDERTHREAD_TEST(FrameBuilder, simpleStroke) { } }; - auto node = TestUtils::createNode(0, 0, 100, 200, + auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 200, [](RenderProperties& props, RecordingCanvas& canvas) { SkPaint strokedPaint; strokedPaint.setStrokeWidth(10); @@ -170,11 +171,11 @@ RENDERTHREAD_TEST(FrameBuilder, simpleStroke) { EXPECT_EQ(1, renderer.getIndex()); } -RENDERTHREAD_TEST(FrameBuilder, simpleRejection) { - auto node = TestUtils::createNode(0, 0, 200, 200, +RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, simpleRejection) { + auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200, [](RenderProperties& props, RecordingCanvas& canvas) { canvas.save(SaveFlags::MatrixClip); - canvas.clipRect(200, 200, 400, 400, SkRegion::kIntersect_Op); // 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(); }); @@ -186,7 +187,7 @@ RENDERTHREAD_TEST(FrameBuilder, simpleRejection) { frameBuilder.replayBakedOps<TestDispatcher>(renderer); } -RENDERTHREAD_TEST(FrameBuilder, simpleBatching) { +RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, simpleBatching) { const int LOOPS = 5; class SimpleBatchingTestRenderer : public TestRendererBase { public: @@ -198,10 +199,11 @@ RENDERTHREAD_TEST(FrameBuilder, simpleBatching) { } }; - auto node = TestUtils::createNode(0, 0, 200, 200, + auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200, [](RenderProperties& props, RecordingCanvas& canvas) { - SkBitmap bitmap = TestUtils::createSkBitmap(10, 10, - kAlpha_8_SkColorType); // Disable merging by using alpha 8 bitmap + + 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. @@ -209,7 +211,7 @@ RENDERTHREAD_TEST(FrameBuilder, simpleBatching) { 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.drawBitmap(*bitmap, 5, 0, nullptr); } canvas.restore(); }); @@ -223,7 +225,7 @@ RENDERTHREAD_TEST(FrameBuilder, simpleBatching) { << "Expect number of ops = 2 * loop count"; } -RENDERTHREAD_TEST(FrameBuilder, deferRenderNode_translateClip) { +RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, deferRenderNode_translateClip) { class DeferRenderNodeTranslateClipTestRenderer : public TestRendererBase { public: void onRectOp(const RectOp& op, const BakedOpState& state) override { @@ -234,7 +236,7 @@ RENDERTHREAD_TEST(FrameBuilder, deferRenderNode_translateClip) { } }; - auto node = TestUtils::createNode(0, 0, 100, 100, + auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100, [](RenderProperties& props, RecordingCanvas& canvas) { canvas.drawRect(0, 0, 100, 100, SkPaint()); }); @@ -249,7 +251,7 @@ RENDERTHREAD_TEST(FrameBuilder, deferRenderNode_translateClip) { EXPECT_EQ(1, renderer.getIndex()); } -RENDERTHREAD_TEST(FrameBuilder, deferRenderNodeScene) { +RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, deferRenderNodeScene) { class DeferRenderNodeSceneTestRenderer : public TestRendererBase { public: void onRectOp(const RectOp& op, const BakedOpState& state) override { @@ -287,20 +289,20 @@ RENDERTHREAD_TEST(FrameBuilder, deferRenderNodeScene) { transparentPaint.setAlpha(128); // backdrop - nodes.push_back(TestUtils::createNode(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); })); // content Rect contentDrawBounds(150, 150, 650, 450); // 500x300 - nodes.push_back(TestUtils::createNode(0, 0, 800, 600, + 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(0, 0, 800, 600, + nodes.push_back(TestUtils::createNode<RecordingCanvas>(0, 0, 800, 600, [&transparentPaint](RenderProperties& props, RecordingCanvas& canvas) { canvas.drawRect(0, 0, 800, 200, transparentPaint); })); @@ -318,7 +320,7 @@ RENDERTHREAD_TEST(FrameBuilder, deferRenderNodeScene) { EXPECT_EQ(4, renderer.getIndex()); } -RENDERTHREAD_TEST(FrameBuilder, empty_noFbo0) { +RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, empty_noFbo0) { class EmptyNoFbo0TestRenderer : public TestRendererBase { public: void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override { @@ -336,7 +338,7 @@ RENDERTHREAD_TEST(FrameBuilder, empty_noFbo0) { frameBuilder.replayBakedOps<TestDispatcher>(renderer); } -RENDERTHREAD_TEST(FrameBuilder, empty_withFbo0) { +RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, empty_withFbo0) { class EmptyWithFbo0TestRenderer : public TestRendererBase { public: void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override { @@ -346,7 +348,7 @@ RENDERTHREAD_TEST(FrameBuilder, empty_withFbo0) { EXPECT_EQ(1, mIndex++); } }; - auto node = TestUtils::createNode(10, 10, 110, 110, + auto node = TestUtils::createNode<RecordingCanvas>(10, 10, 110, 110, [](RenderProperties& props, RecordingCanvas& canvas) { // no drawn content }); @@ -362,7 +364,7 @@ RENDERTHREAD_TEST(FrameBuilder, empty_withFbo0) { " but fbo0 update lifecycle should still be observed"; } -RENDERTHREAD_TEST(FrameBuilder, avoidOverdraw_rects) { +RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, avoidOverdraw_rects) { class AvoidOverdrawRectsTestRenderer : public TestRendererBase { public: void onRectOp(const RectOp& op, const BakedOpState& state) override { @@ -371,7 +373,7 @@ RENDERTHREAD_TEST(FrameBuilder, avoidOverdraw_rects) { << "Last rect should occlude others."; } }; - auto node = TestUtils::createNode(0, 0, 200, 200, + 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()); @@ -392,20 +394,20 @@ RENDERTHREAD_TEST(FrameBuilder, avoidOverdraw_rects) { EXPECT_EQ(1, renderer.getIndex()) << "Expect exactly one op"; } -RENDERTHREAD_TEST(FrameBuilder, avoidOverdraw_bitmaps) { - static SkBitmap opaqueBitmap = TestUtils::createSkBitmap(50, 50, - SkColorType::kRGB_565_SkColorType); - static SkBitmap transpBitmap = TestUtils::createSkBitmap(50, 50, - SkColorType::kAlpha_8_SkColorType); +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)); class AvoidOverdrawBitmapsTestRenderer : public TestRendererBase { public: void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override { switch(mIndex++) { case 0: - EXPECT_EQ(opaqueBitmap.pixelRef(), op.bitmap->pixelRef()); + EXPECT_EQ(opaqueBitmap.get(), op.bitmap); break; case 1: - EXPECT_EQ(transpBitmap.pixelRef(), op.bitmap->pixelRef()); + EXPECT_EQ(transpBitmap.get(), op.bitmap); break; default: ADD_FAILURE() << "Only two ops expected."; @@ -413,15 +415,15 @@ RENDERTHREAD_TEST(FrameBuilder, avoidOverdraw_bitmaps) { } }; - auto node = TestUtils::createNode(0, 0, 50, 50, + 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); + 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); + canvas.drawBitmap(*opaqueBitmap, 0, 0, nullptr); + canvas.drawBitmap(*transpBitmap, 0, 0, nullptr); }); FrameBuilder frameBuilder(SkRect::MakeWH(50, 50), 50, 50, sLightGeometry, Caches::getInstance()); @@ -435,7 +437,7 @@ RENDERTHREAD_TEST(FrameBuilder, avoidOverdraw_bitmaps) { EXPECT_EQ(2, renderer.getIndex()) << "Expect exactly two ops"; } -RENDERTHREAD_TEST(FrameBuilder, clippedMerging) { +RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, clippedMerging) { class ClippedMergingTestRenderer : public TestRendererBase { public: void onMergedBitmapOps(const MergedBakedOpList& opList) override { @@ -447,25 +449,25 @@ RENDERTHREAD_TEST(FrameBuilder, clippedMerging) { opList.clipSideFlags); } }; - auto node = TestUtils::createNode(0, 0, 100, 100, - [](RenderProperties& props, TestCanvas& canvas) { - SkBitmap bitmap = TestUtils::createSkBitmap(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, SkRegion::kReplace_Op); - canvas.drawBitmap(bitmap, 0, 40, nullptr); + canvas.clipRect(10, 0, 50, 100, SkClipOp::kReplace); + canvas.drawBitmap(*bitmap, 0, 40, nullptr); // top side clipped (to inset top half) - canvas.clipRect(0, 10, 100, 50, SkRegion::kReplace_Op); - canvas.drawBitmap(bitmap, 40, 0, nullptr); + canvas.clipRect(0, 10, 100, 50, SkClipOp::kReplace); + canvas.drawBitmap(*bitmap, 40, 0, nullptr); // right side clipped (to inset right half) - canvas.clipRect(50, 0, 90, 100, SkRegion::kReplace_Op); - canvas.drawBitmap(bitmap, 80, 40, nullptr); + canvas.clipRect(50, 0, 90, 100, SkClipOp::kReplace); + canvas.drawBitmap(*bitmap, 80, 40, nullptr); // bottom not clipped, just abutting (inset bottom half) - canvas.clipRect(0, 50, 100, 90, SkRegion::kReplace_Op); - canvas.drawBitmap(bitmap, 40, 70, nullptr); + canvas.clipRect(0, 50, 100, 90, SkClipOp::kReplace); + canvas.drawBitmap(*bitmap, 40, 70, nullptr); }); FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, @@ -477,17 +479,17 @@ RENDERTHREAD_TEST(FrameBuilder, clippedMerging) { EXPECT_EQ(4, renderer.getIndex()); } -RENDERTHREAD_TEST(FrameBuilder, regionClipStopsMerge) { +RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, regionClipStopsMerge) { class RegionClipStopsMergeTestRenderer : public TestRendererBase { public: void onTextOp(const TextOp& op, const BakedOpState& state) override { mIndex++; } }; - auto node = TestUtils::createNode(0, 0, 400, 400, - [](RenderProperties& props, TestCanvas& canvas) { + 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, SkRegion::kIntersect_Op); + canvas.clipPath(&path, SkClipOp::kIntersect); SkPaint paint; paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); paint.setAntiAlias(true); @@ -506,7 +508,7 @@ RENDERTHREAD_TEST(FrameBuilder, regionClipStopsMerge) { EXPECT_EQ(2, renderer.getIndex()); } -RENDERTHREAD_TEST(FrameBuilder, textMerging) { +RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, textMerging) { class TextMergingTestRenderer : public TestRendererBase { public: void onMergedTextOps(const MergedBakedOpList& opList) override { @@ -518,8 +520,8 @@ RENDERTHREAD_TEST(FrameBuilder, textMerging) { EXPECT_EQ(OpClipSideFlags::None, opList.states[1]->computedState.clipSideFlags); } }; - auto node = TestUtils::createNode(0, 0, 400, 400, - [](RenderProperties& props, TestCanvas& canvas) { + auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 400, 400, + [](RenderProperties& props, RecordingCanvas& canvas) { SkPaint paint; paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); paint.setAntiAlias(true); @@ -536,7 +538,7 @@ RENDERTHREAD_TEST(FrameBuilder, textMerging) { EXPECT_EQ(2, renderer.getIndex()) << "Expect 2 ops"; } -RENDERTHREAD_TEST(FrameBuilder, textStrikethrough) { +RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, textStrikethrough) { const int LOOPS = 5; class TextStrikethroughTestRenderer : public TestRendererBase { public: @@ -549,7 +551,7 @@ RENDERTHREAD_TEST(FrameBuilder, textStrikethrough) { EXPECT_EQ(5u, opList.count); } }; - auto node = TestUtils::createNode(0, 0, 200, 2000, + auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 2000, [](RenderProperties& props, RecordingCanvas& canvas) { SkPaint textPaint; textPaint.setAntiAlias(true); @@ -574,7 +576,7 @@ RENDERTHREAD_TEST(FrameBuilder, textStrikethrough) { static auto styles = { SkPaint::kFill_Style, SkPaint::kStroke_Style, SkPaint::kStrokeAndFill_Style }; -RENDERTHREAD_TEST(FrameBuilder, textStyle) { +RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, textStyle) { class TextStyleTestRenderer : public TestRendererBase { public: void onMergedTextOps(const MergedBakedOpList& opList) override { @@ -605,8 +607,8 @@ RENDERTHREAD_TEST(FrameBuilder, textStyle) { EXPECT_EQ(stroke, outsetFill); } }; - auto node = TestUtils::createNode(0, 0, 400, 400, - [](RenderProperties& props, TestCanvas& canvas) { + auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 400, 400, + [](RenderProperties& props, RecordingCanvas& canvas) { SkPaint paint; paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); paint.setAntiAlias(true); @@ -628,7 +630,7 @@ RENDERTHREAD_TEST(FrameBuilder, textStyle) { EXPECT_EQ(3, renderer.getIndex()) << "Expect 3 ops"; } -RENDERTHREAD_TEST(FrameBuilder, textureLayer_clipLocalMatrix) { +RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, textureLayer_clipLocalMatrix) { class TextureLayerClipLocalMatrixTestRenderer : public TestRendererBase { public: void onTextureLayerOp(const TextureLayerOp& op, const BakedOpState& state) override { @@ -645,10 +647,10 @@ RENDERTHREAD_TEST(FrameBuilder, textureLayer_clipLocalMatrix) { auto layerUpdater = TestUtils::createTextureLayerUpdater(renderThread, 100, 100, SkMatrix::MakeTrans(5, 5)); - auto node = TestUtils::createNode(0, 0, 200, 200, + auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200, [&layerUpdater](RenderProperties& props, RecordingCanvas& canvas) { canvas.save(SaveFlags::MatrixClip); - canvas.clipRect(50, 50, 150, 150, SkRegion::kIntersect_Op); + canvas.clipRect(50, 50, 150, 150, SkClipOp::kIntersect); canvas.drawLayer(layerUpdater.get()); canvas.restore(); }); @@ -662,7 +664,7 @@ RENDERTHREAD_TEST(FrameBuilder, textureLayer_clipLocalMatrix) { EXPECT_EQ(1, renderer.getIndex()); } -RENDERTHREAD_TEST(FrameBuilder, textureLayer_combineMatrices) { +RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, textureLayer_combineMatrices) { class TextureLayerCombineMatricesTestRenderer : public TestRendererBase { public: void onTextureLayerOp(const TextureLayerOp& op, const BakedOpState& state) override { @@ -677,7 +679,7 @@ RENDERTHREAD_TEST(FrameBuilder, textureLayer_combineMatrices) { auto layerUpdater = TestUtils::createTextureLayerUpdater(renderThread, 100, 100, SkMatrix::MakeTrans(5, 5)); - auto node = TestUtils::createNode(0, 0, 200, 200, + auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200, [&layerUpdater](RenderProperties& props, RecordingCanvas& canvas) { canvas.save(SaveFlags::MatrixClip); canvas.translate(30, 40); @@ -694,12 +696,15 @@ RENDERTHREAD_TEST(FrameBuilder, textureLayer_combineMatrices) { EXPECT_EQ(1, renderer.getIndex()); } -RENDERTHREAD_TEST(FrameBuilder, textureLayer_reject) { +RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, textureLayer_reject) { auto layerUpdater = TestUtils::createTextureLayerUpdater(renderThread, 100, 100, SkMatrix::MakeTrans(5, 5)); - layerUpdater->backingLayer()->setRenderTarget(GL_NONE); // Should be rejected + EXPECT_EQ(Layer::Api::OpenGL, layerUpdater->backingLayer()->getApi()); - auto node = TestUtils::createNode(0, 0, 200, 200, + GlLayer* glLayer = static_cast<GlLayer*>(layerUpdater->backingLayer()); + 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()); }); @@ -712,7 +717,7 @@ RENDERTHREAD_TEST(FrameBuilder, textureLayer_reject) { frameBuilder.replayBakedOps<TestDispatcher>(renderer); } -RENDERTHREAD_TEST(FrameBuilder, functor_reject) { +RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, functor_reject) { class FunctorTestRenderer : public TestRendererBase { public: void onFunctorOp(const FunctorOp& op, const BakedOpState& state) override { @@ -722,7 +727,7 @@ RENDERTHREAD_TEST(FrameBuilder, functor_reject) { Functor noopFunctor; // 1 million pixel tall view, scrolled down 80% - auto scrolledFunctorView = TestUtils::createNode(0, 0, 400, 1000000, + auto scrolledFunctorView = TestUtils::createNode<RecordingCanvas>(0, 0, 400, 1000000, [&noopFunctor](RenderProperties& props, RecordingCanvas& canvas) { canvas.translate(0, -800000); canvas.callDrawGLFunction(&noopFunctor, nullptr); @@ -737,7 +742,7 @@ RENDERTHREAD_TEST(FrameBuilder, functor_reject) { EXPECT_EQ(1, renderer.getIndex()) << "Functor should not be rejected"; } -RENDERTHREAD_TEST(FrameBuilder, deferColorOp_unbounded) { +RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, deferColorOp_unbounded) { class ColorTestRenderer : public TestRendererBase { public: void onColorOp(const ColorOp& op, const BakedOpState& state) override { @@ -747,10 +752,10 @@ RENDERTHREAD_TEST(FrameBuilder, deferColorOp_unbounded) { } }; - auto unclippedColorView = TestUtils::createNode(0, 0, 10, 10, + auto unclippedColorView = TestUtils::createNode<RecordingCanvas>(0, 0, 10, 10, [](RenderProperties& props, RecordingCanvas& canvas) { props.setClipToBounds(false); - canvas.drawColor(SK_ColorWHITE, SkXfermode::Mode::kSrcOver_Mode); + canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver); }); FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, @@ -762,7 +767,7 @@ RENDERTHREAD_TEST(FrameBuilder, deferColorOp_unbounded) { EXPECT_EQ(1, renderer.getIndex()) << "ColorOp should not be rejected"; } -TEST(FrameBuilder, renderNode) { +OPENGL_PIPELINE_TEST(FrameBuilder, renderNode) { class RenderNodeTestRenderer : public TestRendererBase { public: void onRectOp(const RectOp& op, const BakedOpState& state) override { @@ -781,14 +786,14 @@ TEST(FrameBuilder, renderNode) { } }; - auto child = TestUtils::createNode(10, 10, 110, 110, + 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(0, 0, 200, 200, + auto parent = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200, [&child](RenderProperties& props, RecordingCanvas& canvas) { SkPaint paint; paint.setColor(SK_ColorDKGRAY); @@ -809,7 +814,7 @@ TEST(FrameBuilder, renderNode) { EXPECT_EQ(2, renderer.getIndex()); } -RENDERTHREAD_TEST(FrameBuilder, clipped) { +RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, clipped) { class ClippedTestRenderer : public TestRendererBase { public: void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override { @@ -820,10 +825,10 @@ RENDERTHREAD_TEST(FrameBuilder, clipped) { } }; - auto node = TestUtils::createNode(0, 0, 200, 200, + auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200, [](RenderProperties& props, RecordingCanvas& canvas) { - SkBitmap bitmap = TestUtils::createSkBitmap(200, 200); - canvas.drawBitmap(bitmap, 0, 0, nullptr); + sk_sp<Bitmap> bitmap(TestUtils::createBitmap(200, 200)); + canvas.drawBitmap(*bitmap, 0, 0, nullptr); }); // clip to small area, should see in receiver @@ -835,7 +840,7 @@ RENDERTHREAD_TEST(FrameBuilder, clipped) { frameBuilder.replayBakedOps<TestDispatcher>(renderer); } -RENDERTHREAD_TEST(FrameBuilder, saveLayer_simple) { +RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, saveLayer_simple) { class SaveLayerSimpleTestRenderer : public TestRendererBase { public: OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override { @@ -869,7 +874,7 @@ RENDERTHREAD_TEST(FrameBuilder, saveLayer_simple) { } }; - auto node = TestUtils::createNode(0, 0, 200, 200, + 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()); @@ -885,7 +890,7 @@ RENDERTHREAD_TEST(FrameBuilder, saveLayer_simple) { EXPECT_EQ(5, renderer.getIndex()); } -RENDERTHREAD_TEST(FrameBuilder, saveLayer_nested) { +RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, saveLayer_nested) { /* saveLayer1 { rect1, saveLayer2 { rect2 } } will play back as: * - startTemporaryLayer2, rect2 endLayer2 * - startTemporaryLayer1, rect1, drawLayer2, endLayer1 @@ -945,7 +950,7 @@ RENDERTHREAD_TEST(FrameBuilder, saveLayer_nested) { } }; - auto node = TestUtils::createNode(0, 0, 800, 800, + auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 800, 800, [](RenderProperties& props, RecordingCanvas& canvas) { canvas.saveLayerAlpha(0, 0, 800, 800, 128, SaveFlags::ClipToLayer); { @@ -968,11 +973,11 @@ RENDERTHREAD_TEST(FrameBuilder, saveLayer_nested) { EXPECT_EQ(12, renderer.getIndex()); } -RENDERTHREAD_TEST(FrameBuilder, saveLayer_contentRejection) { - auto node = TestUtils::createNode(0, 0, 200, 200, +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, SkRegion::kIntersect_Op); + 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 @@ -991,7 +996,7 @@ RENDERTHREAD_TEST(FrameBuilder, saveLayer_contentRejection) { frameBuilder.replayBakedOps<TestDispatcher>(renderer); } -RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_simple) { +RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, saveLayerUnclipped_simple) { class SaveLayerUnclippedSimpleTestRenderer : public TestRendererBase { public: void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override { @@ -1003,7 +1008,7 @@ RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_simple) { void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override { EXPECT_EQ(1, mIndex++); ASSERT_NE(nullptr, op.paint); - ASSERT_EQ(SkXfermode::kClear_Mode, PaintUtils::getXfermodeDirect(op.paint)); + ASSERT_EQ(SkBlendMode::kClear, PaintUtils::getBlendModeDirect(op.paint)); } void onRectOp(const RectOp& op, const BakedOpState& state) override { EXPECT_EQ(2, mIndex++); @@ -1020,7 +1025,7 @@ RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_simple) { } }; - auto node = TestUtils::createNode(0, 0, 200, 200, + 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()); @@ -1036,7 +1041,7 @@ RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_simple) { EXPECT_EQ(4, renderer.getIndex()); } -RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_round) { +RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, saveLayerUnclipped_round) { class SaveLayerUnclippedRoundTestRenderer : public TestRendererBase { public: void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override { @@ -1053,7 +1058,7 @@ RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_round) { } }; - auto node = TestUtils::createNode(0, 0, 200, 200, + 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)); @@ -1070,7 +1075,7 @@ RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_round) { EXPECT_EQ(2, renderer.getIndex()); } -RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_mergedClears) { +RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, saveLayerUnclipped_mergedClears) { class SaveLayerUnclippedMergedClearsTestRenderer : public TestRendererBase { public: void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override { @@ -1105,7 +1110,7 @@ RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_mergedClears) { } }; - auto node = TestUtils::createNode(0, 0, 200, 200, + auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200, [](RenderProperties& props, RecordingCanvas& canvas) { int restoreTo = canvas.save(SaveFlags::MatrixClip); @@ -1128,7 +1133,7 @@ RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_mergedClears) { << "Expect 4 copyTos, 4 copyFroms, 1 clear SimpleRects, and 1 rect."; } -RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_clearClip) { +RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, saveLayerUnclipped_clearClip) { class SaveLayerUnclippedClearClipTestRenderer : public TestRendererBase { public: void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override { @@ -1137,7 +1142,7 @@ RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_clearClip) { void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override { EXPECT_EQ(1, mIndex++); ASSERT_NE(nullptr, op.paint); - EXPECT_EQ(SkXfermode::kClear_Mode, PaintUtils::getXfermodeDirect(op.paint)); + EXPECT_EQ(SkBlendMode::kClear, PaintUtils::getBlendModeDirect(op.paint)); EXPECT_EQ(Rect(50, 50, 150, 150), state.computedState.clippedBounds) << "Expect dirty rect as clip"; ASSERT_NE(nullptr, state.computedState.clipState); @@ -1152,7 +1157,7 @@ RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_clearClip) { } }; - auto node = TestUtils::createNode(0, 0, 200, 200, + 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)); @@ -1170,8 +1175,8 @@ RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_clearClip) { EXPECT_EQ(4, renderer.getIndex()); } -RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_reject) { - auto node = TestUtils::createNode(0, 0, 200, 200, +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)); @@ -1192,7 +1197,7 @@ RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_reject) { * - startTemporaryLayer, onCopyToLayer, onSimpleRects, onRect, onCopyFromLayer, endLayer * - startFrame, onCopyToLayer, onSimpleRects, drawLayer, onCopyFromLayer, endframe */ -RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_complex) { +RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, saveLayerUnclipped_complex) { class SaveLayerUnclippedComplexTestRenderer : public TestRendererBase { public: OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) { @@ -1237,7 +1242,7 @@ RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_complex) { } }; - auto node = TestUtils::createNode(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 @@ -1257,7 +1262,7 @@ RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_complex) { EXPECT_EQ(13, renderer.getIndex()); } -RENDERTHREAD_TEST(FrameBuilder, hwLayer_simple) { +RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, hwLayer_simple) { class HwLayerSimpleTestRenderer : public TestRendererBase { public: void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override { @@ -1289,7 +1294,7 @@ RENDERTHREAD_TEST(FrameBuilder, hwLayer_simple) { } }; - auto node = TestUtils::createNode(10, 10, 110, 110, + auto node = TestUtils::createNode<RecordingCanvas>(10, 10, 110, 110, [](RenderProperties& props, RecordingCanvas& canvas) { props.mutateLayerProperties().setType(LayerType::RenderLayer); SkPaint paint; @@ -1321,7 +1326,7 @@ RENDERTHREAD_TEST(FrameBuilder, hwLayer_simple) { *layerHandle = nullptr; } -RENDERTHREAD_TEST(FrameBuilder, hwLayer_complex) { +RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, hwLayer_complex) { /* parentLayer { greyRect, saveLayer { childLayer { whiteRect } } } will play back as: * - startRepaintLayer(child), rect(grey), endLayer * - startTemporaryLayer, drawLayer(child), endLayer @@ -1384,7 +1389,7 @@ RENDERTHREAD_TEST(FrameBuilder, hwLayer_complex) { } }; - auto child = TestUtils::createNode(50, 50, 150, 150, + auto child = TestUtils::createNode<RecordingCanvas>(50, 50, 150, 150, [](RenderProperties& props, RecordingCanvas& canvas) { props.mutateLayerProperties().setType(LayerType::RenderLayer); SkPaint paint; @@ -1395,7 +1400,7 @@ RENDERTHREAD_TEST(FrameBuilder, hwLayer_complex) { *(child->getLayerHandle()) = &childLayer; RenderNode* childPtr = child.get(); - auto parent = TestUtils::createNode(0, 0, 200, 200, + auto parent = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200, [childPtr](RenderProperties& props, RecordingCanvas& canvas) { props.mutateLayerProperties().setType(LayerType::RenderLayer); SkPaint paint; @@ -1430,7 +1435,7 @@ RENDERTHREAD_TEST(FrameBuilder, hwLayer_complex) { } -RENDERTHREAD_TEST(FrameBuilder, buildLayer) { +RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, buildLayer) { class BuildLayerTestRenderer : public TestRendererBase { public: void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override { @@ -1459,10 +1464,10 @@ RENDERTHREAD_TEST(FrameBuilder, buildLayer) { } }; - auto node = TestUtils::createNode(10, 10, 110, 110, + auto node = TestUtils::createNode<RecordingCanvas>(10, 10, 110, 110, [](RenderProperties& props, RecordingCanvas& canvas) { props.mutateLayerProperties().setType(LayerType::RenderLayer); - canvas.drawColor(SK_ColorWHITE, SkXfermode::Mode::kSrcOver_Mode); + canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver); }); OffscreenBuffer** layerHandle = node->getLayerHandle(); @@ -1486,14 +1491,16 @@ RENDERTHREAD_TEST(FrameBuilder, buildLayer) { *layerHandle = nullptr; } -static void drawOrderedRect(RecordingCanvas* canvas, uint8_t expectedDrawOrder) { +namespace { + +static void drawOrderedRect(Canvas* canvas, uint8_t expectedDrawOrder) { SkPaint paint; // order put in blue channel, transparent so overlapped content doesn't get rejected paint.setColor(SkColorSetARGB(1, 0, 0, expectedDrawOrder)); canvas->drawRect(0, 0, 100, 100, paint); } -static void drawOrderedNode(RecordingCanvas* canvas, uint8_t expectedDrawOrder, float z) { - auto node = TestUtils::createNode(0, 0, 100, 100, +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); }); @@ -1501,17 +1508,34 @@ static void drawOrderedNode(RecordingCanvas* canvas, uint8_t expectedDrawOrder, node->setPropertyFieldsDirty(RenderNode::TRANSLATION_Z); canvas->drawRenderNode(node.get()); // canvas takes reference/sole ownership } -RENDERTHREAD_TEST(FrameBuilder, zReorder) { - 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 - EXPECT_EQ(expectedOrder, mIndex++) << "An op was drawn out of order"; + +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, + [expectedDrawOrder, setup](RenderProperties& props, RecordingCanvas& canvas) { + 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 + EXPECT_EQ(expectedOrder, mIndex++) << "An op was drawn out of order"; + } +}; + +} // end anonymous namespace - auto parent = TestUtils::createNode(0, 0, 100, 100, +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); @@ -1524,6 +1548,14 @@ RENDERTHREAD_TEST(FrameBuilder, zReorder) { 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()); @@ -1531,10 +1563,10 @@ RENDERTHREAD_TEST(FrameBuilder, zReorder) { ZReorderTestRenderer renderer; frameBuilder.replayBakedOps<TestDispatcher>(renderer); - EXPECT_EQ(10, renderer.getIndex()); + EXPECT_EQ(13, renderer.getIndex()); }; -RENDERTHREAD_TEST(FrameBuilder, projectionReorder) { +RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorder) { static const int scrollX = 5; static const int scrollY = 10; class ProjectionReorderTestRenderer : public TestRendererBase { @@ -1579,7 +1611,7 @@ RENDERTHREAD_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(0, 0, 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 @@ -1591,7 +1623,7 @@ RENDERTHREAD_TEST(FrameBuilder, projectionReorder) { paint.setColor(SK_ColorWHITE); canvas.drawRect(0, 0, 100, 100, paint); }); - auto projectingRipple = TestUtils::createNode(50, 0, 100, 50, + auto projectingRipple = TestUtils::createNode<RecordingCanvas>(50, 0, 100, 50, [](RenderProperties& properties, RecordingCanvas& canvas) { properties.setProjectBackwards(true); properties.setClipToBounds(false); @@ -1599,14 +1631,14 @@ RENDERTHREAD_TEST(FrameBuilder, projectionReorder) { paint.setColor(SK_ColorDKGRAY); canvas.drawRect(-10, -10, 60, 60, paint); }); - auto child = TestUtils::createNode(0, 50, 100, 100, + 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(0, 0, 100, 100, + 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); @@ -1627,7 +1659,7 @@ RENDERTHREAD_TEST(FrameBuilder, projectionReorder) { EXPECT_EQ(3, renderer.getIndex()); } -RENDERTHREAD_TEST(FrameBuilder, projectionHwLayer) { +RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionHwLayer) { static const int scrollX = 5; static const int scrollY = 10; class ProjectionHwLayerTestRenderer : public TestRendererBase { @@ -1660,7 +1692,7 @@ RENDERTHREAD_TEST(FrameBuilder, projectionHwLayer) { ASSERT_EQ(nullptr, state.computedState.localProjectionPathMask); } }; - auto receiverBackground = TestUtils::createNode(0, 0, 400, 400, + 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 @@ -1670,19 +1702,19 @@ RENDERTHREAD_TEST(FrameBuilder, projectionHwLayer) { canvas.drawRect(0, 0, 400, 400, SkPaint()); }); - auto projectingRipple = TestUtils::createNode(0, 0, 200, 200, + 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(100, 100, 300, 300, + 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(0, 0, 400, 400, + 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); @@ -1718,7 +1750,7 @@ RENDERTHREAD_TEST(FrameBuilder, projectionHwLayer) { *layerHandle = nullptr; } -RENDERTHREAD_TEST(FrameBuilder, projectionChildScroll) { +RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionChildScroll) { static const int scrollX = 500000; static const int scrollY = 0; class ProjectionChildScrollTestRenderer : public TestRendererBase { @@ -1735,12 +1767,12 @@ RENDERTHREAD_TEST(FrameBuilder, projectionChildScroll) { EXPECT_TRUE(state.computedState.transform.isIdentity()); } }; - auto receiverBackground = TestUtils::createNode(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(0, 0, 200, 200, + 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! @@ -1750,15 +1782,15 @@ RENDERTHREAD_TEST(FrameBuilder, projectionChildScroll) { properties.setClipToBounds(false); canvas.drawOval(0, 0, 200, 200, SkPaint()); }); - auto child = TestUtils::createNode(0, 0, 400, 400, + 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, SkRegion::kIntersect_Op); + 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(0, 0, 400, 400, + auto parent = TestUtils::createNode<RecordingCanvas>(0, 0, 400, 400, [&receiverBackground, &child](RenderProperties& properties, RecordingCanvas& canvas) { canvas.drawRenderNode(receiverBackground.get()); canvas.drawRenderNode(child.get()); @@ -1775,7 +1807,7 @@ RENDERTHREAD_TEST(FrameBuilder, projectionChildScroll) { // creates a 100x100 shadow casting node with provided translationZ static sp<RenderNode> createWhiteRectShadowCaster(float translationZ) { - return TestUtils::createNode(0, 0, 100, 100, + 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); @@ -1785,7 +1817,7 @@ static sp<RenderNode> createWhiteRectShadowCaster(float translationZ) { }); } -RENDERTHREAD_TEST(FrameBuilder, shadow) { +RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, shadow) { class ShadowTestRenderer : public TestRendererBase { public: void onShadowOp(const ShadowOp& op, const BakedOpState& state) override { @@ -1803,7 +1835,7 @@ RENDERTHREAD_TEST(FrameBuilder, shadow) { } }; - auto parent = TestUtils::createNode(0, 0, 200, 200, + auto parent = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200, [](RenderProperties& props, RecordingCanvas& canvas) { canvas.insertReorderBarrier(true); canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get()); @@ -1818,7 +1850,7 @@ RENDERTHREAD_TEST(FrameBuilder, shadow) { EXPECT_EQ(2, renderer.getIndex()); } -RENDERTHREAD_TEST(FrameBuilder, shadowSaveLayer) { +RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, shadowSaveLayer) { class ShadowSaveLayerTestRenderer : public TestRendererBase { public: OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override { @@ -1844,7 +1876,7 @@ RENDERTHREAD_TEST(FrameBuilder, shadowSaveLayer) { } }; - auto parent = TestUtils::createNode(0, 0, 200, 200, + 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); @@ -1864,7 +1896,7 @@ RENDERTHREAD_TEST(FrameBuilder, shadowSaveLayer) { EXPECT_EQ(6, renderer.getIndex()); } -RENDERTHREAD_TEST(FrameBuilder, shadowHwLayer) { +RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, shadowHwLayer) { class ShadowHwLayerTestRenderer : public TestRendererBase { public: void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override { @@ -1887,7 +1919,7 @@ RENDERTHREAD_TEST(FrameBuilder, shadowHwLayer) { } }; - auto parent = TestUtils::createNode(50, 60, 150, 160, + auto parent = TestUtils::createNode<RecordingCanvas>(50, 60, 150, 160, [](RenderProperties& props, RecordingCanvas& canvas) { props.mutateLayerProperties().setType(LayerType::RenderLayer); canvas.insertReorderBarrier(true); @@ -1922,7 +1954,7 @@ RENDERTHREAD_TEST(FrameBuilder, shadowHwLayer) { *layerHandle = nullptr; } -RENDERTHREAD_TEST(FrameBuilder, shadowLayering) { +RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, shadowLayering) { class ShadowLayeringTestRenderer : public TestRendererBase { public: void onShadowOp(const ShadowOp& op, const BakedOpState& state) override { @@ -1934,7 +1966,7 @@ RENDERTHREAD_TEST(FrameBuilder, shadowLayering) { EXPECT_TRUE(index == 2 || index == 3); } }; - auto parent = TestUtils::createNode(0, 0, 200, 200, + auto parent = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200, [](RenderProperties& props, RecordingCanvas& canvas) { canvas.insertReorderBarrier(true); canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get()); @@ -1949,7 +1981,7 @@ RENDERTHREAD_TEST(FrameBuilder, shadowLayering) { EXPECT_EQ(4, renderer.getIndex()); } -RENDERTHREAD_TEST(FrameBuilder, shadowClipping) { +RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, shadowClipping) { class ShadowClippingTestRenderer : public TestRendererBase { public: void onShadowOp(const ShadowOp& op, const BakedOpState& state) override { @@ -1961,11 +1993,11 @@ RENDERTHREAD_TEST(FrameBuilder, shadowClipping) { EXPECT_EQ(1, mIndex++); } }; - auto parent = TestUtils::createNode(0, 0, 100, 100, + 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, SkRegion::kIntersect_Op); + canvas.clipRect(25, 25, 75, 75, SkClipOp::kIntersect); canvas.insertReorderBarrier(true); canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get()); }); @@ -1983,7 +2015,7 @@ static void testProperty(std::function<void(RenderProperties&)> propSetupCallbac std::function<void(const RectOp&, const BakedOpState&)> opValidateCallback) { class PropertyTestRenderer : public TestRendererBase { public: - 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); @@ -1992,7 +2024,7 @@ static void testProperty(std::function<void(RenderProperties&)> propSetupCallbac std::function<void(const RectOp&, const BakedOpState&)> mCallback; }; - auto node = TestUtils::createNode(0, 0, 100, 100, + auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100, [propSetupCallback](RenderProperties& props, RecordingCanvas& canvas) { propSetupCallback(props); SkPaint paint; @@ -2009,7 +2041,7 @@ static void testProperty(std::function<void(RenderProperties&)> propSetupCallbac EXPECT_EQ(1, renderer.getIndex()) << "Should have seen one op"; } -RENDERTHREAD_TEST(FrameBuilder, renderPropOverlappingRenderingAlpha) { +RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, renderPropOverlappingRenderingAlpha) { testProperty([](RenderProperties& properties) { properties.setAlpha(0.5f); properties.setHasOverlappingRendering(false); @@ -2018,7 +2050,7 @@ RENDERTHREAD_TEST(FrameBuilder, renderPropOverlappingRenderingAlpha) { }); } -RENDERTHREAD_TEST(FrameBuilder, renderPropClipping) { +RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, renderPropClipping) { testProperty([](RenderProperties& properties) { properties.setClipToBounds(true); properties.setClipBounds(Rect(10, 20, 300, 400)); @@ -2028,7 +2060,7 @@ RENDERTHREAD_TEST(FrameBuilder, renderPropClipping) { }); } -RENDERTHREAD_TEST(FrameBuilder, renderPropRevealClip) { +RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, renderPropRevealClip) { testProperty([](RenderProperties& properties) { properties.mutableRevealClip().set(true, 50, 50, 25); }, [](const RectOp& op, const BakedOpState& state) { @@ -2039,7 +2071,7 @@ RENDERTHREAD_TEST(FrameBuilder, renderPropRevealClip) { }); } -RENDERTHREAD_TEST(FrameBuilder, renderPropOutlineClip) { +RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, renderPropOutlineClip) { testProperty([](RenderProperties& properties) { properties.mutableOutline().setShouldClip(true); properties.mutableOutline().setRoundRect(10, 20, 30, 40, 5.0f, 0.5f); @@ -2051,7 +2083,7 @@ RENDERTHREAD_TEST(FrameBuilder, renderPropOutlineClip) { }); } -RENDERTHREAD_TEST(FrameBuilder, renderPropTransform) { +RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, renderPropTransform) { testProperty([](RenderProperties& properties) { properties.setLeftTopRightBottom(10, 10, 110, 110); @@ -2105,7 +2137,7 @@ void testSaveLayerAlphaClip(SaveLayerAlphaData* outObservedData, std::function<void(RenderProperties&)> propSetupCallback) { class SaveLayerAlphaClipTestRenderer : public TestRendererBase { public: - SaveLayerAlphaClipTestRenderer(SaveLayerAlphaData* outData) + explicit SaveLayerAlphaClipTestRenderer(SaveLayerAlphaData* outData) : mOutData(outData) {} OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override { @@ -2136,7 +2168,7 @@ void testSaveLayerAlphaClip(SaveLayerAlphaData* outObservedData, ASSERT_GT(10000, DeviceInfo::get()->maxTextureSize()) << "Node must be bigger than max texture size to exercise saveLayer codepath"; - auto node = TestUtils::createNode(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 @@ -2160,7 +2192,7 @@ void testSaveLayerAlphaClip(SaveLayerAlphaData* outObservedData, ASSERT_EQ(5, renderer.getIndex()) << "Test must trigger saveLayer alpha behavior."; } -RENDERTHREAD_TEST(FrameBuilder, renderPropSaveLayerAlphaClipBig) { +RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, renderPropSaveLayerAlphaClipBig) { SaveLayerAlphaData observedData; testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) { properties.setTranslationX(10); // offset rendering content @@ -2179,7 +2211,7 @@ RENDERTHREAD_TEST(FrameBuilder, renderPropSaveLayerAlphaClipBig) { << "expect drawLayer to be translated as part of being clipped"; } -RENDERTHREAD_TEST(FrameBuilder, renderPropSaveLayerAlphaRotate) { +RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, renderPropSaveLayerAlphaRotate) { SaveLayerAlphaData observedData; testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) { // Translate and rotate the view so that the only visible part is the top left corner of @@ -2198,7 +2230,7 @@ RENDERTHREAD_TEST(FrameBuilder, renderPropSaveLayerAlphaRotate) { EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), observedData.rectMatrix); } -RENDERTHREAD_TEST(FrameBuilder, renderPropSaveLayerAlphaScale) { +RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, renderPropSaveLayerAlphaScale) { SaveLayerAlphaData observedData; testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) { properties.setPivotX(0); @@ -2212,7 +2244,7 @@ RENDERTHREAD_TEST(FrameBuilder, renderPropSaveLayerAlphaScale) { EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), observedData.rectMatrix); } -RENDERTHREAD_TEST(FrameBuilder, clip_replace) { +RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, clip_replace) { class ClipReplaceTestRenderer : public TestRendererBase { public: void onColorOp(const ColorOp& op, const BakedOpState& state) override { @@ -2222,10 +2254,10 @@ RENDERTHREAD_TEST(FrameBuilder, clip_replace) { << "Expect resolved clip to be intersection of viewport clip and clip op"; } }; - auto node = TestUtils::createNode(20, 20, 30, 30, + auto node = TestUtils::createNode<RecordingCanvas>(20, 20, 30, 30, [](RenderProperties& props, RecordingCanvas& canvas) { - canvas.clipRect(0, -20, 10, 30, SkRegion::kReplace_Op); - canvas.drawColor(SK_ColorWHITE, SkXfermode::Mode::kSrcOver_Mode); + canvas.clipRect(0, -20, 10, 30, SkClipOp::kReplace); + canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver); }); FrameBuilder frameBuilder(SkRect::MakeLTRB(10, 10, 40, 40), 50, 50, @@ -2237,5 +2269,349 @@ RENDERTHREAD_TEST(FrameBuilder, clip_replace) { EXPECT_EQ(1, renderer.getIndex()); } +OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderProjectedInMiddle) { + /* R is backward projected on B + A + / \ + 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) { + 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; + frameBuilder.replayBakedOps<TestDispatcher>(renderer); + EXPECT_EQ(3, renderer.getIndex()); +} + +OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderProjectLast) { + /* R is backward projected on E + A + / | \ + / | \ + B C E + | + 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()); + frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA)); + + ZReorderTestRenderer renderer; + frameBuilder.replayBakedOps<TestDispatcher>(renderer); + EXPECT_EQ(4, renderer.getIndex()); +} + +OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderNoReceivable) { + /* R is backward projected without receiver + A + / \ + B C + | + 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()); + frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA)); + + ZReorderTestRenderer renderer; + frameBuilder.replayBakedOps<TestDispatcher>(renderer); + EXPECT_EQ(2, renderer.getIndex()); +} + +OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderParentReceivable) { + /* R is backward projected on C + A + / \ + B C + | + 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()); + frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA)); + + ZReorderTestRenderer renderer; + frameBuilder.replayBakedOps<TestDispatcher>(renderer); + EXPECT_EQ(3, renderer.getIndex()); +} + +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()); + frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA)); + + ZReorderTestRenderer renderer; + frameBuilder.replayBakedOps<TestDispatcher>(renderer); + EXPECT_EQ(2, renderer.getIndex()); +} + +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 + /* 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 + /|\ + / | \ + 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 + + FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, + sLightGeometry, Caches::getInstance()); + frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA)); + + ZReorderTestRenderer renderer; + frameBuilder.replayBakedOps<TestDispatcher>(renderer); + EXPECT_EQ(3, renderer.getIndex()); +} + +OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderProjectedSibling2) { + /* R is set to project on B, but R is not drawn because projecting on a sibling is not allowed. + A + | + G + /|\ + / | \ + 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()); + frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA)); + + ZReorderTestRenderer renderer; + frameBuilder.replayBakedOps<TestDispatcher>(renderer); + EXPECT_EQ(3, renderer.getIndex()); +} + +OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderGrandparentReceivable) { + /* R is backward projected on B + A + | + 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); + 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; + frameBuilder.replayBakedOps<TestDispatcher>(renderer); + EXPECT_EQ(3, renderer.getIndex()); +} + +OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderTwoReceivables) { + /* B and G are receivables, R is backward projected + A + / \ + B C + / \ + 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()); + frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA)); + + ZReorderTestRenderer renderer; + frameBuilder.replayBakedOps<TestDispatcher>(renderer); + EXPECT_EQ(4, renderer.getIndex()); +} + +OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderTwoReceivablesLikelyScenario) { + /* B and G are receivables, G is backward projected + A + / \ + B C + / \ + 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()); + frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA)); + + ZReorderTestRenderer renderer; + frameBuilder.replayBakedOps<TestDispatcher>(renderer); + EXPECT_EQ(4, renderer.getIndex()); +} + +OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderTwoReceivablesDeeper) { + /* B and G are receivables, R is backward projected + A + / \ + B C + / \ + G D + | + 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()); + frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA)); + + ZReorderTestRenderer renderer; + frameBuilder.replayBakedOps<TestDispatcher>(renderer); + EXPECT_EQ(5, renderer.getIndex()); +} + } // namespace uirenderer } // namespace android diff --git a/libs/hwui/tests/unit/GlopBuilderTests.cpp b/libs/hwui/tests/unit/GlopBuilderTests.cpp index 95543d33b1ef..caeb6bf0081b 100644 --- a/libs/hwui/tests/unit/GlopBuilderTests.cpp +++ b/libs/hwui/tests/unit/GlopBuilderTests.cpp @@ -45,7 +45,11 @@ static void expectFillEq(Glop::Fill& expectedFill, Glop::Fill& builtFill) { 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_EQ(expectedFill.texture.target, builtFill.texture.target); + 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()); + } EXPECT_EQ(expectedFill.texture.textureTransform, builtFill.texture.textureTransform); } @@ -85,9 +89,6 @@ static void expectTransformEq(Glop::Transform& expectedTransform, Glop::Transfor } static void expectGlopEq(Glop& expectedGlop, Glop& builtGlop) { -#if !HWUI_NEW_OPS - EXPECT_EQ(expectedGlop.bounds, builtGlop.bounds); -#endif expectBlendEq(expectedGlop.blend, builtGlop.blend); expectFillEq(expectedGlop.fill, builtGlop.fill); expectMeshEq(expectedGlop.mesh, builtGlop.mesh); @@ -111,11 +112,11 @@ static std::unique_ptr<Glop> blackUnitQuadGlop(RenderState& renderState) { 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, GL_INVALID_ENUM, nullptr }; + glop->fill.texture = { nullptr, GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr }; return glop; } -RENDERTHREAD_TEST(GlopBuilder, rectSnapTest) { +RENDERTHREAD_OPENGL_PIPELINE_TEST(GlopBuilder, rectSnapTest) { RenderState& renderState = renderThread.renderState(); Caches& caches = Caches::getInstance(); SkPaint paint; @@ -138,9 +139,6 @@ RENDERTHREAD_TEST(GlopBuilder, rectSnapTest) { // unit quad also should be translate by additional (0.3, 0.3) to snap to exact pixels. goldenGlop->transform.modelView.loadTranslate(1.3, 1.3, 0); goldenGlop->transform.modelView.scale(99, 99, 1); -#if !HWUI_NEW_OPS - goldenGlop->bounds = android::uirenderer::Rect(1.70, 1.70, 100.70, 100.70); -#endif goldenGlop->transform.canvas = simpleTranslate; goldenGlop->fill.texture.filter = GL_NEAREST; expectGlopEq(*goldenGlop, glop); diff --git a/libs/hwui/tests/unit/GpuMemoryTrackerTests.cpp b/libs/hwui/tests/unit/GpuMemoryTrackerTests.cpp index aa1dcb2ea51b..8cbd24edbde2 100644 --- a/libs/hwui/tests/unit/GpuMemoryTrackerTests.cpp +++ b/libs/hwui/tests/unit/GpuMemoryTrackerTests.cpp @@ -51,7 +51,7 @@ static void destroyEglContext() { TEST(GpuMemoryTracker, sizeCheck) { destroyEglContext(); - GpuMemoryTracker::onGLContextCreated(); + GpuMemoryTracker::onGpuContextCreated(); ASSERT_EQ(0, GpuMemoryTracker::getTotalSize(GpuObjectType::Texture)); ASSERT_EQ(0, GpuMemoryTracker::getInstanceCount(GpuObjectType::Texture)); { @@ -66,5 +66,5 @@ TEST(GpuMemoryTracker, sizeCheck) { } ASSERT_EQ(0, GpuMemoryTracker::getTotalSize(GpuObjectType::Texture)); ASSERT_EQ(0, GpuMemoryTracker::getInstanceCount(GpuObjectType::Texture)); - GpuMemoryTracker::onGLContextDestroyed(); + GpuMemoryTracker::onGpuContextDestroyed(); } diff --git a/libs/hwui/tests/unit/GradientCacheTests.cpp b/libs/hwui/tests/unit/GradientCacheTests.cpp index 0ee96470fc57..a3b346f11a87 100644 --- a/libs/hwui/tests/unit/GradientCacheTests.cpp +++ b/libs/hwui/tests/unit/GradientCacheTests.cpp @@ -23,7 +23,7 @@ using namespace android; using namespace android::uirenderer; -RENDERTHREAD_TEST(GradientCache, addRemove) { +RENDERTHREAD_OPENGL_PIPELINE_TEST(GradientCache, addRemove) { Extensions extensions; GradientCache cache(extensions); ASSERT_LT(1000u, cache.getMaxSize()) << "Expect non-trivial size"; diff --git a/libs/hwui/tests/unit/LeakCheckTests.cpp b/libs/hwui/tests/unit/LeakCheckTests.cpp index 6148b33eceb8..6c42ca1f2c2e 100644 --- a/libs/hwui/tests/unit/LeakCheckTests.cpp +++ b/libs/hwui/tests/unit/LeakCheckTests.cpp @@ -29,9 +29,9 @@ using namespace android::uirenderer; const FrameBuilder::LightGeometry sLightGeometery = { {100, 100, 100}, 50}; const BakedOpRenderer::LightInfo sLightInfo = { 128, 128 }; -RENDERTHREAD_TEST(LeakCheck, saveLayer_overdrawRejection) { +RENDERTHREAD_OPENGL_PIPELINE_TEST(LeakCheck, saveLayer_overdrawRejection) { auto node = TestUtils::createNode(0, 0, 100, 100, - [](RenderProperties& props, RecordingCanvas& canvas) { + [](RenderProperties& props, Canvas& canvas) { canvas.saveLayerAlpha(0, 0, 100, 100, 128, SaveFlags::ClipToLayer); canvas.drawRect(0, 0, 100, 100, SkPaint()); canvas.restore(); @@ -49,9 +49,9 @@ RENDERTHREAD_TEST(LeakCheck, saveLayer_overdrawRejection) { frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer); } -RENDERTHREAD_TEST(LeakCheck, saveLayerUnclipped_simple) { +RENDERTHREAD_OPENGL_PIPELINE_TEST(LeakCheck, saveLayerUnclipped_simple) { auto node = TestUtils::createNode(0, 0, 200, 200, - [](RenderProperties& props, RecordingCanvas& canvas) { + [](RenderProperties& props, Canvas& canvas) { canvas.saveLayerAlpha(10, 10, 190, 190, 128, (SaveFlags::Flags)(0)); canvas.drawRect(0, 0, 200, 200, SkPaint()); canvas.restore(); diff --git a/libs/hwui/tests/unit/MeshStateTests.cpp b/libs/hwui/tests/unit/MeshStateTests.cpp new file mode 100644 index 000000000000..511d6d25fbaf --- /dev/null +++ b/libs/hwui/tests/unit/MeshStateTests.cpp @@ -0,0 +1,36 @@ +/* + * 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 <debug/MockGlesDriver.h> +#include <debug/ScopedReplaceDriver.h> +#include <gtest/gtest.h> +#include <gmock/gmock.h> +#include <renderstate/MeshState.h> +#include <tests/common/TestUtils.h> + +using namespace android::uirenderer; +using namespace testing; + +RENDERTHREAD_OPENGL_PIPELINE_TEST(MeshState, genOrUpdate) { + debug::ScopedReplaceDriver<debug::MockGlesDriver> driverRef; + auto& mockGlDriver = driverRef.get(); + EXPECT_CALL(mockGlDriver, glGenBuffers_(_, _)).WillOnce(SetArgPointee<1>(35)); + EXPECT_CALL(mockGlDriver, glBindBuffer_(_, 35)); + EXPECT_CALL(mockGlDriver, glBufferData_(_, _, _, _)); + + GLuint buffer = 0; + 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 b7950aab5662..6cd595af6d2f 100644 --- a/libs/hwui/tests/unit/OffscreenBufferPoolTests.cpp +++ b/libs/hwui/tests/unit/OffscreenBufferPoolTests.cpp @@ -30,7 +30,7 @@ TEST(OffscreenBuffer, computeIdealDimension) { EXPECT_EQ(1024u, OffscreenBuffer::computeIdealDimension(1000)); } -RENDERTHREAD_TEST(OffscreenBuffer, construct) { +RENDERTHREAD_OPENGL_PIPELINE_TEST(OffscreenBuffer, construct) { OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 49u, 149u); EXPECT_EQ(49u, layer.viewportWidth); EXPECT_EQ(149u, layer.viewportHeight); @@ -41,7 +41,7 @@ RENDERTHREAD_TEST(OffscreenBuffer, construct) { EXPECT_EQ(64u * 192u * 4u, layer.getSizeInBytes()); } -RENDERTHREAD_TEST(OffscreenBuffer, getTextureCoordinates) { +RENDERTHREAD_OPENGL_PIPELINE_TEST(OffscreenBuffer, getTextureCoordinates) { OffscreenBuffer layerAligned(renderThread.renderState(), Caches::getInstance(), 256u, 256u); EXPECT_EQ(Rect(0, 1, 1, 0), layerAligned.getTextureCoordinates()); @@ -51,7 +51,7 @@ RENDERTHREAD_TEST(OffscreenBuffer, getTextureCoordinates) { layerUnaligned.getTextureCoordinates()); } -RENDERTHREAD_TEST(OffscreenBuffer, dirty) { +RENDERTHREAD_OPENGL_PIPELINE_TEST(OffscreenBuffer, dirty) { OffscreenBuffer buffer(renderThread.renderState(), Caches::getInstance(), 256u, 256u); buffer.dirty(Rect(-100, -100, 100, 100)); EXPECT_EQ(android::Rect(100, 100), buffer.region.getBounds()); @@ -65,7 +65,7 @@ RENDERTHREAD_TEST(OffscreenBufferPool, construct) { << "pool must read size from Properties"; } -RENDERTHREAD_TEST(OffscreenBufferPool, getPutClear) { +RENDERTHREAD_OPENGL_PIPELINE_TEST(OffscreenBufferPool, getPutClear) { OffscreenBufferPool pool; auto layer = pool.get(renderThread.renderState(), 100u, 200u); @@ -88,7 +88,7 @@ RENDERTHREAD_TEST(OffscreenBufferPool, getPutClear) { EXPECT_EQ(0u, pool.getCount()); } -RENDERTHREAD_TEST(OffscreenBufferPool, resize) { +RENDERTHREAD_OPENGL_PIPELINE_TEST(OffscreenBufferPool, resize) { OffscreenBufferPool pool; auto layer = pool.get(renderThread.renderState(), 64u, 64u); @@ -123,7 +123,7 @@ RENDERTHREAD_TEST(OffscreenBufferPool, resize) { pool.putOrDelete(layer2); } -RENDERTHREAD_TEST(OffscreenBufferPool, putAndDestroy) { +RENDERTHREAD_OPENGL_PIPELINE_TEST(OffscreenBufferPool, putAndDestroy) { OffscreenBufferPool pool; // layer too big to return to the pool // Note: this relies on the fact that the pool won't reject based on max texture size @@ -133,7 +133,7 @@ RENDERTHREAD_TEST(OffscreenBufferPool, putAndDestroy) { EXPECT_EQ(0u, pool.getCount()); // failed to put (so was destroyed instead) } -RENDERTHREAD_TEST(OffscreenBufferPool, clear) { +RENDERTHREAD_OPENGL_PIPELINE_TEST(OffscreenBufferPool, clear) { EXPECT_EQ(0, GpuMemoryTracker::getInstanceCount(GpuObjectType::OffscreenBuffer)); OffscreenBufferPool pool; diff --git a/libs/hwui/tests/unit/PathInterpolatorTests.cpp b/libs/hwui/tests/unit/PathInterpolatorTests.cpp new file mode 100644 index 000000000000..d7cb23a4b793 --- /dev/null +++ b/libs/hwui/tests/unit/PathInterpolatorTests.cpp @@ -0,0 +1,110 @@ +/* + * 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 <gtest/gtest.h> + +#include <Interpolator.h> + +namespace android { +namespace uirenderer { + +struct TestData { + const std::vector<float> x; + const std::vector<float> y; + const std::vector<float> inFraction; + const std::vector<float> outFraction; +}; + +const static TestData sTestDataSet[] = { + { + // Straight line as a path. + {0.0f, 1.0f}, + {0.0f, 1.0f}, + {0.0f, 0.2f, 0.4f, 0.6f, 0.8f, 1.0f}, + {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.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 + } + }, + + +}; + +static std::vector<float> getX(const TestData& data) { + return data.x; +} + +static std::vector<float> getY(const TestData& data) { + return data.y; +} + +TEST(Interpolator, pathInterpolation) { + 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 c072d0b80135..124f5face2cb 100644 --- a/libs/hwui/tests/unit/RecordingCanvasTests.cpp +++ b/libs/hwui/tests/unit/RecordingCanvasTests.cpp @@ -25,6 +25,7 @@ #include <utils/Color.h> #include <SkGradientShader.h> +#include <SkImagePriv.h> #include <SkShader.h> namespace android { @@ -46,7 +47,13 @@ static void validateSingleOp(std::unique_ptr<DisplayList>& dl, opValidator(*(dl->getOps()[0])); } -TEST(RecordingCanvas, emptyPlayback) { +// The RecordingCanvas is only ever used by the OpenGL RenderPipeline and never when Skia is in use. +// Thus, even though many of these test are not directly dependent on the current RenderPipeline, we +// set them all to be OPENGL_PIPELINE_TESTs in case the underlying code in RecordingCanvas ever +// changes to require the use of the OPENGL_PIPELINE. Currently the textureLayer test is the only +// test that requires being an OPENGL_PIPELINE_TEST. + +OPENGL_PIPELINE_TEST(RecordingCanvas, emptyPlayback) { auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) { canvas.save(SaveFlags::MatrixClip); canvas.restore(); @@ -54,10 +61,10 @@ TEST(RecordingCanvas, emptyPlayback) { playbackOps(*dl, [](const RecordedOp& op) { ADD_FAILURE(); }); } -TEST(RecordingCanvas, clipRect) { +OPENGL_PIPELINE_TEST(RecordingCanvas, clipRect) { auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 100, [](RecordingCanvas& canvas) { canvas.save(SaveFlags::MatrixClip); - canvas.clipRect(0, 0, 100, 100, SkRegion::kIntersect_Op); + canvas.clipRect(0, 0, 100, 100, SkClipOp::kIntersect); canvas.drawRect(0, 0, 50, 50, SkPaint()); canvas.drawRect(50, 50, 100, 100, SkPaint()); canvas.restore(); @@ -70,18 +77,18 @@ TEST(RecordingCanvas, clipRect) { << "Clip should be serialized once"; } -TEST(RecordingCanvas, emptyClipRect) { +OPENGL_PIPELINE_TEST(RecordingCanvas, emptyClipRect) { auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) { canvas.save(SaveFlags::MatrixClip); - canvas.clipRect(0, 0, 100, 100, SkRegion::kIntersect_Op); - canvas.clipRect(100, 100, 200, 200, SkRegion::kIntersect_Op); + 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.restore(); }); ASSERT_EQ(0u, dl->getOps().size()) << "Must be zero ops. Rect should be rejected."; } -TEST(RecordingCanvas, emptyPaintRejection) { +OPENGL_PIPELINE_TEST(RecordingCanvas, emptyPaintRejection) { auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) { SkPaint emptyPaint; emptyPaint.setColor(Color::Transparent); @@ -102,7 +109,7 @@ TEST(RecordingCanvas, emptyPaintRejection) { EXPECT_EQ(0u, dl->getOps().size()) << "Op should be rejected"; } -TEST(RecordingCanvas, drawArc) { +OPENGL_PIPELINE_TEST(RecordingCanvas, drawArc) { auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) { canvas.drawArc(0, 0, 200, 200, 0, 180, true, SkPaint()); canvas.drawArc(0, 0, 100, 100, 0, 360, true, SkPaint()); @@ -118,7 +125,7 @@ TEST(RecordingCanvas, drawArc) { EXPECT_EQ(Rect(100, 100), ops[1]->unmappedBounds); } -TEST(RecordingCanvas, drawLines) { +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 @@ -135,7 +142,7 @@ TEST(RecordingCanvas, drawLines) { << "unmapped bounds must be size of line, and not outset for stroke width"; } -TEST(RecordingCanvas, drawRect) { +OPENGL_PIPELINE_TEST(RecordingCanvas, drawRect) { auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) { canvas.drawRect(10, 20, 90, 180, SkPaint()); }); @@ -147,7 +154,7 @@ TEST(RecordingCanvas, drawRect) { EXPECT_EQ(Rect(10, 20, 90, 180), op.unmappedBounds); } -TEST(RecordingCanvas, drawRoundRect) { +OPENGL_PIPELINE_TEST(RecordingCanvas, drawRoundRect) { // Round case - stays rounded auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) { canvas.drawRoundRect(0, 0, 100, 100, 10, 10, SkPaint()); @@ -164,7 +171,7 @@ TEST(RecordingCanvas, drawRoundRect) { << "Non-rounded rects should be converted"; } -TEST(RecordingCanvas, drawGlyphs) { +OPENGL_PIPELINE_TEST(RecordingCanvas, drawGlyphs) { auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) { SkPaint paint; paint.setAntiAlias(true); @@ -185,7 +192,7 @@ TEST(RecordingCanvas, drawGlyphs) { ASSERT_EQ(1, count); } -TEST(RecordingCanvas, drawGlyphs_strikeThruAndUnderline) { +OPENGL_PIPELINE_TEST(RecordingCanvas, drawGlyphs_strikeThruAndUnderline) { auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) { SkPaint paint; paint.setAntiAlias(true); @@ -217,7 +224,7 @@ TEST(RecordingCanvas, drawGlyphs_strikeThruAndUnderline) { EXPECT_EQ(RecordedOpId::RectOp, ops[index++]->opId); // strikethrough } -TEST(RecordingCanvas, drawGlyphs_forceAlignLeft) { +OPENGL_PIPELINE_TEST(RecordingCanvas, drawGlyphs_forceAlignLeft) { auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) { SkPaint paint; paint.setAntiAlias(true); @@ -247,9 +254,9 @@ TEST(RecordingCanvas, drawGlyphs_forceAlignLeft) { ASSERT_EQ(3, count); } -TEST(RecordingCanvas, drawColor) { +OPENGL_PIPELINE_TEST(RecordingCanvas, drawColor) { auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) { - canvas.drawColor(Color::Black, SkXfermode::kSrcOver_Mode); + canvas.drawColor(Color::Black, SkBlendMode::kSrcOver); }); ASSERT_EQ(1u, dl->getOps().size()) << "Must be exactly one op"; @@ -259,10 +266,9 @@ TEST(RecordingCanvas, drawColor) { EXPECT_TRUE(op.unmappedBounds.isEmpty()) << "Expect undefined recorded bounds"; } -TEST(RecordingCanvas, backgroundAndImage) { +OPENGL_PIPELINE_TEST(RecordingCanvas, backgroundAndImage) { auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) { - SkBitmap bitmap; - bitmap.setInfo(SkImageInfo::MakeUnknown(25, 25)); + sk_sp<Bitmap> bitmap(TestUtils::createBitmap(25, 25)); SkPaint paint; paint.setColor(SK_ColorBLUE); @@ -278,7 +284,7 @@ TEST(RecordingCanvas, backgroundAndImage) { canvas.save(SaveFlags::MatrixClip); canvas.translate(25, 25); canvas.scale(2, 2); - canvas.drawBitmap(bitmap, 0, 0, nullptr); + canvas.drawBitmap(*bitmap, 0, 0, nullptr); canvas.restore(); } canvas.restore(); @@ -312,7 +318,7 @@ TEST(RecordingCanvas, backgroundAndImage) { ASSERT_EQ(2, count); } -RENDERTHREAD_TEST(RecordingCanvas, textureLayer) { +RENDERTHREAD_OPENGL_PIPELINE_TEST(RecordingCanvas, textureLayer) { auto layerUpdater = TestUtils::createTextureLayerUpdater(renderThread, 100, 100, SkMatrix::MakeTrans(5, 5)); @@ -327,7 +333,7 @@ RENDERTHREAD_TEST(RecordingCanvas, textureLayer) { }); } -TEST(RecordingCanvas, saveLayer_simple) { +OPENGL_PIPELINE_TEST(RecordingCanvas, saveLayer_simple) { auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) { canvas.saveLayerAlpha(10, 20, 190, 180, 128, SaveFlags::ClipToLayer); canvas.drawRect(10, 20, 190, 180, SkPaint()); @@ -361,7 +367,7 @@ TEST(RecordingCanvas, saveLayer_simple) { EXPECT_EQ(3, count); } -TEST(RecordingCanvas, saveLayer_rounding) { +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()); @@ -391,7 +397,7 @@ TEST(RecordingCanvas, saveLayer_rounding) { EXPECT_EQ(3, count); } -TEST(RecordingCanvas, saveLayer_missingRestore) { +OPENGL_PIPELINE_TEST(RecordingCanvas, saveLayer_missingRestore) { auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) { canvas.saveLayerAlpha(0, 0, 200, 200, 128, SaveFlags::ClipToLayer); canvas.drawRect(0, 0, 200, 200, SkPaint()); @@ -406,7 +412,7 @@ TEST(RecordingCanvas, saveLayer_missingRestore) { EXPECT_EQ(3, count) << "Missing a restore shouldn't result in an unmatched saveLayer"; } -TEST(RecordingCanvas, saveLayer_simpleUnclipped) { +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.drawRect(10, 20, 190, 180, SkPaint()); @@ -438,10 +444,10 @@ TEST(RecordingCanvas, saveLayer_simpleUnclipped) { EXPECT_EQ(3, count); } -TEST(RecordingCanvas, saveLayer_addClipFlag) { +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, SkRegion::kIntersect_Op); + canvas.clipRect(10, 20, 190, 180, SkClipOp::kIntersect); canvas.saveLayerAlpha(10, 20, 190, 180, 128, (SaveFlags::Flags)0); // unclipped canvas.drawRect(10, 20, 190, 180, SkPaint()); canvas.restore(); @@ -457,10 +463,10 @@ TEST(RecordingCanvas, saveLayer_addClipFlag) { EXPECT_EQ(3, count); } -TEST(RecordingCanvas, saveLayer_viewportCrop) { +OPENGL_PIPELINE_TEST(RecordingCanvas, saveLayer_viewportCrop) { auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) { // shouldn't matter, since saveLayer will clip to its bounds - canvas.clipRect(-1000, -1000, 1000, 1000, SkRegion::kReplace_Op); + canvas.clipRect(-1000, -1000, 1000, 1000, SkClipOp::kReplace); canvas.saveLayerAlpha(100, 100, 300, 300, 128, SaveFlags::ClipToLayer); canvas.drawRect(0, 0, 400, 400, SkPaint()); @@ -481,7 +487,7 @@ TEST(RecordingCanvas, saveLayer_viewportCrop) { EXPECT_EQ(3, count); } -TEST(RecordingCanvas, saveLayer_rotateUnclipped) { +OPENGL_PIPELINE_TEST(RecordingCanvas, saveLayer_rotateUnclipped) { auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) { canvas.save(SaveFlags::MatrixClip); canvas.translate(100, 100); @@ -507,7 +513,7 @@ TEST(RecordingCanvas, saveLayer_rotateUnclipped) { EXPECT_EQ(3, count); } -TEST(RecordingCanvas, saveLayer_rotateClipped) { +OPENGL_PIPELINE_TEST(RecordingCanvas, saveLayer_rotateClipped) { auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) { canvas.save(SaveFlags::MatrixClip); canvas.translate(100, 100); @@ -545,12 +551,12 @@ TEST(RecordingCanvas, saveLayer_rotateClipped) { EXPECT_EQ(3, count); } -TEST(RecordingCanvas, saveLayer_rejectBegin) { +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 // empty clip rect should force layer + contents to be rejected - canvas.clipRect(0, -20, 200, -20, SkRegion::kIntersect_Op); + canvas.clipRect(0, -20, 200, -20, SkClipOp::kIntersect); canvas.saveLayerAlpha(0, 0, 200, 200, 128, SaveFlags::ClipToLayer); canvas.drawRect(0, 0, 200, 200, SkPaint()); canvas.restore(); @@ -560,24 +566,24 @@ TEST(RecordingCanvas, saveLayer_rejectBegin) { ASSERT_EQ(0u, dl->getOps().size()) << "Begin/Rect/End should all be rejected."; } -TEST(RecordingCanvas, drawRenderNode_rejection) { +OPENGL_PIPELINE_TEST(RecordingCanvas, drawRenderNode_rejection) { auto child = TestUtils::createNode(50, 50, 150, 150, - [](RenderProperties& props, RecordingCanvas& canvas) { + [](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, SkRegion::kIntersect_Op); // empty clip, reject node + 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()); } -TEST(RecordingCanvas, drawRenderNode_projection) { +OPENGL_PIPELINE_TEST(RecordingCanvas, drawRenderNode_projection) { sp<RenderNode> background = TestUtils::createNode(50, 50, 150, 150, - [](RenderProperties& props, RecordingCanvas& canvas) { + [](RenderProperties& props, Canvas& canvas) { SkPaint paint; paint.setColor(SK_ColorWHITE); canvas.drawRect(0, 0, 100, 100, paint); @@ -618,11 +624,11 @@ TEST(RecordingCanvas, drawRenderNode_projection) { } } -TEST(RecordingCanvas, firstClipWillReplace) { +OPENGL_PIPELINE_TEST(RecordingCanvas, firstClipWillReplace) { auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) { canvas.save(SaveFlags::MatrixClip); // since no explicit clip set on canvas, this should be the one observed on op: - canvas.clipRect(-100, -100, 300, 300, SkRegion::kIntersect_Op); + canvas.clipRect(-100, -100, 300, 300, SkClipOp::kIntersect); SkPaint paint; paint.setColor(SK_ColorWHITE); @@ -635,11 +641,11 @@ TEST(RecordingCanvas, firstClipWillReplace) { EXPECT_CLIP_RECT(Rect(-100, -100, 300, 300), dl->getOps()[0]->localClip); } -TEST(RecordingCanvas, replaceClipIntersectWithRoot) { +OPENGL_PIPELINE_TEST(RecordingCanvas, replaceClipIntersectWithRoot) { auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 100, [](RecordingCanvas& canvas) { canvas.save(SaveFlags::MatrixClip); - canvas.clipRect(-10, -10, 110, 110, SkRegion::kReplace_Op); - canvas.drawColor(SK_ColorWHITE, SkXfermode::Mode::kSrcOver_Mode); + canvas.clipRect(-10, -10, 110, 110, SkClipOp::kReplace); + canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver); canvas.restore(); }); ASSERT_EQ(1u, dl->getOps().size()) << "Must have one op"; @@ -648,7 +654,7 @@ TEST(RecordingCanvas, replaceClipIntersectWithRoot) { EXPECT_TRUE(dl->getOps()[0]->localClip->intersectWithRoot); } -TEST(RecordingCanvas, insertReorderBarrier) { +OPENGL_PIPELINE_TEST(RecordingCanvas, insertReorderBarrier) { auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) { canvas.drawRect(0, 0, 400, 400, SkPaint()); canvas.insertReorderBarrier(true); @@ -669,14 +675,14 @@ TEST(RecordingCanvas, insertReorderBarrier) { EXPECT_TRUE(chunks[1].reorderChildren); } -TEST(RecordingCanvas, insertReorderBarrier_clip) { +OPENGL_PIPELINE_TEST(RecordingCanvas, insertReorderBarrier_clip) { auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) { // first chunk: no recorded clip canvas.insertReorderBarrier(true); canvas.drawRect(0, 0, 400, 400, SkPaint()); // second chunk: no recorded clip, since inorder region - canvas.clipRect(0, 0, 200, 200, SkRegion::kIntersect_Op); + canvas.clipRect(0, 0, 200, 200, SkClipOp::kIntersect); canvas.insertReorderBarrier(false); canvas.drawRect(0, 0, 400, 400, SkPaint()); @@ -699,7 +705,7 @@ TEST(RecordingCanvas, insertReorderBarrier_clip) { EXPECT_EQ(Rect(200, 200), chunks[2].reorderClip->rect); } -TEST(RecordingCanvas, refPaint) { +OPENGL_PIPELINE_TEST(RecordingCanvas, refPaint) { SkPaint paint; auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [&paint](RecordingCanvas& canvas) { @@ -727,62 +733,72 @@ TEST(RecordingCanvas, refPaint) { EXPECT_NE(&paint, ops[2]->paint); } -TEST(RecordingCanvas, refBitmap) { - SkBitmap bitmap = TestUtils::createSkBitmap(100, 100); +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); + canvas.drawBitmap(*bitmap, 0, 0, nullptr); }); auto& bitmaps = dl->getBitmapResources(); EXPECT_EQ(1u, bitmaps.size()); } -TEST(RecordingCanvas, refBitmapInShader_bitmapShader) { - SkBitmap bitmap = TestUtils::createSkBitmap(100, 100); +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; - SkAutoTUnref<SkShader> shader(SkShader::CreateBitmapShader(bitmap, + SkBitmap skBitmap; + bitmap->getSkBitmap(&skBitmap); + sk_sp<SkShader> shader = SkMakeBitmapShader(skBitmap, + SkShader::TileMode::kClamp_TileMode, SkShader::TileMode::kClamp_TileMode, - SkShader::TileMode::kClamp_TileMode)); - paint.setShader(shader); + nullptr, + kNever_SkCopyPixelsMode, + 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()); } -TEST(RecordingCanvas, refBitmapInShader_composeShader) { - SkBitmap bitmap = TestUtils::createSkBitmap(100, 100); +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; - SkAutoTUnref<SkShader> shader1(SkShader::CreateBitmapShader(bitmap, + SkBitmap skBitmap; + bitmap->getSkBitmap(&skBitmap); + sk_sp<SkShader> shader1 = SkMakeBitmapShader(skBitmap, + SkShader::TileMode::kClamp_TileMode, SkShader::TileMode::kClamp_TileMode, - SkShader::TileMode::kClamp_TileMode)); + nullptr, + kNever_SkCopyPixelsMode, + nullptr); SkPoint center; center.set(50, 50); SkColor colors[2]; colors[0] = Color::Black; colors[1] = Color::White; - SkAutoTUnref<SkShader> shader2(SkGradientShader::CreateRadial(center, 50, colors, nullptr, 2, - SkShader::TileMode::kRepeat_TileMode)); + sk_sp<SkShader> shader2 = SkGradientShader::MakeRadial(center, 50, colors, nullptr, 2, + SkShader::TileMode::kRepeat_TileMode); - SkAutoTUnref<SkShader> composeShader(SkShader::CreateComposeShader(shader1, shader2, - SkXfermode::Mode::kMultiply_Mode)); - paint.setShader(composeShader); + 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()); } -TEST(RecordingCanvas, drawText) { +OPENGL_PIPELINE_TEST(RecordingCanvas, drawText) { auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) { 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, kBidi_Force_LTR, paint, NULL); + canvas.drawText(dst.get(), 0, 5, 5, 25, 25, minikin::kBidi_Force_LTR, paint, NULL); }); int count = 0; @@ -797,7 +813,7 @@ TEST(RecordingCanvas, drawText) { ASSERT_EQ(1, count); } -TEST(RecordingCanvas, drawTextInHighContrast) { +OPENGL_PIPELINE_TEST(RecordingCanvas, drawTextInHighContrast) { auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) { canvas.setHighContrastText(true); Paint paint; @@ -806,7 +822,7 @@ TEST(RecordingCanvas, drawTextInHighContrast) { 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, kBidi_Force_LTR, paint, NULL); + canvas.drawText(dst.get(), 0, 5, 5, 25, 25, minikin::kBidi_Force_LTR, paint, NULL); }); int count = 0; diff --git a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp new file mode 100644 index 000000000000..f5ff05849793 --- /dev/null +++ b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp @@ -0,0 +1,949 @@ +/* + * 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 <gtest/gtest.h> +#include <VectorDrawable.h> + +#include "AnimationContext.h" +#include "DamageAccumulator.h" +#include "IContextFactory.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; +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) { + canvas.drawColor(Color::Red_500, SkBlendMode::kSrcOver); + }); + + auto skLiteDL = SkLiteDL::New(SkRect::MakeWH(1, 1)); + SkLiteRecorder canvas; + canvas.reset(skLiteDL.get()); + canvas.translate(100, 100); + RenderNodeDrawable drawable(rootNode.get(), &canvas); + + ASSERT_EQ(drawable.getRenderNode(), rootNode.get()); + ASSERT_EQ(&drawable.getNodeProperties(), &rootNode->properties()); + ASSERT_EQ(drawable.getRecordedMatrix(), canvas.getTotalMatrix()); +} + +namespace { + +static void drawOrderedRect(Canvas* canvas, uint8_t expectedDrawOrder) { + SkPaint paint; + // order put in blue channel, transparent so overlapped content doesn't get rejected + paint.setColor(SkColorSetARGB(1, 0, 0, expectedDrawOrder)); + canvas->drawRect(0, 0, 100, 100, paint); +} + +static void drawOrderedNode(Canvas* canvas, uint8_t expectedDrawOrder, float z) { + 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 +} + +static void drawOrderedNode(Canvas* canvas, uint8_t expectedDrawOrder, + std::function<void(RenderProperties& props, SkiaRecordingCanvas& canvas)> setup) { + 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 +} + +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 + EXPECT_EQ(expectedOrder, mDrawCounter++) << "An op was drawn out of order"; + } + int getIndex() { return mDrawCounter; } +protected: + int mDrawCounter = 0; +}; + +} // end anonymous namespace + +TEST(RenderNodeDrawable, zReorder) { + + 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 + 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); + }); + + //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) +{ + 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); + }); + + //attach a layer to the render node + auto surfaceLayer = SkSurface::MakeRasterN32Premul(1, 1); + auto canvas2 = surfaceLayer->getCanvas(); + canvas2->drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver); + rootNode->setLayerSurface(surfaceLayer); + + RenderNodeDrawable drawable1(rootNode.get(), &canvas, false); + canvas.drawDrawable(&drawable1); + ASSERT_EQ(SK_ColorRED, TestUtils::getColor(surface, 0, 0)); + + RenderNodeDrawable drawable2(rootNode.get(), &canvas, true); + canvas.drawDrawable(&drawable2); + ASSERT_EQ(SK_ColorWHITE, TestUtils::getColor(surface, 0, 0)); + + RenderNodeDrawable drawable3(rootNode.get(), &canvas, false); + canvas.drawDrawable(&drawable3); + ASSERT_EQ(SK_ColorRED, TestUtils::getColor(surface, 0, 0)); + + rootNode->setLayerSurface(sk_sp<SkSurface>()); +} + +namespace { +static SkRect getRecorderClipBounds(const SkiaRecordingCanvas& recorder) { + SkRect clipBounds; + recorder.getClipBounds(&clipBounds); + return clipBounds; +} + +static SkMatrix getRecorderMatrix(const SkiaRecordingCanvas& recorder) { + SkMatrix matrix; + recorder.getMatrix(&matrix); + return matrix; +} +} + +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()); + + //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.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()); + + 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); + ASSERT_EQ(SK_ColorGREEN, TestUtils::getColor(surface, 200, 600)); +} + +namespace { +class ContextFactory : public IContextFactory { +public: + virtual AnimationContext* createAnimationContext(renderthread::TimeLord& clock) override { + return new AnimationContext(clock); + } +}; +} // end anonymous namespace + +RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorder) { + static const int SCROLL_X = 5; + static const int SCROLL_Y = 10; + class ProjectionTestCanvas : public SkCanvas { + public: + ProjectionTestCanvas(int width, int height) : SkCanvas(width, height) {} + void onDrawRect(const SkRect& rect, const SkPaint& paint) override { + const int index = mDrawCounter++; + 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(); + } + EXPECT_EQ(expectedMatrix, getTotalMatrix()); + } + + int getIndex() { return mDrawCounter; } + protected: + int mDrawCounter = 0; + }; + + /** + * Construct a tree of nodes, where the root (A) has a receiver background (B), and a child (C) + * with a projecting child (P) of its own. P would normally draw between B and C's "background" + * draw, but because it is projected backwards, it's drawn in between B and C. + * + * 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, + [](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, + [](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, + [&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"); + 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; + info.observer = nullptr; + 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 + + //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) { + /* R is backward projected on B and C is a layer. + A + / \ + B C + | + R + */ + static const int SCROLL_X = 5; + static const int SCROLL_Y = 10; + static const int CANVAS_WIDTH = 400; + static const int CANVAS_HEIGHT = 400; + static const int LAYER_WIDTH = 200; + static const int LAYER_HEIGHT = 200; + class ProjectionTestCanvas : public SkCanvas { + public: + ProjectionTestCanvas(int* 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)); + } + 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)); + } + void onDrawOval(const SkRect&, const SkPaint&) override { + EXPECT_EQ(2, (*mDrawCounter)++); + SkMatrix expectedMatrix; + expectedMatrix.setTranslate(100 - SCROLL_X, 100 - SCROLL_Y); + EXPECT_EQ(expectedMatrix, getTotalMatrix()); + EXPECT_EQ(SkRect::MakeLTRB(-85, -80, 295, 300), TestUtils::getLocalClipBounds(this)); + } + int* mDrawCounter; + }; + + 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 { + 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 sk_sp<SkSurface>(); + } + sk_sp<SkImage> onNewImageSnapshot(SkBudgeted) override { + return sk_sp<SkImage>(); + } + void onCopyOnWrite(ContentChangeMode) override {} + int* mDrawCounter; + }; + + 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, + [](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, + [&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, + [&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 + 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; + info.observer = nullptr; + parent->prepareTree(info); + + int drawCounter = 0; + //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); + Matrix4 windowTransform; + windowTransform.loadTranslate(100, 100, 0); + child->getSkiaLayer()->inverseTransformInWindow.loadInverse(windowTransform); + + LayerUpdateQueue layerUpdateQueue; + layerUpdateQueue.enqueueLayerWithDamage(child.get(), + android::uirenderer::Rect(LAYER_WIDTH, LAYER_HEIGHT)); + SkiaPipeline::renderLayersImpl(layerUpdateQueue, true); + EXPECT_EQ(1, drawCounter); //assert index 0 is drawn on the layer + + RenderNodeDrawable drawable(parent.get(), surfaceLayer1->getCanvas(), true); + surfaceLayer1->getCanvas()->drawDrawable(&drawable); + EXPECT_EQ(4, drawCounter); + + // clean up layer pointer, so we can safely destruct RenderNode + child->setLayerSurface(nullptr); +} + +RENDERTHREAD_TEST(RenderNodeDrawable, projectionChildScroll) { + /* R is backward projected on B. + A + / \ + B C + | + R + */ + static const int SCROLL_X = 500000; + static const int SCROLL_Y = 0; + static const int CANVAS_WIDTH = 400; + static const int CANVAS_HEIGHT = 400; + class ProjectionChildScrollTestCanvas : public SkCanvas { + public: + ProjectionChildScrollTestCanvas() : SkCanvas(CANVAS_WIDTH, CANVAS_HEIGHT) {} + void onDrawRect(const SkRect& rect, const SkPaint& paint) override { + EXPECT_EQ(0, mDrawCounter++); + EXPECT_TRUE(getTotalMatrix().isIdentity()); + } + void onDrawOval(const SkRect&, const SkPaint&) override { + EXPECT_EQ(1, mDrawCounter++); + EXPECT_EQ(SkRect::MakeWH(CANVAS_WIDTH, CANVAS_HEIGHT), TestUtils::getClipBounds(this)); + EXPECT_TRUE(getTotalMatrix().isIdentity()); + } + int mDrawCounter = 0; + }; + + 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, + [](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, + [&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 + 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; + info.observer = nullptr; + parent->prepareTree(info); + + std::unique_ptr<ProjectionChildScrollTestCanvas> canvas(new ProjectionChildScrollTestCanvas()); + RenderNodeDrawable drawable(parent.get(), canvas.get(), true); + canvas->drawDrawable(&drawable); + EXPECT_EQ(2, canvas->mDrawCounter); +} + +namespace { +static int drawNode(RenderThread& renderThread, const sp<RenderNode>& renderNode) +{ + ContextFactory 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; + info.observer = nullptr; + renderNode->prepareTree(info); + + //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); + return canvas.getIndex(); +} +} + +RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderProjectedInMiddle) { + /* R is backward projected on B + A + / \ + B C + | + R + */ + 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, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { + drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { + props.setProjectBackwards(true); + props.setClipToBounds(false); + } ); //nodeR + } ); //nodeC + }); //nodeA + EXPECT_EQ(3, drawNode(renderThread, nodeA)); +} + +RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderProjectLast) { + /* R is backward projected on E + A + / | \ + / | \ + B C E + | + R + */ + 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 + props.setProjectBackwards(true); + props.setClipToBounds(false); + } ); //nodeR + } ); //nodeC + drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //drawn as 3 + props.setProjectionReceiver(true); + } ); //nodeE + }); //nodeA + EXPECT_EQ(4, drawNode(renderThread, nodeA)); +} + +RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderNoReceivable) { + /* R is backward projected without receiver + A + / \ + B C + | + R + */ + 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 + props.setProjectBackwards(true); + props.setClipToBounds(false); + } ); //nodeR + } ); //nodeC + }); //nodeA + EXPECT_EQ(2, drawNode(renderThread, nodeA)); +} + +RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderParentReceivable) { + /* R is backward projected on C + A + / \ + B C + | + R + */ + 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 + EXPECT_EQ(3, drawNode(renderThread, nodeA)); +} + +RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderSameNodeReceivable) { + /* R is backward projected on R + A + / \ + B C + | + R + */ + 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 + props.setProjectionReceiver(true); + props.setProjectBackwards(true); + props.setClipToBounds(false); + } ); //nodeR + } ); //nodeC + }); //nodeA + EXPECT_EQ(2, drawNode(renderThread, nodeA)); +} + +//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 + /|\ + / | \ + B C R + */ + 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 + drawOrderedNode(&canvas, 255, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { + props.setProjectBackwards(true); + props.setClipToBounds(false); + } ); //nodeR + }); //nodeA + EXPECT_EQ(2, drawNode(renderThread, nodeA)); +} + +RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderProjectedSibling2) { + /* R is set to project on B, but R is not drawn because projecting on a sibling is not allowed. + A + | + G + /|\ + / | \ + B C R + */ + 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 + drawOrderedNode(&canvas, 255, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { + props.setProjectBackwards(true); + props.setClipToBounds(false); + } ); //nodeR + } ); //nodeG + }); //nodeA + EXPECT_EQ(3, drawNode(renderThread, nodeA)); +} + +RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderGrandparentReceivable) { + /* R is backward projected on B + A + | + B + | + C + | + R + */ + 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 + EXPECT_EQ(3, drawNode(renderThread, nodeA)); +} + +RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderTwoReceivables) { + /* B and G are receivables, R is backward projected + A + / \ + B C + / \ + G R + */ + 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 + EXPECT_EQ(4, drawNode(renderThread, nodeA)); +} + +RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderTwoReceivablesLikelyScenario) { + /* B and G are receivables, G is backward projected + A + / \ + B C + / \ + G R + */ + 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 + EXPECT_EQ(4, drawNode(renderThread, nodeA)); +} + +RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderTwoReceivablesDeeper) { + /* B and G are receivables, R is backward projected + A + / \ + B C + / \ + G D + | + R + */ + 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 + EXPECT_EQ(5, drawNode(renderThread, nodeA)); +} + +RENDERTHREAD_TEST(RenderNodeDrawable, simple) { + static const int CANVAS_WIDTH = 100; + static const int CANVAS_HEIGHT = 200; + class SimpleTestCanvas : public TestCanvasBase { + public: + SimpleTestCanvas() : TestCanvasBase(CANVAS_WIDTH, CANVAS_HEIGHT) { + } + void onDrawRect(const SkRect& rect, const SkPaint& paint) override { + EXPECT_EQ(0, mDrawCounter++); + } + void onDrawImage(const SkImage*, SkScalar dx, SkScalar dy, const SkPaint*) override { + EXPECT_EQ(1, mDrawCounter++); + } + }; + + 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); + }); + + SimpleTestCanvas canvas; + RenderNodeDrawable drawable(node.get(), &canvas, true); + canvas.drawDrawable(&drawable); + EXPECT_EQ(2, canvas.mDrawCounter); +} + +RENDERTHREAD_TEST(RenderNodeDrawable, colorOp_unbounded) { + static const int CANVAS_WIDTH = 200; + static const int CANVAS_HEIGHT = 200; + class ColorTestCanvas : public TestCanvasBase { + public: + ColorTestCanvas() : TestCanvasBase(CANVAS_WIDTH, CANVAS_HEIGHT) { + } + void onDrawPaint(const SkPaint&) { + switch (mDrawCounter++) { + case 0: + // While this mirrors FrameBuilder::colorOp_unbounded, this value is different + // because there is no root (root is clipped in SkiaPipeline::renderFrame). + // SkiaPipeline.clipped and clip_replace verify the root clip. + EXPECT_TRUE(TestUtils::getClipBounds(this).isEmpty()); + 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 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); + canvas.drawDrawable(&drawable); + EXPECT_EQ(1, canvas.mDrawCounter); + RenderNodeDrawable drawable2(clippedColorView.get(), &canvas, true); + canvas.drawDrawable(&drawable2); + EXPECT_EQ(2, canvas.mDrawCounter); +} + +TEST(RenderNodeDrawable, renderNode) { + static const int CANVAS_WIDTH = 200; + static const int CANVAS_HEIGHT = 200; + class RenderNodeTestCanvas : public TestCanvasBase { + public: + 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(); + } + } + }; + + 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, + [&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(); + }); + + RenderNodeTestCanvas canvas; + RenderNodeDrawable drawable(parent.get(), &canvas, true); + canvas.drawDrawable(&drawable); + EXPECT_EQ(2, canvas.mDrawCounter); +} + diff --git a/libs/hwui/tests/unit/RenderNodeTests.cpp b/libs/hwui/tests/unit/RenderNodeTests.cpp index cf76a8691dcd..ab8e4e106d3e 100644 --- a/libs/hwui/tests/unit/RenderNodeTests.cpp +++ b/libs/hwui/tests/unit/RenderNodeTests.cpp @@ -15,6 +15,7 @@ */ #include <gtest/gtest.h> +#include <VectorDrawable.h> #include "AnimationContext.h" #include "DamageAccumulator.h" @@ -39,11 +40,11 @@ public: TEST(RenderNode, hasParents) { auto child = TestUtils::createNode(0, 0, 200, 400, - [](RenderProperties& props, TestCanvas& canvas) { - canvas.drawColor(Color::Red_500, SkXfermode::kSrcOver_Mode); + [](RenderProperties& props, Canvas& canvas) { + canvas.drawColor(Color::Red_500, SkBlendMode::kSrcOver); }); auto parent = TestUtils::createNode(0, 0, 200, 400, - [&child](RenderProperties& props, TestCanvas& canvas) { + [&child](RenderProperties& props, Canvas& canvas) { canvas.drawRenderNode(child.get()); }); @@ -52,8 +53,8 @@ TEST(RenderNode, hasParents) { EXPECT_TRUE(child->hasParents()) << "Child node has no parent"; EXPECT_FALSE(parent->hasParents()) << "Root node shouldn't have any parents"; - TestUtils::recordNode(*parent, [](TestCanvas& canvas) { - canvas.drawColor(Color::Amber_500, SkXfermode::kSrcOver_Mode); + TestUtils::recordNode(*parent, [](Canvas& canvas) { + canvas.drawColor(Color::Amber_500, SkBlendMode::kSrcOver); }); EXPECT_TRUE(child->hasParents()) << "Child should still have a parent"; @@ -68,7 +69,7 @@ TEST(RenderNode, hasParents) { TEST(RenderNode, releasedCallback) { class DecRefOnReleased : public GlFunctorLifecycleListener { public: - DecRefOnReleased(int* refcnt) : mRefCnt(refcnt) {} + explicit DecRefOnReleased(int* refcnt) : mRefCnt(refcnt) {} void onGlFunctorReleased(Functor* functor) override { *mRefCnt -= 1; } @@ -81,14 +82,14 @@ TEST(RenderNode, releasedCallback) { Functor noopFunctor; auto node = TestUtils::createNode(0, 0, 200, 400, - [&](RenderProperties& props, TestCanvas& canvas) { + [&](RenderProperties& props, Canvas& canvas) { refcnt++; canvas.callDrawGLFunction(&noopFunctor, listener.get()); }); TestUtils::syncHierarchyPropertiesAndDisplayList(node); EXPECT_EQ(1, refcnt); - TestUtils::recordNode(*node, [&](TestCanvas& canvas) { + TestUtils::recordNode(*node, [&](Canvas& canvas) { refcnt++; canvas.callDrawGLFunction(&noopFunctor, listener.get()); }); @@ -97,24 +98,26 @@ TEST(RenderNode, releasedCallback) { TestUtils::syncHierarchyPropertiesAndDisplayList(node); EXPECT_EQ(1, refcnt); - TestUtils::recordNode(*node, [](TestCanvas& canvas) {}); + TestUtils::recordNode(*node, [](Canvas& canvas) {}); EXPECT_EQ(1, refcnt); TestUtils::syncHierarchyPropertiesAndDisplayList(node); EXPECT_EQ(0, refcnt); } RENDERTHREAD_TEST(RenderNode, prepareTree_nullableDisplayList) { + auto rootNode = TestUtils::createNode(0, 0, 200, 400, nullptr); ContextFactory contextFactory; - CanvasContext canvasContext(renderThread, false, nullptr, &contextFactory); - TreeInfo info(TreeInfo::MODE_RT_ONLY, canvasContext); + 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; info.observer = nullptr; { auto nonNullDLNode = TestUtils::createNode(0, 0, 200, 400, - [](RenderProperties& props, TestCanvas& canvas) { - canvas.drawColor(Color::Red_500, SkXfermode::kSrcOver_Mode); + [](RenderProperties& props, Canvas& canvas) { + canvas.drawColor(Color::Red_500, SkBlendMode::kSrcOver); }); TestUtils::syncHierarchyPropertiesAndDisplayList(nonNullDLNode); EXPECT_TRUE(nonNullDLNode->getDisplayList()); @@ -128,5 +131,39 @@ RENDERTHREAD_TEST(RenderNode, prepareTree_nullableDisplayList) { nullDLNode->prepareTree(info); } - canvasContext.destroy(nullptr); + canvasContext->destroy(nullptr); +} + +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()); + }); + ContextFactory 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; + info.damageAccumulator = &damageAccumulator; + info.layerUpdateQueue = &layerUpdateQueue; + info.observer = nullptr; + + // Put node on HW layer + rootNode->mutateStagingProperties().mutateLayerProperties().setType(LayerType::RenderLayer); + + TestUtils::syncHierarchyPropertiesAndDisplayList(rootNode); + rootNode->prepareTree(info); + + // Check that the VD is in the dislay list, and the layer update queue contains the correct + // damage rect. + EXPECT_TRUE(rootNode->getDisplayList()->hasVectorDrawables()); + EXPECT_FALSE(info.layerUpdateQueue->entries().empty()); + EXPECT_EQ(rootNode.get(), info.layerUpdateQueue->entries().at(0).renderNode); + EXPECT_EQ(uirenderer::Rect(0, 0, 200, 400), info.layerUpdateQueue->entries().at(0).damage); + canvasContext->destroy(nullptr); } diff --git a/libs/hwui/tests/unit/SkiaBehaviorTests.cpp b/libs/hwui/tests/unit/SkiaBehaviorTests.cpp index cd759cb3d3b6..f32d97a3d809 100644 --- a/libs/hwui/tests/unit/SkiaBehaviorTests.cpp +++ b/libs/hwui/tests/unit/SkiaBehaviorTests.cpp @@ -17,22 +17,35 @@ #include "tests/common/TestUtils.h" #include <gtest/gtest.h> -#include <SkShader.h> #include <SkColorMatrixFilter.h> +#include <SkColorSpace.h> +#include <SkImagePriv.h> +#include <SkShader.h> using namespace android; using namespace android::uirenderer; +SkBitmap createSkBitmap(int width, int height) { + SkBitmap bitmap; + SkImageInfo info = SkImageInfo::Make(width, height, kN32_SkColorType, kPremul_SkAlphaType); + bitmap.setInfo(info); + bitmap.allocPixels(info); + return bitmap; +} + /** * 1x1 bitmaps must not be optimized into solid color shaders, since HWUI can't * compose/render color shaders */ TEST(SkiaBehavior, CreateBitmapShader1x1) { - SkBitmap origBitmap = TestUtils::createSkBitmap(1, 1); - SkAutoTUnref<SkShader> s(SkShader::CreateBitmapShader( + SkBitmap origBitmap = createSkBitmap(1, 1); + sk_sp<SkShader> s = SkMakeBitmapShader( origBitmap, SkShader::kClamp_TileMode, - SkShader::kRepeat_TileMode)); + SkShader::kRepeat_TileMode, + nullptr, + kNever_SkCopyPixelsMode, + nullptr); SkBitmap bitmap; SkShader::TileMode xy[2]; @@ -44,7 +57,7 @@ TEST(SkiaBehavior, CreateBitmapShader1x1) { } TEST(SkiaBehavior, genIds) { - SkBitmap bitmap = TestUtils::createSkBitmap(100, 100); + SkBitmap bitmap = createSkBitmap(100, 100); uint32_t genId = bitmap.getGenerationID(); bitmap.notifyPixelsChanged(); EXPECT_NE(genId, bitmap.getGenerationID()); @@ -52,19 +65,35 @@ TEST(SkiaBehavior, genIds) { TEST(SkiaBehavior, lightingColorFilter_simplify) { { - SkAutoTUnref<SkColorFilter> filter( - SkColorMatrixFilter::CreateLightingFilter(0x11223344, 0)); + sk_sp<SkColorFilter> filter( + SkColorMatrixFilter::MakeLightingFilter(0x11223344, 0)); SkColor observedColor; - SkXfermode::Mode observedMode; + SkBlendMode observedMode; ASSERT_TRUE(filter->asColorMode(&observedColor, &observedMode)); EXPECT_EQ(0xFF223344, observedColor); - EXPECT_EQ(SkXfermode::Mode::kModulate_Mode, observedMode); + EXPECT_EQ(SkBlendMode::kModulate, observedMode); } { - SkAutoTUnref<SkColorFilter> failFilter( - SkColorMatrixFilter::CreateLightingFilter(0x11223344, 0x1)); + sk_sp<SkColorFilter> failFilter( + SkColorMatrixFilter::MakeLightingFilter(0x11223344, 0x1)); EXPECT_FALSE(failFilter->asColorMode(nullptr, nullptr)); } } + +TEST(SkiaBehavior, porterDuffCreateIsCached) { + SkPaint paint; + paint.setBlendMode(SkBlendMode::kOverlay); + auto expected = paint.getBlendMode(); + paint.setBlendMode(SkBlendMode::kClear); + ASSERT_NE(expected, paint.getBlendMode()); + paint.setBlendMode(SkBlendMode::kOverlay); + ASSERT_EQ(expected, paint.getBlendMode()); +} + +TEST(SkiaBehavior, srgbColorSpaceIsSingleton) { + sk_sp<SkColorSpace> sRGB1 = SkColorSpace::MakeNamed(SkColorSpace::kSRGB_Named); + sk_sp<SkColorSpace> sRGB2 = SkColorSpace::MakeNamed(SkColorSpace::kSRGB_Named); + ASSERT_EQ(sRGB1.get(), sRGB2.get()); +} diff --git a/libs/hwui/tests/unit/SkiaCanvasTests.cpp b/libs/hwui/tests/unit/SkiaCanvasTests.cpp index 5a011938e2bb..0ac09ac49f5d 100644 --- a/libs/hwui/tests/unit/SkiaCanvasTests.cpp +++ b/libs/hwui/tests/unit/SkiaCanvasTests.cpp @@ -28,7 +28,7 @@ using namespace android::uirenderer; * Verify that we get the same culling bounds for text for (1) drawing glyphs * directly to a Canvas or (2) going through a SkPicture as an intermediate step. */ -TEST(SkiaCanvasProxy, drawGlyphsViaPicture) { +OPENGL_PIPELINE_TEST(SkiaCanvasProxy, drawGlyphsViaPicture) { auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) { // setup test variables SkPaint paint; @@ -45,7 +45,7 @@ TEST(SkiaCanvasProxy, drawGlyphsViaPicture) { SkCanvas* skCanvas = recorder.beginRecording(200, 200, NULL, 0); std::unique_ptr<Canvas> pictCanvas(Canvas::create_canvas(skCanvas)); TestUtils::drawUtf8ToCanvas(pictCanvas.get(), text, paint, 25, 25); - SkAutoTUnref<const SkPicture> picture(recorder.endRecording()); + sk_sp<SkPicture> picture = recorder.finishRecordingAsPicture(); canvas.asSkCanvas()->drawPicture(picture); }); diff --git a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp new file mode 100644 index 000000000000..8f6fc8b2e960 --- /dev/null +++ b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp @@ -0,0 +1,180 @@ +/* + * 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 <gtest/gtest.h> +#include <VectorDrawable.h> + +#include "AnimationContext.h" +#include "DamageAccumulator.h" +#include "IContextFactory.h" +#include "pipeline/skia/SkiaDisplayList.h" +#include "renderthread/CanvasContext.h" +#include "tests/common/TestUtils.h" + +using namespace android; +using namespace android::uirenderer; +using namespace android::uirenderer::renderthread; +using namespace android::uirenderer::skiapipeline; + +TEST(SkiaDisplayList, create) { + SkRect bounds = SkRect::MakeWH(200, 200); + SkiaDisplayList skiaDL(bounds); + ASSERT_TRUE(skiaDL.isEmpty()); + ASSERT_FALSE(skiaDL.mProjectionReceiver); + ASSERT_EQ(skiaDL.mDrawable->getBounds(), bounds); +} + +TEST(SkiaDisplayList, reset) { + SkRect bounds = SkRect::MakeWH(200, 200); + SkiaDisplayList skiaDL(bounds); + + SkCanvas dummyCanvas; + RenderNodeDrawable drawable(nullptr, &dummyCanvas); + skiaDL.mChildNodes.emplace_back(nullptr, &dummyCanvas); + skiaDL.mChildFunctors.emplace_back(nullptr, nullptr, &dummyCanvas); + skiaDL.mMutableImages.push_back(nullptr); + skiaDL.mVectorDrawables.push_back(nullptr); + skiaDL.mDrawable->drawAnnotation(bounds, "testAnnotation", nullptr); + skiaDL.mProjectionReceiver = &drawable; + + ASSERT_EQ(skiaDL.mDrawable->getBounds(), bounds); + ASSERT_FALSE(skiaDL.mChildNodes.empty()); + ASSERT_FALSE(skiaDL.mChildFunctors.empty()); + ASSERT_FALSE(skiaDL.mMutableImages.empty()); + ASSERT_FALSE(skiaDL.mVectorDrawables.empty()); + ASSERT_FALSE(skiaDL.isEmpty()); + ASSERT_TRUE(skiaDL.mProjectionReceiver); + + bounds = SkRect::MakeWH(100, 100); + skiaDL.reset(bounds); + + ASSERT_EQ(skiaDL.mDrawable->getBounds(), bounds); + ASSERT_TRUE(skiaDL.mChildNodes.empty()); + ASSERT_TRUE(skiaDL.mChildFunctors.empty()); + ASSERT_TRUE(skiaDL.mMutableImages.empty()); + ASSERT_TRUE(skiaDL.mVectorDrawables.empty()); + ASSERT_TRUE(skiaDL.isEmpty()); + ASSERT_FALSE(skiaDL.mProjectionReceiver); +} + +TEST(SkiaDisplayList, reuseDisplayList) { + sp<RenderNode> renderNode = new RenderNode(); + std::unique_ptr<SkiaDisplayList> availableList; + + // no list has been attached so it should return a nullptr + availableList = renderNode->detachAvailableList(); + ASSERT_EQ(availableList.get(), nullptr); + + // attach a displayList for reuse + SkiaDisplayList skiaDL(SkRect::MakeWH(200, 200)); + ASSERT_TRUE(skiaDL.reuseDisplayList(renderNode.get(), nullptr)); + + // 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 + + // after detaching there should return no available list + availableList = renderNode->detachAvailableList(); + ASSERT_EQ(availableList.get(), nullptr); +} + +TEST(SkiaDisplayList, syncContexts) { + SkRect bounds = SkRect::MakeWH(200, 200); + SkiaDisplayList skiaDL(bounds); + + SkCanvas dummyCanvas; + TestUtils::MockFunctor functor; + skiaDL.mChildFunctors.emplace_back(&functor, nullptr, &dummyCanvas); + + VectorDrawableRoot vectorDrawable(new VectorDrawable::Group()); + vectorDrawable.mutateStagingProperties()->setBounds(bounds); + skiaDL.mVectorDrawables.push_back(&vectorDrawable); + + // ensure that the functor and vectorDrawable are properly synced + skiaDL.syncContents(); + + ASSERT_EQ(functor.getLastMode(), DrawGlInfo::kModeSync); + ASSERT_EQ(vectorDrawable.mutateProperties()->getBounds(), bounds); +} + +class ContextFactory : public IContextFactory { +public: + virtual AnimationContext* createAnimationContext(renderthread::TimeLord& clock) override { + return new AnimationContext(clock); + } +}; + +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)); + TreeInfo info(TreeInfo::MODE_FULL, *canvasContext.get()); + DamageAccumulator damageAccumulator; + info.damageAccumulator = &damageAccumulator; + info.observer = nullptr; + + SkiaDisplayList skiaDL(SkRect::MakeWH(200, 200)); + + // prepare with a clean VD + VectorDrawableRoot cleanVD(new VectorDrawable::Group()); + skiaDL.mVectorDrawables.push_back(&cleanVD); + cleanVD.getBitmapUpdateIfDirty(); // this clears the dirty bit + + ASSERT_FALSE(cleanVD.isDirty()); + ASSERT_FALSE(cleanVD.getPropertyChangeWillBeConsumed()); + ASSERT_FALSE(skiaDL.prepareListAndChildren(info, false, [](RenderNode*, TreeInfo&, bool) {})); + ASSERT_TRUE(cleanVD.getPropertyChangeWillBeConsumed()); + + // prepare again this time adding a dirty VD + VectorDrawableRoot dirtyVD(new VectorDrawable::Group()); + skiaDL.mVectorDrawables.push_back(&dirtyVD); + + ASSERT_TRUE(dirtyVD.isDirty()); + ASSERT_FALSE(dirtyVD.getPropertyChangeWillBeConsumed()); + ASSERT_TRUE(skiaDL.prepareListAndChildren(info, false, [](RenderNode*, TreeInfo&, bool) {})); + ASSERT_TRUE(dirtyVD.getPropertyChangeWillBeConsumed()); + + // prepare again this time adding a RenderNode and a callback + sp<RenderNode> renderNode = new RenderNode(); + TreeInfo* infoPtr = &info; + SkCanvas dummyCanvas; + skiaDL.mChildNodes.emplace_back(renderNode.get(), &dummyCanvas); + bool hasRun = false; + ASSERT_TRUE(skiaDL.prepareListAndChildren(info, false, + [&hasRun, renderNode, infoPtr](RenderNode* n, TreeInfo& i, bool r) { + hasRun = true; + ASSERT_EQ(renderNode.get(), n); + ASSERT_EQ(infoPtr, &i); + ASSERT_FALSE(r); + })); + ASSERT_TRUE(hasRun); + + canvasContext->destroy(nullptr); +} + +TEST(SkiaDisplayList, updateChildren) { + SkRect bounds = SkRect::MakeWH(200, 200); + SkiaDisplayList skiaDL(bounds); + + sp<RenderNode> renderNode = new RenderNode(); + SkCanvas dummyCanvas; + skiaDL.mChildNodes.emplace_back(renderNode.get(), &dummyCanvas); + 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 new file mode 100644 index 000000000000..0b8c2a98fab5 --- /dev/null +++ b/libs/hwui/tests/unit/SkiaPipelineTests.cpp @@ -0,0 +1,347 @@ +/* + * 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 <gtest/gtest.h> +#include <VectorDrawable.h> + +#include "AnimationContext.h" +#include "DamageAccumulator.h" +#include "IContextFactory.h" +#include "pipeline/skia/SkiaDisplayList.h" +#include "pipeline/skia/SkiaRecordingCanvas.h" +#include "pipeline/skia/SkiaOpenGLPipeline.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; +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); + }); + 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, 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); + }); + LayerUpdateQueue layerUpdateQueue; + SkRect dirty = SkRect::MakeLargest(); + std::vector<sp<RenderNode>> renderNodes; + renderNodes.push_back(halfGreenNode); + android::uirenderer::Rect contentDrawBounds(0, 0, 2, 2); + auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread); + 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, 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, 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); + }); + LayerUpdateQueue layerUpdateQueue; + SkRect dirty = SkRect::MakeXYWH(0, 1, 2, 1); + std::vector<sp<RenderNode>> renderNodes; + renderNodes.push_back(redNode); + android::uirenderer::Rect contentDrawBounds(0, 0, 2, 2); + auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread); + 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, 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); + ASSERT_EQ(TestUtils::getColor(surface, 1, 1), SK_ColorRED); +} + +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 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); + }); + 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 + LayerUpdateQueue layerUpdateQueue; + SkRect dirty = SkRect::MakeLargest(); + layerUpdateQueue.enqueueLayerWithDamage(redNode.get(), dirty); + layerUpdateQueue.enqueueLayerWithDamage(blueNode.get(), SkRect::MakeWH(2, 1)); + ASSERT_EQ(layerUpdateQueue.entries().size(), 2UL); + + bool opaque = true; + FrameBuilder::LightGeometry lightGeometry; + lightGeometry.radius = 1.0f; + lightGeometry.center = { 0.0f, 0.0f, 0.0f }; + BakedOpRenderer::LightInfo lightInfo; + auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread); + pipeline->renderLayers(lightGeometry, &layerUpdateQueue, opaque, lightInfo); + ASSERT_EQ(TestUtils::getColor(surfaceLayer1, 0, 0), SK_ColorRED); + ASSERT_EQ(TestUtils::getColor(surfaceLayer2, 0, 0), SK_ColorBLUE); + ASSERT_EQ(TestUtils::getColor(surfaceLayer2, 0, 1), SK_ColorWHITE); + ASSERT_TRUE(layerUpdateQueue.entries().empty()); + redNode->setLayerSurface(sk_sp<SkSurface>()); + blueNode->setLayerSurface(sk_sp<SkSurface>()); +} + +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); + }); + 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 + android::uirenderer::Rect contentDrawBounds(0, 0, 0, 0); + auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread); + auto surface = SkSurface::MakeRasterN32Premul(1, 1); + + // Initialize the canvas to blue. + surface->getCanvas()->drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver); + ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE); + + // Single draw, should be white. + pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface); + ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorWHITE); + + // 1 Overdraw, should be blue blended onto white. + renderNodes.push_back(whiteNode); //this is the "content" node + renderNodes.push_back(whiteNode); //the "content" node above does not cause an overdraw, because + //it clips the first "background" node + pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, 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, 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, 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, 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, contentDrawBounds, surface); + ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned) 0xffff8080); +} + +namespace { +template <typename T> +class DeferLayer : public SkSurface_Base { +public: + DeferLayer() : SkSurface_Base(T().imageInfo(), nullptr) {} + virtual ~DeferLayer() {} + + SkCanvas* onNewCanvas() override { + return new T(); + } + sk_sp<SkSurface> onNewSurface(const SkImageInfo&) override { + return sk_sp<SkSurface>(); + } + sk_sp<SkImage> onNewImageSnapshot(SkBudgeted) override { + return sk_sp<SkImage>(); + } + T* canvas() { return static_cast<T*>(getCanvas()); } + void onCopyOnWrite(ContentChangeMode) override {} +}; +} + +RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, deferRenderNodeScene) { + class DeferTestCanvas : public SkCanvas { + public: + DeferTestCanvas() : SkCanvas(800, 600) {} + 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"; + } + EXPECT_EQ(expected, getTotalMatrix()); + } + int mDrawCounter = 0; + }; + + std::vector<sp<RenderNode>> nodes; + SkPaint transparentPaint; + transparentPaint.setAlpha(128); + + // backdrop + nodes.push_back(TestUtils::createSkiaNode(100, 100, 700, 500, // 600x400 + [&transparentPaint](RenderProperties& props, SkiaRecordingCanvas& canvas) { + 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, + [&transparentPaint](RenderProperties& props, SkiaRecordingCanvas& canvas) { + canvas.drawRect(0, 0, 800, 600, transparentPaint); + })); + + // overlay + nodes.push_back(TestUtils::createSkiaNode(0, 0, 800, 600, + [&transparentPaint](RenderProperties& props, SkiaRecordingCanvas& canvas) { + canvas.drawRect(0, 0, 800, 200, transparentPaint); + })); + + LayerUpdateQueue layerUpdateQueue; + SkRect dirty = SkRect::MakeWH(800, 600); + auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread); + sk_sp<DeferLayer<DeferTestCanvas>> surface(new DeferLayer<DeferTestCanvas>()); + pipeline->renderFrame(layerUpdateQueue, dirty, nodes, true, contentDrawBounds, surface); + EXPECT_EQ(4, surface->canvas()->mDrawCounter); +} + +RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, clipped) { + static const int CANVAS_WIDTH = 200; + static const int CANVAS_HEIGHT = 200; + class ClippedTestCanvas : public SkCanvas { + public: + 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)); + EXPECT_TRUE(getTotalMatrix().isIdentity()); + } + int mDrawCounter = 0; + }; + + std::vector<sp<RenderNode>> nodes; + 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); + })); + + 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, + SkRect::MakeWH(CANVAS_WIDTH, CANVAS_HEIGHT), surface); + EXPECT_EQ(1, surface->canvas()->mDrawCounter); +} + +RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, clip_replace) { + static const int CANVAS_WIDTH = 50; + static const int CANVAS_HEIGHT = 50; + class ClipReplaceTestCanvas : public SkCanvas { + public: + 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)) + << "Expect resolved clip to be intersection of viewport clip and clip op"; + } + int mDrawCounter = 0; + }; + + 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); + 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, + 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 new file mode 100644 index 000000000000..92d9d3d0d5fe --- /dev/null +++ b/libs/hwui/tests/unit/SkiaRenderPropertiesTests.cpp @@ -0,0 +1,149 @@ +/* + * 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 <gtest/gtest.h> +#include <VectorDrawable.h> + +#include "AnimationContext.h" +#include "DamageAccumulator.h" +#include "IContextFactory.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; +using namespace android::uirenderer::renderthread; +using namespace android::uirenderer::skiapipeline; + +namespace { + +static void testProperty(std::function<void(RenderProperties&)> propSetupCallback, + std::function<void(const SkCanvas&)> opValidateCallback) { + static const int CANVAS_WIDTH = 100; + static const int CANVAS_HEIGHT = 100; + class PropertyTestCanvas : public TestCanvasBase { + public: + PropertyTestCanvas(std::function<void(const SkCanvas&)> callback) + : TestCanvasBase(CANVAS_WIDTH, CANVAS_HEIGHT), mCallback(callback) {} + void onDrawRect(const SkRect& rect, const SkPaint& paint) override { + EXPECT_EQ(mDrawCounter++, 0); + mCallback(*this); + } + void onClipRRect(const SkRRect& rrect, SkClipOp op, ClipEdgeStyle style) { + SkCanvas::onClipRRect(rrect, op, style); + } + std::function<void(const SkCanvas&)> mCallback; + }; + + 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); + }); + + 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"; + }); +} + +TEST(RenderNodeDrawable, renderPropRevealClip) { + testProperty([](RenderProperties& properties) { + properties.mutableRevealClip().set(true, 50, 50, 25); + }, [](const SkCanvas& canvas) { + SkClipStack::Iter it(*canvas.getClipStack(), SkClipStack::Iter::kBottom_IterStart); + const SkClipStack::Element *top = it.next(); + ASSERT_NE(nullptr, top); + SkPath clip; + top->asPath(&clip); + SkRect rect; + EXPECT_TRUE(clip.isOval(&rect)); + EXPECT_EQ(SkRect::MakeLTRB(25, 25, 75, 75), rect); + }); +} + +TEST(RenderNodeDrawable, renderPropOutlineClip) { + testProperty([](RenderProperties& properties) { + properties.mutableOutline().setShouldClip(true); + properties.mutableOutline().setRoundRect(10, 20, 30, 40, 5.0f, 0.5f); + }, [](const SkCanvas& canvas) { + SkClipStack::Iter it(*canvas.getClipStack(), SkClipStack::Iter::kBottom_IterStart); + const SkClipStack::Element *top = it.next(); + ASSERT_NE(nullptr, top); + SkPath clip; + top->asPath(&clip); + SkRRect rrect; + EXPECT_TRUE(clip.isRRect(&rrect)); + EXPECT_EQ(SkRRect::MakeRectXY(SkRect::MakeLTRB(10, 20, 30, 40), 5.0f, 5.0f), rrect); + }); +} + +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"; + }); +} diff --git a/libs/hwui/tests/unit/TextDropShadowCacheTests.cpp b/libs/hwui/tests/unit/TextDropShadowCacheTests.cpp index 0d26df203f02..8312bda8d67d 100644 --- a/libs/hwui/tests/unit/TextDropShadowCacheTests.cpp +++ b/libs/hwui/tests/unit/TextDropShadowCacheTests.cpp @@ -26,7 +26,7 @@ using namespace android; using namespace android::uirenderer; -RENDERTHREAD_TEST(TextDropShadowCache, addRemove) { +RENDERTHREAD_OPENGL_PIPELINE_TEST(TextDropShadowCache, addRemove) { SkPaint paint; paint.setTextSize(20); diff --git a/libs/hwui/tests/unit/main.cpp b/libs/hwui/tests/unit/main.cpp index 409a12d37693..cea84c057b63 100644 --- a/libs/hwui/tests/unit/main.cpp +++ b/libs/hwui/tests/unit/main.cpp @@ -15,19 +15,16 @@ */ #include "gtest/gtest.h" +#include "gmock/gmock.h" #include "Caches.h" +#include "debug/GlesDriver.h" +#include "debug/NullGlesDriver.h" +#include "hwui/Typeface.h" #include "thread/TaskManager.h" -#include "tests/common/TestUtils.h" +#include "tests/common/LeakChecker.h" -#include <memunreachable/memunreachable.h> - -#include <cstdio> -#include <iostream> -#include <map> -#include <unordered_set> #include <signal.h> -#include <unistd.h> using namespace std; using namespace android; @@ -54,66 +51,12 @@ static void gtestSigHandler(int sig, siginfo_t* siginfo, void* context) { raise(sig); } -static void logUnreachable(initializer_list<UnreachableMemoryInfo> infolist) { - // merge them all - UnreachableMemoryInfo merged; - unordered_set<uintptr_t> addrs; - merged.allocation_bytes = 0; - merged.leak_bytes = 0; - merged.num_allocations = 0; - merged.num_leaks = 0; - for (auto& info : infolist) { - // We'll be a little hazzy about these ones and just hope the biggest - // is the most accurate - 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); - } - } - } - - // Now log the result - if (merged.num_leaks) { - cout << endl << "Leaked memory!" << endl; - if (!merged.leaks[0].backtrace.num_frames) { - cout << "Re-run with 'setprop libc.debug.malloc.program hwui_unit_test'" - << endl << "and 'setprop libc.debug.malloc.options backtrace=8'" - << " to get backtraces" << endl; - } - cout << merged.ToString(false); - } -} - -static void checkForLeaks() { - // TODO: Until we can shutdown the RT thread we need to do this in - // two passes as GetUnreachableMemory has limited insight into - // thread-local caches so some leaks will not be properly tagged as leaks - nsecs_t before = systemTime(); - UnreachableMemoryInfo rtMemInfo; - TestUtils::runOnRenderThread([&rtMemInfo](renderthread::RenderThread& thread) { - if (Caches::hasInstance()) { - Caches::getInstance().tasks.stop(); - } - // Check for leaks - if (!GetUnreachableMemory(rtMemInfo)) { - cerr << "Failed to get unreachable memory!" << endl; - return; - } - }); - UnreachableMemoryInfo uiMemInfo; - if (!GetUnreachableMemory(uiMemInfo)) { - cerr << "Failed to get unreachable memory!" << endl; - return; +class TypefaceEnvironment : public testing::Environment { +public: + virtual void SetUp() { + Typeface::setRobotoTypefaceForTest(); } - logUnreachable({rtMemInfo, uiMemInfo}); - nsecs_t after = systemTime(); - cout << "Leak check took " << ns2ms(after - before) << "ms" << endl; -} +}; int main(int argc, char* argv[]) { // Register a crash handler @@ -127,10 +70,17 @@ int main(int argc, char* argv[]) { gSigChain.insert(pair<int, struct sigaction>(sig, old_sa)); } + // Replace the default GLES driver + debug::GlesDriver::replace(std::make_unique<debug::NullGlesDriver>()); + // Run the tests testing::InitGoogleTest(&argc, argv); + testing::InitGoogleMock(&argc, argv); + + testing::AddGlobalTestEnvironment(new TypefaceEnvironment()); + int ret = RUN_ALL_TESTS(); - checkForLeaks(); + test::LeakChecker::checkForLeaks(); return ret; } diff --git a/libs/hwui/utils/Color.h b/libs/hwui/utils/Color.h index b5157f401438..f9cc46d1cf30 100644 --- a/libs/hwui/utils/Color.h +++ b/libs/hwui/utils/Color.h @@ -16,6 +16,8 @@ #ifndef COLOR_H #define COLOR_H +#include <math.h> + #include <SkColor.h> namespace android { @@ -80,6 +82,42 @@ namespace uirenderer { }; static constexpr int BrightColorsCount = sizeof(BrightColors) / sizeof(Color::Color); + // Opto-electronic conversion function for the sRGB color space + // Takes a gamma-encoded sRGB value and converts it to a linear 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 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 OECF(float linear) { +#ifdef ANDROID_ENABLE_LINEAR_BLENDING + return OECF_sRGB(linear); +#else + return linear; +#endif + } + + // Electro-optical conversion function for the sRGB color space + // Takes a linear sRGB value and converts it to a gamma-encoded 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 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 EOCF(float srgb) { +#ifdef ANDROID_ENABLE_LINEAR_BLENDING + return EOCF_sRGB(srgb); +#else + return srgb; +#endif + } } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/utils/GLUtils.h b/libs/hwui/utils/GLUtils.h index f8dfe1040063..c12747805293 100644 --- a/libs/hwui/utils/GLUtils.h +++ b/libs/hwui/utils/GLUtils.h @@ -27,7 +27,7 @@ namespace uirenderer { #if DEBUG_OPENGL #define GL_CHECKPOINT(LEVEL) \ do { if (DEBUG_OPENGL >= DEBUG_LEVEL_##LEVEL) {\ - LOG_ALWAYS_FATAL_IF(GLUtils::dumpGLErrors(),\ + LOG_ALWAYS_FATAL_IF(android::uirenderer::GLUtils::dumpGLErrors(),\ "GL errors! %s:%d", __FILE__, __LINE__);\ } } while (0) #else diff --git a/libs/hwui/utils/NinePatch.h b/libs/hwui/utils/NinePatch.h deleted file mode 100644 index 323e56312fb7..000000000000 --- a/libs/hwui/utils/NinePatch.h +++ /dev/null @@ -1,37 +0,0 @@ -/* -** -** Copyright 2015, The Android Open 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_GRAPHICS_NINEPATCH_H -#define ANDROID_GRAPHICS_NINEPATCH_H - -#include <androidfw/ResourceTypes.h> -#include <cutils/compiler.h> - -#include "SkCanvas.h" -#include "SkRegion.h" - -namespace android { - -class ANDROID_API NinePatch { -public: - static void Draw(SkCanvas* canvas, const SkRect& bounds, const SkBitmap& bitmap, - const Res_png_9patch& chunk, const SkPaint* paint, SkRegion** outRegion); -}; - -} // namespace android - -#endif // ANDROID_GRAPHICS_NINEPATCH_H diff --git a/libs/hwui/utils/NinePatchImpl.cpp b/libs/hwui/utils/NinePatchImpl.cpp deleted file mode 100644 index 985f3fb66814..000000000000 --- a/libs/hwui/utils/NinePatchImpl.cpp +++ /dev/null @@ -1,326 +0,0 @@ -/* -** -** Copyright 2006, The Android Open 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 "utils/NinePatch.h" - -#include "SkBitmap.h" -#include "SkCanvas.h" -#include "SkColorPriv.h" -#include "SkNinePatch.h" -#include "SkPaint.h" -#include "SkUnPreMultiply.h" - -#include <utils/Log.h> - -namespace android { - -static const bool kUseTrace = true; -static bool gTrace = false; - -static bool getColor(const SkBitmap& bitmap, int x, int y, SkColor* c) { - switch (bitmap.colorType()) { - case kN32_SkColorType: - *c = SkUnPreMultiply::PMColorToColor(*bitmap.getAddr32(x, y)); - break; - case kRGB_565_SkColorType: - *c = SkPixel16ToPixel32(*bitmap.getAddr16(x, y)); - break; - case kARGB_4444_SkColorType: - *c = SkUnPreMultiply::PMColorToColor( - SkPixel4444ToPixel32(*bitmap.getAddr16(x, y))); - break; - case kIndex_8_SkColorType: { - SkColorTable* ctable = bitmap.getColorTable(); - *c = SkUnPreMultiply::PMColorToColor( - (*ctable)[*bitmap.getAddr8(x, y)]); - break; - } - default: - return false; - } - return true; -} - -static SkColor modAlpha(SkColor c, int alpha) { - int scale = alpha + (alpha >> 7); - int a = SkColorGetA(c) * scale >> 8; - return SkColorSetA(c, a); -} - -static void drawStretchyPatch(SkCanvas* canvas, SkIRect& src, const SkRect& dst, - const SkBitmap& bitmap, const SkPaint& paint, - SkColor initColor, uint32_t colorHint, - bool hasXfer) { - if (colorHint != android::Res_png_9patch::NO_COLOR) { - ((SkPaint*)&paint)->setColor(modAlpha(colorHint, paint.getAlpha())); - canvas->drawRect(dst, paint); - ((SkPaint*)&paint)->setColor(initColor); - } else if (src.width() == 1 && src.height() == 1) { - SkColor c; - if (!getColor(bitmap, src.fLeft, src.fTop, &c)) { - goto SLOW_CASE; - } - if (0 != c || hasXfer) { - SkColor prev = paint.getColor(); - ((SkPaint*)&paint)->setColor(c); - canvas->drawRect(dst, paint); - ((SkPaint*)&paint)->setColor(prev); - } - } else { - SLOW_CASE: - canvas->drawBitmapRect(bitmap, SkRect::Make(src), dst, &paint); - } -} - -SkScalar calculateStretch(SkScalar boundsLimit, SkScalar startingPoint, - int srcSpace, int numStrechyPixelsRemaining, - int numFixedPixelsRemaining) { - SkScalar spaceRemaining = boundsLimit - startingPoint; - SkScalar stretchySpaceRemaining = - spaceRemaining - SkIntToScalar(numFixedPixelsRemaining); - return srcSpace * stretchySpaceRemaining / numStrechyPixelsRemaining; -} - -void NinePatch::Draw(SkCanvas* canvas, const SkRect& bounds, - const SkBitmap& bitmap, const Res_png_9patch& chunk, - const SkPaint* paint, SkRegion** outRegion) { - if (canvas && canvas->quickReject(bounds)) { - return; - } - - SkPaint defaultPaint; - if (NULL == paint) { - // matches default dither in NinePatchDrawable.java. - defaultPaint.setDither(true); - paint = &defaultPaint; - } - - const int32_t* xDivs = chunk.getXDivs(); - const int32_t* yDivs = chunk.getYDivs(); - // if our SkCanvas were back by GL we should enable this and draw this as - // a mesh, which will be faster in most cases. - if ((false)) { - SkNinePatch::DrawMesh(canvas, bounds, bitmap, - xDivs, chunk.numXDivs, - yDivs, chunk.numYDivs, - paint); - return; - } - - if (kUseTrace) { - gTrace = true; - } - - SkASSERT(canvas || outRegion); - - if (kUseTrace) { - if (canvas) { - const SkMatrix& m = canvas->getTotalMatrix(); - ALOGV("ninepatch [%g %g %g] [%g %g %g]\n", - SkScalarToFloat(m[0]), SkScalarToFloat(m[1]), SkScalarToFloat(m[2]), - SkScalarToFloat(m[3]), SkScalarToFloat(m[4]), SkScalarToFloat(m[5])); - } - - ALOGV("======== ninepatch bounds [%g %g]\n", SkScalarToFloat(bounds.width()), - SkScalarToFloat(bounds.height())); - ALOGV("======== ninepatch paint bm [%d,%d]\n", bitmap.width(), bitmap.height()); - ALOGV("======== ninepatch xDivs [%d,%d]\n", xDivs[0], xDivs[1]); - ALOGV("======== ninepatch yDivs [%d,%d]\n", yDivs[0], yDivs[1]); - } - - if (bounds.isEmpty() || - bitmap.width() == 0 || bitmap.height() == 0 || - (paint && paint->getXfermode() == NULL && paint->getAlpha() == 0)) - { - if (kUseTrace) { - ALOGV("======== abort ninepatch draw\n"); - } - return; - } - - // should try a quick-reject test before calling lockPixels - - SkAutoLockPixels alp(bitmap); - // after the lock, it is valid to check getPixels() - if (bitmap.getPixels() == NULL) - return; - - const bool hasXfer = paint->getXfermode() != NULL; - SkRect dst; - SkIRect src; - - const int32_t x0 = xDivs[0]; - const int32_t y0 = yDivs[0]; - const SkColor initColor = ((SkPaint*)paint)->getColor(); - const uint8_t numXDivs = chunk.numXDivs; - const uint8_t numYDivs = chunk.numYDivs; - int i; - int j; - int colorIndex = 0; - uint32_t color; - bool xIsStretchable; - const bool initialXIsStretchable = (x0 == 0); - bool yIsStretchable = (y0 == 0); - const int bitmapWidth = bitmap.width(); - const int bitmapHeight = bitmap.height(); - - // Number of bytes needed for dstRights array. - // Need to cast numXDivs to a larger type to avoid overflow. - const size_t dstBytes = ((size_t) numXDivs + 1) * sizeof(SkScalar); - SkScalar* dstRights = (SkScalar*) alloca(dstBytes); - bool dstRightsHaveBeenCached = false; - - int numStretchyXPixelsRemaining = 0; - for (i = 0; i < numXDivs; i += 2) { - numStretchyXPixelsRemaining += xDivs[i + 1] - xDivs[i]; - } - int numFixedXPixelsRemaining = bitmapWidth - numStretchyXPixelsRemaining; - int numStretchyYPixelsRemaining = 0; - for (i = 0; i < numYDivs; i += 2) { - numStretchyYPixelsRemaining += yDivs[i + 1] - yDivs[i]; - } - int numFixedYPixelsRemaining = bitmapHeight - numStretchyYPixelsRemaining; - - if (kUseTrace) { - ALOGV("NinePatch [%d %d] bounds [%g %g %g %g] divs [%d %d]\n", - bitmap.width(), bitmap.height(), - SkScalarToFloat(bounds.fLeft), SkScalarToFloat(bounds.fTop), - SkScalarToFloat(bounds.width()), SkScalarToFloat(bounds.height()), - numXDivs, numYDivs); - } - - src.fTop = 0; - dst.fTop = bounds.fTop; - // The first row always starts with the top being at y=0 and the bottom - // being either yDivs[1] (if yDivs[0]=0) or yDivs[0]. In the former case - // the first row is stretchable along the Y axis, otherwise it is fixed. - // The last row always ends with the bottom being bitmap.height and the top - // being either yDivs[numYDivs-2] (if yDivs[numYDivs-1]=bitmap.height) or - // yDivs[numYDivs-1]. In the former case the last row is stretchable along - // the Y axis, otherwise it is fixed. - // - // The first and last columns are similarly treated with respect to the X - // axis. - // - // The above is to help explain some of the special casing that goes on the - // code below. - - // The initial yDiv and whether the first row is considered stretchable or - // not depends on whether yDiv[0] was zero or not. - for (j = yIsStretchable ? 1 : 0; - j <= numYDivs && src.fTop < bitmapHeight; - j++, yIsStretchable = !yIsStretchable) { - src.fLeft = 0; - dst.fLeft = bounds.fLeft; - if (j == numYDivs) { - src.fBottom = bitmapHeight; - dst.fBottom = bounds.fBottom; - } else { - src.fBottom = yDivs[j]; - const int srcYSize = src.fBottom - src.fTop; - if (yIsStretchable) { - dst.fBottom = dst.fTop + calculateStretch(bounds.fBottom, dst.fTop, - srcYSize, - numStretchyYPixelsRemaining, - numFixedYPixelsRemaining); - numStretchyYPixelsRemaining -= srcYSize; - } else { - dst.fBottom = dst.fTop + SkIntToScalar(srcYSize); - numFixedYPixelsRemaining -= srcYSize; - } - } - - xIsStretchable = initialXIsStretchable; - // The initial xDiv and whether the first column is considered - // stretchable or not depends on whether xDiv[0] was zero or not. - const uint32_t* colors = chunk.getColors(); - for (i = xIsStretchable ? 1 : 0; - i <= numXDivs && src.fLeft < bitmapWidth; - i++, xIsStretchable = !xIsStretchable) { - color = colors[colorIndex++]; - if (i == numXDivs) { - src.fRight = bitmapWidth; - dst.fRight = bounds.fRight; - } else { - src.fRight = xDivs[i]; - if (dstRightsHaveBeenCached) { - dst.fRight = dstRights[i]; - } else { - const int srcXSize = src.fRight - src.fLeft; - if (xIsStretchable) { - dst.fRight = dst.fLeft + calculateStretch(bounds.fRight, dst.fLeft, - srcXSize, - numStretchyXPixelsRemaining, - numFixedXPixelsRemaining); - numStretchyXPixelsRemaining -= srcXSize; - } else { - dst.fRight = dst.fLeft + SkIntToScalar(srcXSize); - numFixedXPixelsRemaining -= srcXSize; - } - dstRights[i] = dst.fRight; - } - } - // If this horizontal patch is too small to be displayed, leave - // the destination left edge where it is and go on to the next patch - // in the source. - if (src.fLeft >= src.fRight) { - src.fLeft = src.fRight; - continue; - } - // Make sure that we actually have room to draw any bits - if (dst.fRight <= dst.fLeft || dst.fBottom <= dst.fTop) { - goto nextDiv; - } - // If this patch is transparent, skip and don't draw. - if (color == android::Res_png_9patch::TRANSPARENT_COLOR && !hasXfer) { - if (outRegion) { - if (*outRegion == NULL) { - *outRegion = new SkRegion(); - } - SkIRect idst; - dst.round(&idst); - //ALOGI("Adding trans rect: (%d,%d)-(%d,%d)\n", - // idst.fLeft, idst.fTop, idst.fRight, idst.fBottom); - (*outRegion)->op(idst, SkRegion::kUnion_Op); - } - goto nextDiv; - } - if (canvas) { - if (kUseTrace) { - ALOGV("-- src [%d %d %d %d] dst [%g %g %g %g]\n", - src.fLeft, src.fTop, src.width(), src.height(), - SkScalarToFloat(dst.fLeft), SkScalarToFloat(dst.fTop), - SkScalarToFloat(dst.width()), SkScalarToFloat(dst.height())); - if (2 == src.width() && SkIntToScalar(5) == dst.width()) { - ALOGV("--- skip patch\n"); - } - } - drawStretchyPatch(canvas, src, dst, bitmap, *paint, initColor, - color, hasXfer); - } - -nextDiv: - src.fLeft = src.fRight; - dst.fLeft = dst.fRight; - } - src.fTop = src.fBottom; - dst.fTop = dst.fBottom; - dstRightsHaveBeenCached = true; - } -} - -} // namespace android diff --git a/libs/hwui/utils/PaintUtils.h b/libs/hwui/utils/PaintUtils.h index 4faab9a5f648..845a3eac35f8 100644 --- a/libs/hwui/utils/PaintUtils.h +++ b/libs/hwui/utils/PaintUtils.h @@ -21,7 +21,6 @@ #include <SkColorFilter.h> #include <SkDrawLooper.h> #include <SkShader.h> -#include <SkXfermode.h> namespace android { namespace uirenderer { @@ -33,18 +32,6 @@ namespace uirenderer { class PaintUtils { public: - /** - * Safely retrieves the mode from the specified xfermode. If the specified - * xfermode is null, the mode is assumed to be SkXfermode::kSrcOver_Mode. - */ - static inline SkXfermode::Mode getXfermode(SkXfermode* mode) { - SkXfermode::Mode resultMode; - if (!SkXfermode::AsMode(mode, &resultMode)) { - resultMode = SkXfermode::kSrcOver_Mode; - } - return resultMode; - } - static inline GLenum getFilter(const SkPaint* paint) { if (!paint || paint->getFilterQuality() != kNone_SkFilterQuality) { return GL_LINEAR; @@ -56,7 +43,7 @@ public: static inline bool paintWillNotDraw(const SkPaint& paint) { return paint.getAlpha() == 0 && !paint.getColorFilter() - && getXfermode(paint.getXfermode()) == SkXfermode::kSrcOver_Mode; + && paint.getBlendMode() == SkBlendMode::kSrcOver; } // TODO: move to a method on android:Paint? replace with SkPaint::nothingToDraw()? @@ -64,7 +51,7 @@ public: return paint.getAlpha() == 0 && paint.getLooper() == nullptr && !paint.getColorFilter() - && getXfermode(paint.getXfermode()) == SkXfermode::kSrcOver_Mode; + && paint.getBlendMode() == SkBlendMode::kSrcOver; } static bool isOpaquePaint(const SkPaint* paint) { @@ -77,9 +64,9 @@ public: } // Only let simple srcOver / src blending modes declare opaque, since behavior is clear. - SkXfermode::Mode mode = getXfermode(paint->getXfermode()); - return mode == SkXfermode::Mode::kSrcOver_Mode - || mode == SkXfermode::Mode::kSrc_Mode; + SkBlendMode mode = paint->getBlendMode(); + return mode == SkBlendMode::kSrcOver + || mode == SkBlendMode::kSrc; } static bool isBlendedShader(const SkShader* shader) { @@ -121,8 +108,8 @@ public: return getTextShadow(paint, nullptr); } - static inline SkXfermode::Mode getXfermodeDirect(const SkPaint* paint) { - return paint ? getXfermode(paint->getXfermode()) : SkXfermode::kSrcOver_Mode; + static inline SkBlendMode getBlendModeDirect(const SkPaint* paint) { + return paint ? paint->getBlendMode() : SkBlendMode::kSrcOver; } static inline int getAlphaDirect(const SkPaint* paint) { diff --git a/libs/hwui/utils/StringUtils.h b/libs/hwui/utils/StringUtils.h index 5add95711f2d..af5d10f8522b 100644 --- a/libs/hwui/utils/StringUtils.h +++ b/libs/hwui/utils/StringUtils.h @@ -16,10 +16,14 @@ #ifndef STRING_UTILS_H #define STRING_UTILS_H +#include <iomanip> +#include <iostream> +#include <ostream> +#include <sstream> #include <string> #include <unordered_set> -#include <ostream> -#include <iomanip> + +#include <utils/Log.h> namespace android { namespace uirenderer { @@ -51,6 +55,22 @@ struct SizePrinter { } }; +class LogcatStream: public std::ostream { + class LogcatStreamBuf: public std::stringbuf { + virtual int sync() { + ALOGD("%s", str().c_str()); + str(""); + return 0; + } + }; + + LogcatStreamBuf buffer; +public: + LogcatStream() + :std::ostream(&buffer) { + } +}; + } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/utils/TestWindowContext.cpp b/libs/hwui/utils/TestWindowContext.cpp index b3195c4bbbfb..8b80d690b39f 100644 --- a/libs/hwui/utils/TestWindowContext.cpp +++ b/libs/hwui/utils/TestWindowContext.cpp @@ -16,7 +16,6 @@ #include "TestWindowContext.h" #include "AnimationContext.h" -#include "DisplayListCanvas.h" #include "IContextFactory.h" #include "RecordingCanvas.h" #include "RenderNode.h" @@ -28,6 +27,7 @@ #include "gui/Surface.h" #include "renderthread/RenderProxy.h" +#include <cutils/memory.h> namespace { @@ -57,7 +57,7 @@ class TestWindowContext::TestWindowData { public: - TestWindowData(SkISize size) : mSize(size) { + explicit TestWindowData(SkISize size) : mSize(size) { android::BufferQueue::createBufferQueue(&mProducer, &mConsumer); mCpuConsumer = new android::CpuConsumer(mConsumer, 1); mCpuConsumer->setName(android::String8("TestWindowContext")); @@ -86,20 +86,14 @@ public: mProxy->initialize(mAndroidSurface.get()); float lightX = mSize.width() / 2.0f; android::uirenderer::Vector3 lightVector { lightX, -200.0f, 800.0f }; - mProxy->setup(mSize.width(), mSize.height(), 800.0f, - 255 * 0.075f, 255 * 0.15f); + mProxy->setup(800.0f, 255 * 0.075f, 255 * 0.15f); mProxy->setLightCenter(lightVector); -#if HWUI_NEW_OPS mCanvas.reset(new android::uirenderer::RecordingCanvas(mSize.width(), mSize.height())); -#else - mCanvas.reset(new android::uirenderer::DisplayListCanvas(mSize.width(), mSize.height())); -#endif } SkCanvas* prepareToDraw() { //mCanvas->reset(mSize.width(), mSize.height()); - mCanvas->clipRect(0, 0, mSize.width(), mSize.height(), - SkRegion::Op::kReplace_Op); + mCanvas->clipRect(0, 0, mSize.width(), mSize.height(), SkClipOp::kReplace); return mCanvas->asSkCanvas(); } @@ -115,12 +109,13 @@ public: } bool capturePixels(SkBitmap* bmp) { + sk_sp<SkColorSpace> colorSpace = SkColorSpace::MakeNamed(SkColorSpace::kSRGB_Named); SkImageInfo destinationConfig = SkImageInfo::Make(mSize.width(), mSize.height(), - kRGBA_8888_SkColorType, kPremul_SkAlphaType); + kRGBA_8888_SkColorType, kPremul_SkAlphaType, colorSpace); bmp->allocPixels(destinationConfig); - sk_memset32((uint32_t*) bmp->getPixels(), SK_ColorRED, - mSize.width() * mSize.height()); + android_memset32((uint32_t*) bmp->getPixels(), SK_ColorRED, + mSize.width() * mSize.height() * 4); android::CpuConsumer::LockedBuffer nativeBuffer; android::status_t retval = mCpuConsumer->lockNextBuffer(&nativeBuffer); @@ -171,11 +166,7 @@ private: std::unique_ptr<android::uirenderer::RenderNode> mRootNode; std::unique_ptr<android::uirenderer::renderthread::RenderProxy> mProxy; -#if HWUI_NEW_OPS std::unique_ptr<android::uirenderer::RecordingCanvas> mCanvas; -#else - std::unique_ptr<android::uirenderer::DisplayListCanvas> mCanvas; -#endif android::sp<android::IGraphicBufferProducer> mProducer; android::sp<android::IGraphicBufferConsumer> mConsumer; android::sp<android::CpuConsumer> mCpuConsumer; diff --git a/libs/hwui/utils/VectorDrawableUtils.cpp b/libs/hwui/utils/VectorDrawableUtils.cpp index ca75c5945b7f..6f0c96db4b1e 100644 --- a/libs/hwui/utils/VectorDrawableUtils.cpp +++ b/libs/hwui/utils/VectorDrawableUtils.cpp @@ -198,12 +198,12 @@ static void drawArc(SkPath* p, /* Solve for intersecting unit circles */ double dsq = dx * dx + dy * dy; if (dsq == 0.0) { - ALOGW("Points are coincident"); + VECTOR_DRAWABLE_LOGD("Points are coincident"); return; /* Points are coincident */ } double disc = 1.0 / dsq - 1.0 / 4.0; if (disc < 0.0) { - ALOGW("Points are too far apart %f", dsq); + 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); diff --git a/libs/incident/Android.mk b/libs/incident/Android.mk new file mode 100644 index 000000000000..439e86deba5d --- /dev/null +++ b/libs/incident/Android.mk @@ -0,0 +1,41 @@ +# 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. + +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_MODULE := libincident + +LOCAL_CFLAGS := \ + -Wall -Werror -Wno-missing-field-initializers -Wno-unused-variable -Wunused-parameter + +LOCAL_SHARED_LIBRARIES := \ + libbinder \ + liblog \ + libutils + +LOCAL_AIDL_INCLUDES := $(LOCAL_PATH)/../../core/java +LOCAL_C_INCLUDES := \ + $(LOCAL_PATH)/include + +LOCAL_SRC_FILES := \ + ../../core/java/android/os/IIncidentManager.aidl \ + ../../core/java/android/os/IIncidentReportCompletedListener.aidl \ + ../../core/java/android/os/IIncidentReportStatusListener.aidl \ + src/IncidentReportArgs.cpp + +LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include + +include $(BUILD_SHARED_LIBRARY) + diff --git a/libs/incident/include/android/os/IncidentReportArgs.h b/libs/incident/include/android/os/IncidentReportArgs.h new file mode 100644 index 000000000000..956ef6c39b99 --- /dev/null +++ b/libs/incident/include/android/os/IncidentReportArgs.h @@ -0,0 +1,62 @@ +/** + * 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. + */ + +#ifndef ANDROID_OS_DUMPSTATE_ARGS_H_ +#define ANDROID_OS_DUMPSTATE_ARGS_H_ + +#include <binder/Parcel.h> +#include <binder/Parcelable.h> +#include <utils/String16.h> + +#include <set> +#include <vector> + +namespace android { +namespace os { + +using namespace std; + +class IncidentReportArgs : public Parcelable { +public: + IncidentReportArgs(); + explicit IncidentReportArgs(const IncidentReportArgs& that); + virtual ~IncidentReportArgs(); + + virtual status_t writeToParcel(Parcel* out) const; + virtual status_t readFromParcel(const Parcel* in); + + void setAll(bool all); + void addSection(int section); + void addHeader(const vector<int8_t>& header); + + inline bool all() const { return mAll; }; + bool containsSection(int section) const; + + inline const set<int>& sections() const { return mSections; } + inline const vector<vector<int8_t>>& headers() const { return mHeaders; } + + void merge(const IncidentReportArgs& that); + +private: + set<int> mSections; + vector<vector<int8_t>> mHeaders; + bool mAll; +}; + +} +} + +#endif // ANDROID_OS_DUMPSTATE_ARGS_H_ diff --git a/libs/incident/proto/android/privacy.proto b/libs/incident/proto/android/privacy.proto new file mode 100644 index 000000000000..ae5af0e4afa7 --- /dev/null +++ b/libs/incident/proto/android/privacy.proto @@ -0,0 +1,57 @@ +/* + * 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. + */ + +syntax = "proto2"; + +option java_package = "android"; +option java_multiple_files = true; + +import "google/protobuf/descriptor.proto"; + +package android; + +// TODO: It's better to track this by semantic types and set policy for those. +// Do this for now to bootstrap the tools. +enum Destination { + // Fields or messages annotated with DEST_LOCAL must never be + // extracted from the device automatically. They can be accessed + // by tools on the developer's workstation, and if they are sent + // to another device that must be by the user, with a PII warning. (TBD) + DEST_LOCAL = 0; + + // Fields or messages annotated with DEST_EXPLICIT can be sent + // off the device with an explicit user action. + DEST_EXPLICIT = 1; + + // Fields or messages annotated with DEST_LOCAL 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; + + // There is no more permissive option than DEST_AUTOMATIC. +} + +message PrivacyFlags { + optional Destination dest = 1 [ + default = DEST_LOCAL + ]; +} + +extend google.protobuf.FieldOptions { + // Flags for automatically filtering statistics + optional PrivacyFlags privacy = 102672883; +} diff --git a/libs/incident/src/IncidentReportArgs.cpp b/libs/incident/src/IncidentReportArgs.cpp new file mode 100644 index 000000000000..f60490911aed --- /dev/null +++ b/libs/incident/src/IncidentReportArgs.cpp @@ -0,0 +1,172 @@ +/** + * 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. + */ + +#define LOG_TAG "dumpstate" + +#include <android/os/IncidentReportArgs.h> + +#include <cutils/log.h> + +namespace android { +namespace os { + +IncidentReportArgs::IncidentReportArgs() + :mSections(), + mAll(false) +{ +} + +IncidentReportArgs::IncidentReportArgs(const IncidentReportArgs& that) + :mSections(that.mSections), + mHeaders(that.mHeaders), + mAll(that.mAll) +{ +} + +IncidentReportArgs::~IncidentReportArgs() +{ +} + +status_t +IncidentReportArgs::writeToParcel(Parcel* out) const +{ + status_t err; + + err = out->writeInt32(mAll); + if (err != NO_ERROR) { + return err; + } + + err = out->writeInt32(mSections.size()); + if (err != NO_ERROR) { + return err; + } + + for (set<int>::const_iterator it=mSections.begin(); it!=mSections.end(); it++) { + err = out->writeInt32(*it); + if (err != NO_ERROR) { + return err; + } + } + + err = out->writeInt32(mHeaders.size()); + if (err != NO_ERROR) { + return err; + } + + for (vector<vector<int8_t>>::const_iterator it = mHeaders.begin(); it != mHeaders.end(); it++) { + err = out->writeByteVector(*it); + if (err != NO_ERROR) { + return err; + } + } + + return NO_ERROR; +} + +status_t +IncidentReportArgs::readFromParcel(const Parcel* in) +{ + status_t err; + + int32_t all; + err = in->readInt32(&all); + if (err != NO_ERROR) { + return err; + } + if (all != 0) { + mAll = all; + } + + mSections.clear(); + int32_t sectionCount; + err = in->readInt32(§ionCount); + if (err != NO_ERROR) { + return err; + } + for (int i=0; i<sectionCount; i++) { + int32_t section; + err = in->readInt32(§ion); + if (err != NO_ERROR) { + return err; + } + + mSections.insert(section); + } + + int32_t headerCount; + err = in->readInt32(&headerCount); + if (err != NO_ERROR) { + return err; + } + mHeaders.resize(headerCount); + for (int i=0; i<headerCount; i++) { + err = in->readByteVector(&mHeaders[i]); + if (err != NO_ERROR) { + return err; + } + } + + return OK; +} + +void +IncidentReportArgs::setAll(bool all) +{ + mAll = all; + if (all) { + mSections.clear(); + } +} + +void +IncidentReportArgs::addSection(int section) +{ + if (!mAll) { + mSections.insert(section); + } +} + +void +IncidentReportArgs::addHeader(const vector<int8_t>& header) +{ + mHeaders.push_back(header); +} + +bool +IncidentReportArgs::containsSection(int section) const +{ + return mAll || mSections.find(section) != mSections.end(); +} + +void +IncidentReportArgs::merge(const IncidentReportArgs& that) +{ + if (mAll) { + return; + } else if (that.mAll) { + mAll = true; + mSections.clear(); + } else { + for (set<int>::const_iterator it=that.mSections.begin(); + it!=that.mSections.end(); it++) { + mSections.insert(*it); + } + } +} + +} +} diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp index 07bcbd35819d..7c6046789cdc 100644 --- a/libs/input/PointerController.cpp +++ b/libs/input/PointerController.cpp @@ -31,7 +31,7 @@ #include <SkCanvas.h> #include <SkColor.h> #include <SkPaint.h> -#include <SkXfermode.h> +#include <SkBlendMode.h> #pragma GCC diagnostic pop namespace android { @@ -108,7 +108,7 @@ PointerController::PointerController(const sp<PointerControllerPolicyInterface>& mLocked.pointerAlpha = 0.0f; // pointer is initially faded mLocked.pointerSprite = mSpriteController->createSprite(); mLocked.pointerIconChanged = false; - mLocked.requestedPointerType= mPolicy->getDefaultPointerIconId(); + mLocked.requestedPointerType = mPolicy->getDefaultPointerIconId(); mLocked.animationFrameIndex = 0; mLocked.lastFrameUpdatedTime = 0; @@ -631,7 +631,7 @@ void PointerController::updatePointerLocked() { if (mLocked.pointerIconChanged || mLocked.presentationChanged) { if (mLocked.presentation == PRESENTATION_POINTER) { - if (mLocked.requestedPointerType== mPolicy->getDefaultPointerIconId()) { + if (mLocked.requestedPointerType == mPolicy->getDefaultPointerIconId()) { mLocked.pointerSprite->setIcon(mLocked.pointerIcon); } else { std::map<int32_t, SpriteIcon>::const_iterator iter = diff --git a/libs/input/SpriteController.cpp b/libs/input/SpriteController.cpp index 049b76e6986e..4991f0434bc2 100644 --- a/libs/input/SpriteController.cpp +++ b/libs/input/SpriteController.cpp @@ -30,7 +30,6 @@ #include <SkCanvas.h> #include <SkColor.h> #include <SkPaint.h> -#include <SkXfermode.h> #pragma GCC diagnostic pop #include <android/native_window.h> @@ -216,7 +215,7 @@ void SpriteController::doUpdateSprites() { SkCanvas surfaceCanvas(surfaceBitmap); SkPaint paint; - paint.setXfermodeMode(SkXfermode::kSrc_Mode); + paint.setBlendMode(SkBlendMode::kSrc); surfaceCanvas.drawBitmap(update.state.icon.bitmap, 0, 0, &paint); if (outBuffer.width > update.state.icon.bitmap.width()) { diff --git a/libs/services/Android.mk b/libs/services/Android.mk new file mode 100644 index 000000000000..cbfd4b3f9f10 --- /dev/null +++ b/libs/services/Android.mk @@ -0,0 +1,43 @@ +# 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) + +# Provides C++ wrappers for system services. + +include $(CLEAR_VARS) + +LOCAL_MODULE := libservices +LOCAL_SRC_FILES := \ + ../../core/java/com/android/internal/os/IDropBoxManagerService.aidl \ + src/os/DropBoxManager.cpp + +LOCAL_AIDL_INCLUDES := \ + $(LOCAL_PATH)/../../core/java +LOCAL_C_INCLUDES := \ + system/core/include +LOCAL_SHARED_LIBRARIES := \ + libbinder \ + liblog \ + libcutils \ + libutils + +LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include +LOCAL_C_INCLUDES += $(LOCAL_PATH)/include + +LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code + +include $(BUILD_SHARED_LIBRARY) + + diff --git a/libs/services/include/android/os/DropBoxManager.h b/libs/services/include/android/os/DropBoxManager.h new file mode 100644 index 000000000000..8717178bb7d6 --- /dev/null +++ b/libs/services/include/android/os/DropBoxManager.h @@ -0,0 +1,94 @@ +/* + * 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. + */ + +#ifndef _ANDROID_OS_DROPBOXMANAGER_H +#define _ANDROID_OS_DROPBOXMANAGER_H + +#include <android-base/unique_fd.h> +#include <binder/Parcel.h> +#include <binder/Parcelable.h> +#include <binder/Status.h> +#include <utils/RefBase.h> + +#include <vector> + +namespace android { +namespace os { + +using namespace android; +using namespace android::base; +using namespace android::binder; +using namespace std; + +class DropBoxManager : public virtual RefBase +{ +public: + enum { + IS_EMPTY = 1, + IS_TEXT = 2, + IS_GZIPPED = 4 + }; + + DropBoxManager(); + virtual ~DropBoxManager(); + + static sp<DropBoxManager> create(); + + // Create a new entry with plain text contents. + Status addText(const String16& tag, const string& text); + + // Create a new Entry with byte array contents. Makes a copy of the data. + Status addData(const String16& tag, uint8_t const* data, size_t size, int flags); + + // Create a new Entry from a file. The file will be opened in this process + // 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); + + class Entry : public virtual RefBase, public Parcelable { + public: + Entry(); + virtual ~Entry(); + + virtual status_t writeToParcel(Parcel* out) const; + virtual status_t readFromParcel(const Parcel* in); + + private: + Entry(const String16& tag, int32_t flags); + Entry(const String16& tag, int32_t flags, int fd); + + String16 mTag; + int64_t mTimeMillis; + int32_t mFlags; + + vector<uint8_t> mData; + unique_fd mFd; + + friend class DropBoxManager; + }; + +private: + enum { + HAS_BYTE_ARRAY = 8 + }; + + Status add(const Entry& entry); +}; + +}} // namespace android::os + +#endif // _ANDROID_OS_DROPBOXMANAGER_H + diff --git a/libs/services/src/os/DropBoxManager.cpp b/libs/services/src/os/DropBoxManager.cpp new file mode 100644 index 000000000000..bbb45f022a87 --- /dev/null +++ b/libs/services/src/os/DropBoxManager.cpp @@ -0,0 +1,199 @@ +/* + * 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. + */ + +#define LOG_TAG "DropBoxManager" + +#include <android/os/DropBoxManager.h> + +#include <binder/IServiceManager.h> +#include <com/android/internal/os/IDropBoxManagerService.h> +#include <cutils/log.h> + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + +namespace android { +namespace os { + +using namespace ::com::android::internal::os; + +DropBoxManager::Entry::Entry() + :mTag(), + mTimeMillis(0), + mFlags(IS_EMPTY), + mData(), + mFd() +{ + mFlags = IS_EMPTY; +} + +DropBoxManager::Entry::Entry(const String16& tag, int32_t flags) + :mTag(tag), + mTimeMillis(0), + mFlags(flags), + mData(), + mFd() +{ +} + +DropBoxManager::Entry::Entry(const String16& tag, int32_t flags, int fd) + :mTag(tag), + mTimeMillis(0), + mFlags(flags), + mData(), + mFd(fd) +{ +} + +DropBoxManager::Entry::~Entry() +{ +} + +status_t +DropBoxManager::Entry::writeToParcel(Parcel* out) const +{ + status_t err; + + err = out->writeString16(mTag); + if (err != NO_ERROR) { + return err; + } + + err = out->writeInt64(mTimeMillis); + if (err != NO_ERROR) { + return err; + } + + if (mFd.get() != -1) { + err = out->writeInt32(mFlags & ~HAS_BYTE_ARRAY); // Clear bit just to be safe + if (err != NO_ERROR) { + return err; + } + ALOGD("writing fd %d\n", mFd.get()); + err = out->writeParcelFileDescriptor(mFd); + if (err != NO_ERROR) { + return err; + } + } else { + err = out->writeInt32(mFlags | HAS_BYTE_ARRAY); + if (err != NO_ERROR) { + return err; + } + err = out->writeByteVector(mData); + if (err != NO_ERROR) { + return err; + } + } + return NO_ERROR; +} + +status_t +DropBoxManager::Entry::readFromParcel(const Parcel* in) +{ + status_t err; + + err = in->readString16(&mTag); + if (err != NO_ERROR) { + return err; + } + + err = in->readInt64(&mTimeMillis); + if (err != NO_ERROR) { + return err; + } + + err = in->readInt32(&mFlags); + if (err != NO_ERROR) { + return err; + } + + if ((mFlags & HAS_BYTE_ARRAY) != 0) { + err = in->readByteVector(&mData); + if (err != NO_ERROR) { + return err; + } + mFlags &= ~HAS_BYTE_ARRAY; + } else { + int fd; + fd = in->readParcelFileDescriptor(); + if (fd == -1) { + return EBADF; + } + fd = dup(fd); + if (fd == -1) { + return errno; + } + mFd.reset(fd); + } + + return NO_ERROR; +} + + +DropBoxManager::DropBoxManager() +{ +} + +DropBoxManager::~DropBoxManager() +{ +} + +Status +DropBoxManager::addText(const String16& tag, const string& text) +{ + Entry entry(tag, IS_TEXT); + entry.mData.assign(text.c_str(), text.c_str() + text.size()); + return add(entry); +} + +Status +DropBoxManager::addData(const String16& tag, uint8_t const* data, + size_t size, int flags) +{ + Entry entry(tag, flags); + entry.mData.assign(data, data+size); + return add(entry); +} + +Status +DropBoxManager::addFile(const String16& tag, const string& filename, int flags) +{ + int fd = open(filename.c_str(), O_RDONLY); + if (fd == -1) { + string message("addFile can't open file: "); + message += filename; + ALOGW("DropboxManager: %s", message.c_str()); + return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE, message.c_str()); + } + + Entry entry(tag, flags, fd); + return add(entry); +} + +Status +DropBoxManager::add(const 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->add(entry); +} + +}} // namespace android::os + diff --git a/libs/storage/IMountService.cpp b/libs/storage/IMountService.cpp index 74638e7eccc3..fa3d8bd0930f 100644 --- a/libs/storage/IMountService.cpp +++ b/libs/storage/IMountService.cpp @@ -553,7 +553,7 @@ public: } }; -IMPLEMENT_META_INTERFACE(MountService, "IMountService") +IMPLEMENT_META_INTERFACE(MountService, "android.os.storage.IStorageManager") // ---------------------------------------------------------------------- diff --git a/libs/storage/IMountServiceListener.cpp b/libs/storage/IMountServiceListener.cpp index 11b53fdc1027..033d70d5694f 100644 --- a/libs/storage/IMountServiceListener.cpp +++ b/libs/storage/IMountServiceListener.cpp @@ -24,6 +24,20 @@ enum { TRANSACTION_onStorageStateChanged, }; +class BpMountServiceListener: public BpInterface<IMountServiceListener> { +public: + explicit BpMountServiceListener(const sp<IBinder>& impl) + : BpInterface<IMountServiceListener>(impl) { } + + virtual void onUsbMassStorageConnectionChanged(const bool /* connected */) { } + virtual void onStorageStateChanged(const String16& /* path */, + const String16& /* oldState */, const String16& /* newState */) { } +}; + +IMPLEMENT_META_INTERFACE(MountServiceListener, "android.os.storage.IStorageEventListener") + +// ---------------------------------------------------------------------- + status_t BnMountServiceListener::onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { diff --git a/libs/storage/IMountShutdownObserver.cpp b/libs/storage/IMountShutdownObserver.cpp index a74a768409bc..e5de603cd5ee 100644 --- a/libs/storage/IMountShutdownObserver.cpp +++ b/libs/storage/IMountShutdownObserver.cpp @@ -23,6 +23,16 @@ enum { TRANSACTION_onShutDownComplete = IBinder::FIRST_CALL_TRANSACTION, }; +class BpMountShutdownObserver: public BpInterface<IMountShutdownObserver> { +public: + explicit BpMountShutdownObserver(const sp<IBinder>& impl) + : BpInterface<IMountShutdownObserver>(impl) { } + + virtual void onShutDownComplete(const int32_t /* statusCode */) {} +}; + +IMPLEMENT_META_INTERFACE(MountShutdownObserver, "android.os.storage.IStorageShutdownObserver") + status_t BnMountShutdownObserver::onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { diff --git a/libs/storage/IObbActionListener.cpp b/libs/storage/IObbActionListener.cpp index a71341bc1364..797393ac6f5b 100644 --- a/libs/storage/IObbActionListener.cpp +++ b/libs/storage/IObbActionListener.cpp @@ -34,7 +34,7 @@ public: const int32_t /* state */) { } }; -IMPLEMENT_META_INTERFACE(ObbActionListener, "IObbActionListener") +IMPLEMENT_META_INTERFACE(ObbActionListener, "android.os.storage.IObbActionListener") // ---------------------------------------------------------------------- diff --git a/libs/usb/tests/accessorytest/audio.c b/libs/usb/tests/accessorytest/audio.c index d23d9b3fa5d3..36ee6b81839c 100644 --- a/libs/usb/tests/accessorytest/audio.c +++ b/libs/usb/tests/accessorytest/audio.c @@ -164,7 +164,6 @@ static void* capture_thread(void* arg) static void* play_thread(void* arg) { struct pcm *pcm = arg; - char *buffer; int index, err; fprintf(stderr, "play_thread start\n"); @@ -181,7 +180,6 @@ static void* play_thread(void* arg) fprintf(stderr, "play_thread done\n"); pcm_close(pcm); - free(buffer); return NULL; } |