diff options
Diffstat (limited to 'libs')
284 files changed, 17524 insertions, 15003 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 new file mode 100644 index 000000000000..d501d251f789 --- /dev/null +++ b/libs/androidfw/Android.bp @@ -0,0 +1,74 @@ +// 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. + +// libandroidfw is partially built for the host (used by obbtool, aapt, and others) + +cc_library { + name: "libandroidfw", + host_supported: true, + cflags: [ + "-Wall", + "-Werror", + "-Wunused", + "-Wunreachable-code", + ], + srcs: [ + "Asset.cpp", + "AssetDir.cpp", + "AssetManager.cpp", + "AttributeResolution.cpp", + "LocaleData.cpp", + "misc.cpp", + "ObbFile.cpp", + "ResourceTypes.cpp", + "StreamingZipInflater.cpp", + "TypeWrappers.cpp", + "ZipFileRO.cpp", + "ZipUtils.cpp", + ], + export_include_dirs: ["include"], + target: { + android: { + srcs: [ + "BackupData.cpp", + "BackupHelpers.cpp", + "CursorWindow.cpp", + "DisplayEventDispatcher.cpp", + ], + shared_libs: [ + "libziparchive", + "libbase", + "libbinder", + "liblog", + "libcutils", + "libgui", + "libutils", + "libz", + ], + static: { + enabled: false, + }, + }, + host: { + cflags: ["-DSTATIC_ANDROIDFW_FOR_TOOLS"], + shared: { + enabled: false, + }, + shared_libs: ["libz-host"], + }, + windows: { + enabled: true, + }, + }, +} diff --git a/libs/androidfw/Android.mk b/libs/androidfw/Android.mk index 9636ea72f47d..68c51effd79d 100644 --- a/libs/androidfw/Android.mk +++ b/libs/androidfw/Android.mk @@ -14,73 +14,6 @@ LOCAL_PATH:= $(call my-dir) -# libandroidfw is partially built for the host (used by obbtool, aapt, and others) -# These files are common to host and target builds. - -commonSources := \ - Asset.cpp \ - AssetDir.cpp \ - AssetManager.cpp \ - LocaleData.cpp \ - misc.cpp \ - ObbFile.cpp \ - ResourceTypes.cpp \ - StreamingZipInflater.cpp \ - TypeWrappers.cpp \ - ZipFileRO.cpp \ - ZipUtils.cpp - -deviceSources := \ - $(commonSources) \ - BackupData.cpp \ - BackupHelpers.cpp \ - CursorWindow.cpp \ - DisplayEventDispatcher.cpp - -hostSources := $(commonSources) - -# For the host -# ===================================================== -include $(CLEAR_VARS) - -LOCAL_MODULE:= libandroidfw -LOCAL_MODULE_HOST_OS := darwin linux windows -LOCAL_CFLAGS += -DSTATIC_ANDROIDFW_FOR_TOOLS -LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code -LOCAL_SRC_FILES:= $(hostSources) -LOCAL_C_INCLUDES := external/zlib -LOCAL_C_INCLUDES += $(LOCAL_PATH)/include -LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include - -include $(BUILD_HOST_STATIC_LIBRARY) - - -# For the device -# ===================================================== - -include $(CLEAR_VARS) - -LOCAL_MODULE:= libandroidfw -LOCAL_SRC_FILES:= $(deviceSources) -LOCAL_C_INCLUDES := \ - system/core/include -LOCAL_SHARED_LIBRARIES := \ - libziparchive \ - libbase \ - libbinder \ - liblog \ - libcutils \ - libgui \ - libutils \ - libz - -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) - - # Include subdirectory makefiles # ============================================================ 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/AttributeFinder.h b/libs/androidfw/AttributeFinder.h new file mode 100644 index 000000000000..f281921824e7 --- /dev/null +++ b/libs/androidfw/AttributeFinder.h @@ -0,0 +1,206 @@ +/* + * 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_ATTRIBUTE_FINDER_H +#define ANDROIDFW_ATTRIBUTE_FINDER_H + +#include <utils/KeyedVector.h> + +#include <stdint.h> + +namespace android { + +static inline uint32_t get_package(uint32_t attr) { return attr >> 24; } + +/** + * A helper class to search linearly for the requested + * attribute, maintaining it's position and optimizing for + * the case that subsequent searches will involve an attribute with + * a higher attribute ID. + * + * In the case that a subsequent attribute has a different package ID, + * its resource ID may not be larger than the preceding search, so + * back tracking is supported for this case. This + * back tracking requirement is mainly for shared library + * resources, whose package IDs get assigned at runtime + * and thus attributes from a shared library may + * be out of order. + * + * We make two assumptions about the order of attributes: + * 1) The input has the same sorting rules applied to it as + * the attribute data contained by this class. + * 2) Attributes are grouped by package ID. + * 3) Among attributes with the same package ID, the attributes are + * sorted by increasing resource ID. + * + * Ex: 02010000, 02010001, 010100f4, 010100f5, 0x7f010001, 07f010003 + * + * The total order of attributes (including package ID) can not be linear + * as shared libraries get assigned dynamic package IDs at runtime, which + * may break the sort order established at build time. + */ +template <typename Derived, typename Iterator> +class BackTrackingAttributeFinder { + public: + BackTrackingAttributeFinder(const Iterator& begin, const Iterator& end); + + Iterator Find(uint32_t attr); + + private: + void JumpToClosestAttribute(uint32_t package_id); + void MarkCurrentPackageId(uint32_t package_id); + + 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 framework_start_; + Iterator app_start_; + + // 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) + : 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 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 (current_ == end_) { + current_ = largest_; + } + + 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 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 (!(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; + } + 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); + } + } + + if (current_ > largest_) { + // We've moved past the latest attribute we've seen. + largest_ = current_; + } + + if (attr == prev_attr) { + // We found the attribute we were looking for. + return current_ - 1; + } + } + return end_; +} + +} // namespace android + +#endif // ANDROIDFW_ATTRIBUTE_FINDER_H diff --git a/libs/androidfw/AttributeResolution.cpp b/libs/androidfw/AttributeResolution.cpp new file mode 100644 index 000000000000..00f7a42f10e4 --- /dev/null +++ b/libs/androidfw/AttributeResolution.cpp @@ -0,0 +1,477 @@ +/* + * 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 "AttributeFinder.h" + +#include "androidfw/AttributeResolution.h" +#include "androidfw/ResourceTypes.h" + +#include <android/log.h> +#include <cstdint> + +constexpr bool kDebugStyles = false; + +namespace android { + +class XmlAttributeFinder : public BackTrackingAttributeFinder<XmlAttributeFinder, size_t> { + public: + explicit XmlAttributeFinder(const ResXMLParser* parser) + : BackTrackingAttributeFinder(0, parser != NULL ? 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 = NULL; + 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 != NULL && value.dataType != Res_value::TYPE_NULL) { + indices_idx++; + out_indices[indices_idx] = ii; + } + + out_values += STYLE_NUM_ENTRIES; + } + + res.unlock(); + + if (out_indices != NULL) { + out_indices[0] = indices_idx; + } + return true; +} + +bool ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, + uint32_t def_style_res, 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 != NULL) { + 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 = NULL; + 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 = NULL; + 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 != NULL ? 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 (out_indices != NULL && value.dataType != Res_value::TYPE_NULL) { + indices_idx++; + out_indices[indices_idx] = ii; + } + + out_values += STYLE_NUM_ENTRIES; + } + + res.unlock(); + + if (out_indices != NULL) { + out_indices[0] = indices_idx; + } + return true; +} + +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 != NULL && value.dataType != Res_value::TYPE_NULL) { + indices_idx++; + out_indices[indices_idx] = ii; + } + + out_values += STYLE_NUM_ENTRIES; + } + + res->unlock(); + + if (out_indices != NULL) { + out_indices[0] = indices_idx; + } + return true; +} + +} // namespace android diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp index e10db05e8557..907d9145f4ea 100644 --- a/libs/androidfw/ResourceTypes.cpp +++ b/libs/androidfw/ResourceTypes.cpp @@ -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)); @@ -5881,12 +5919,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/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/AttributeFinder.h b/libs/androidfw/include/androidfw/AttributeFinder.h deleted file mode 100644 index acf70565c4f9..000000000000 --- a/libs/androidfw/include/androidfw/AttributeFinder.h +++ /dev/null @@ -1,206 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef H_ATTRIBUTE_FINDER -#define H_ATTRIBUTE_FINDER - -#include <stdint.h> -#include <utils/KeyedVector.h> - -namespace android { - -static inline uint32_t getPackage(uint32_t attr) { - return attr >> 24; -} - -/** - * A helper class to search linearly for the requested - * attribute, maintaining it's position and optimizing for - * the case that subsequent searches will involve an attribute with - * a higher attribute ID. - * - * In the case that a subsequent attribute has a different package ID, - * its resource ID may not be larger than the preceding search, so - * back tracking is supported for this case. This - * back tracking requirement is mainly for shared library - * resources, whose package IDs get assigned at runtime - * and thus attributes from a shared library may - * be out of order. - * - * We make two assumptions about the order of attributes: - * 1) The input has the same sorting rules applied to it as - * the attribute data contained by this class. - * 2) Attributes are grouped by package ID. - * 3) Among attributes with the same package ID, the attributes are - * sorted by increasing resource ID. - * - * Ex: 02010000, 02010001, 010100f4, 010100f5, 0x7f010001, 07f010003 - * - * The total order of attributes (including package ID) can not be linear - * as shared libraries get assigned dynamic package IDs at runtime, which - * may break the sort order established at build time. - */ -template <typename Derived, typename Iterator> -class BackTrackingAttributeFinder { -public: - BackTrackingAttributeFinder(const Iterator& begin, const Iterator& end); - - Iterator find(uint32_t attr); - -private: - void jumpToClosestAttribute(uint32_t packageId); - void markCurrentPackageId(uint32_t packageId); - - bool mFirstTime; - Iterator mBegin; - Iterator mEnd; - Iterator mCurrent; - Iterator mLargest; - uint32_t mLastPackageId; - uint32_t mCurrentAttr; - - // Package Offsets (best-case, fast look-up). - Iterator mFrameworkStart; - Iterator mAppStart; - - // Worst case, we have shared-library resources. - KeyedVector<uint32_t, Iterator> mPackageOffsets; -}; - -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> -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; - } - } - - // 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; - } - - if (mCurrent != mEnd) { - mCurrentAttr = static_cast<const Derived*>(this)->getAttribute(mCurrent); - } -} - -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; - } -} - -template <typename Derived, typename Iterator> -Iterator BackTrackingAttributeFinder<Derived, Iterator>::find(uint32_t attr) { - if (!(mBegin < mEnd)) { - return mEnd; - } - - 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); - } - - // 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; - } - - // 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; - } - } - return mEnd; -} - -} // namespace android - -#endif // H_ATTRIBUTE_FINDER diff --git a/libs/androidfw/include/androidfw/AttributeResolution.h b/libs/androidfw/include/androidfw/AttributeResolution.h new file mode 100644 index 000000000000..3ed8bced22ef --- /dev/null +++ b/libs/androidfw/include/androidfw/AttributeResolution.h @@ -0,0 +1,56 @@ +/* + * 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. + +bool ResolveAttrs(ResTable::Theme* theme, uint32_t def_style_attr, uint32_t def_style_res, + uint32_t* src_values, size_t src_values_length, uint32_t* attrs, + size_t attrs_length, uint32_t* out_values, uint32_t* out_indices); + +bool ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, + uint32_t def_style_res, uint32_t* attrs, size_t attrs_length, uint32_t* out_values, + uint32_t* out_indices); + +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/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h index 12a6b0f9a4ec..08d6591e6886 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> @@ -1227,7 +1226,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 +1851,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/tests/Android.mk b/libs/androidfw/tests/Android.mk index 1fe1773578fa..d18cb8f472c7 100644 --- a/libs/androidfw/tests/Android.mk +++ b/libs/androidfw/tests/Android.mk @@ -28,6 +28,7 @@ testFiles := \ Config_test.cpp \ ConfigLocale_test.cpp \ Idmap_test.cpp \ + Main.cpp \ ResTable_test.cpp \ Split_test.cpp \ TestHelpers.cpp \ @@ -40,7 +41,7 @@ androidfw_test_cflags := \ -Werror \ -Wunused \ -Wunreachable-code \ - -Wno-missing-field-initializers \ + -Wno-missing-field-initializers # gtest is broken. androidfw_test_cflags += -Wno-unnamed-type-template-args @@ -52,13 +53,15 @@ include $(CLEAR_VARS) LOCAL_MODULE := libandroidfw_tests LOCAL_CFLAGS := $(androidfw_test_cflags) -LOCAL_SRC_FILES := $(testFiles) +LOCAL_SRC_FILES := $(testFiles) AttributeResolution_test.cpp LOCAL_STATIC_LIBRARIES := \ libandroidfw \ + libbase \ libutils \ libcutils \ liblog \ - libz \ + libz +LOCAL_PICKUP_FILES := $(LOCAL_PATH)/data include $(BUILD_HOST_NATIVE_TEST) @@ -76,9 +79,11 @@ LOCAL_SRC_FILES := $(testFiles) \ LOCAL_SHARED_LIBRARIES := \ libandroidfw \ + libbase \ libcutils \ libutils \ - libui \ + libui +LOCAL_PICKUP_FILES := $(LOCAL_PATH)/data include $(BUILD_NATIVE_TEST) endif # Not SDK_ONLY diff --git a/libs/androidfw/tests/AttributeFinder_test.cpp b/libs/androidfw/tests/AttributeFinder_test.cpp index 5054624579ea..d9ed48ebe953 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,105 @@ * limitations under the License. */ -#include <androidfw/AttributeFinder.h> +#include "../AttributeFinder.h" +#include <android-base/macros.h> #include <gtest/gtest.h> using android::BackTrackingAttributeFinder; 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); - } - - ~MockAttributeFinder() { - delete mAttrs; - } - - inline uint32_t getAttribute(const int index) const { - return mAttrs[index]; - } - -private: - uint32_t* mAttrs; -}; + public: + MockAttributeFinder(const uint32_t* attrs, int len) : BackTrackingAttributeFinder(0, len) { + attrs_ = new uint32_t[len]; + memcpy(attrs_, attrs, sizeof(*attrs) * len); + } -static const uint32_t sortedAttributes[] = { - 0x01010000, 0x01010001, 0x01010002, 0x01010004, - 0x02010001, 0x02010010, 0x7f010001 -}; + ~MockAttributeFinder() { delete attrs_; } -static const uint32_t packageUnsortedAttributes[] = { - 0x02010001, 0x02010010, 0x01010000, 0x01010001, - 0x01010002, 0x01010004, 0x7f010001 -}; + inline uint32_t GetAttribute(const int index) const { return attrs_[index]; } -static const uint32_t singlePackageAttributes[] = { - 0x7f010007, 0x7f01000a, 0x7f01000d, 0x00000000 + private: + uint32_t* attrs_; }; +static const uint32_t kSortedAttributes[] = {0x01010000, 0x01010001, 0x01010002, 0x01010004, + 0x02010001, 0x02010010, 0x7f010001}; + +static const uint32_t kPackageUnsortedAttributes[] = { + 0x02010001, 0x02010010, 0x01010000, 0x01010001, 0x01010002, 0x01010004, 0x7f010001}; + +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)); } diff --git a/libs/androidfw/tests/AttributeResolution_test.cpp b/libs/androidfw/tests/AttributeResolution_test.cpp new file mode 100644 index 000000000000..d6d7890882a8 --- /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 "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 test_source_dir = GetTestDataPath(); + std::string contents; + CHECK(base::ReadFileToString(test_source_dir + "/styles/resources.arsc", + &contents)); + CHECK(table_.add(contents.data(), contents.size(), 1 /*cookie*/, + true /*copyData*/) == NO_ERROR); + } + + protected: + ResTable table_; +}; + +class AttributeResolutionXmlTest : public AttributeResolutionTest { + public: + virtual void SetUp() override { + AttributeResolutionTest::SetUp(); + std::string test_source_dir = GetTestDataPath(); + std::string contents; + CHECK(base::ReadFileToString(test_source_dir + "/styles/layout.xml", + &contents)); + CHECK(xml_parser_.setTo(contents.data(), contents.size(), + true /*copyData*/) == NO_ERROR); + + // 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)); + + uint32_t attrs[] = {R::attr::attr_one, R::attr::attr_two, R::attr::attr_three, + R::attr::attr_four}; + std::vector<uint32_t> values; + values.resize(arraysize(attrs) * 6); + + ASSERT_TRUE(ResolveAttrs(&theme, 0 /*def_style_attr*/, 0 /*def_style_res*/, + nullptr /*src_values*/, 0 /*src_values_length*/, + attrs, arraysize(attrs), 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) { + uint32_t attrs[] = {R::attr::attr_one, R::attr::attr_two, R::attr::attr_three, + R::attr::attr_four}; + std::vector<uint32_t> values; + values.resize(arraysize(attrs) * 6); + + ASSERT_TRUE(RetrieveAttributes(&table_, &xml_parser_, attrs, arraysize(attrs), + 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)); + + uint32_t attrs[] = {R::attr::attr_one, R::attr::attr_two, R::attr::attr_three, + R::attr::attr_four, R::attr::attr_five}; + std::vector<uint32_t> values; + values.resize(arraysize(attrs) * 6); + + ASSERT_TRUE(ApplyStyle(&theme, &xml_parser_, 0 /*def_style_attr*/, + 0 /*def_style_res*/, attrs, arraysize(attrs), + values.data(), nullptr /*out_indices*/)); + + 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/ConfigLocale_test.cpp b/libs/androidfw/tests/ConfigLocale_test.cpp index 2bf9b12b6ce5..6e998159e554 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/Main.cpp b/libs/androidfw/tests/Main.cpp new file mode 100644 index 000000000000..6a50691e6bb6 --- /dev/null +++ b/libs/androidfw/tests/Main.cpp @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <libgen.h> + +#include <iostream> +#include <memory> +#include <string> + +#include "android-base/file.h" +#include "android-base/strings.h" +#include "gtest/gtest.h" + +#include "TestHelpers.h" + +// Extract the directory of the current executable path. +static std::string GetExecutableDir() { + const std::string path = android::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; +} + +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + + // Set the default test data path to be the executable path directory. + android::SetTestDataPath(GetExecutableDir()); + + const char* command = argv[0]; + ++argv; + --argc; + + while (argc > 0) { + const std::string arg = *argv; + if (android::base::StartsWith(arg, "--testdata=")) { + android::SetTestDataPath(arg.substr(strlen("--testdata="))); + } 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"; + return 1; + } else { + std::cerr << command << ": Unrecognized argument '" << *argv << "'.\n"; + return 1; + } + + --argc; + ++argv; + } + + std::cerr << "using --testdata=" << android::GetTestDataPath() << "\n"; + return RUN_ALL_TESTS(); +} diff --git a/libs/androidfw/tests/TestHelpers.cpp b/libs/androidfw/tests/TestHelpers.cpp index 41a19a7f2b24..702ee5c9d4ad 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,45 @@ #include "TestHelpers.h" -#include <androidfw/ResourceTypes.h> -#include <utils/String8.h> -#include <gtest/gtest.h> +#include <unistd.h> + +#include "android-base/logging.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"; - } - - 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 = pool->string8ObjectAt(val.data); - if (String8(expectedStr) != actual) { - return ::testing::AssertionFailure() << actual.string(); - } - return ::testing::AssertionSuccess() << actual.string(); +static std::string sTestDataPath; + +void SetTestDataPath(const std::string& path) { sTestDataPath = path; } + +const std::string& GetTestDataPath() { + CHECK(!sTestDataPath.empty()) << "no test data path set."; + return sTestDataPath; +} + +::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(); } -} // namespace android +} // namespace android diff --git a/libs/androidfw/tests/TestHelpers.h b/libs/androidfw/tests/TestHelpers.h index ff9be164dbb0..c1e349fb824f 100644 --- a/libs/androidfw/tests/TestHelpers.h +++ b/libs/androidfw/tests/TestHelpers.h @@ -1,35 +1,62 @@ -#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(); +static inline ::std::ostream& operator<<(::std::ostream& out, + const android::String8& str) { + return out << str.string(); } -static inline ::std::ostream& operator<<(::std::ostream& out, const android::String16& str) { - return out << android::String8(str).string(); +static inline ::std::ostream& operator<<(::std::ostream& out, + const android::String16& str) { + return out << android::String8(str).string(); } namespace android { 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(); + +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); -} // namespace android +} // namespace android -#endif // __TEST_HELPERS_H +#endif // TEST_HELPERS_H_ 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/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..6dc6edec64ab --- /dev/null +++ b/libs/androidfw/tests/data/styles/R.h @@ -0,0 +1,35 @@ +#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, + }; + }; + + struct string { + enum : uint32_t { + string_one = 0x7f030000u, + }; + }; + + struct style { + enum : uint32_t { + StyleOne = 0x7f020000u, + StyleTwo = 0x7f020001u, + }; + }; +}; + +} // namespace app +} // namespace android +} // namespace com diff --git a/libs/androidfw/tests/data/styles/build.sh b/libs/androidfw/tests/data/styles/build.sh new file mode 100755 index 000000000000..e763421626cf --- /dev/null +++ b/libs/androidfw/tests/data/styles/build.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +set -e + +aapt package -F package.apk -M AndroidManifest.xml -S res +unzip -j package.apk resources.arsc res/layout/layout.xml +rm package.apk diff --git a/libs/androidfw/tests/data/styles/layout.xml b/libs/androidfw/tests/data/styles/layout.xml Binary files differnew file mode 100644 index 000000000000..4997e71dce6d --- /dev/null +++ b/libs/androidfw/tests/data/styles/layout.xml 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..70c54f6c4b96 --- /dev/null +++ b/libs/androidfw/tests/data/styles/res/values/styles.xml @@ -0,0 +1,52 @@ +<?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> + </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> + +</resources> diff --git a/libs/androidfw/tests/data/styles/resources.arsc b/libs/androidfw/tests/data/styles/resources.arsc Binary files differnew file mode 100644 index 000000000000..8f65c9ad1507 --- /dev/null +++ b/libs/androidfw/tests/data/styles/resources.arsc diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk index a7cbf5e562d1..cf571e97c1f9 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,8 +38,10 @@ 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 \ @@ -37,7 +50,6 @@ hwui_src_files := \ utils/Blur.cpp \ utils/GLUtils.cpp \ utils/LinearAllocator.cpp \ - utils/NinePatchImpl.cpp \ utils/StringUtils.cpp \ utils/TestWindowContext.cpp \ utils/VectorDrawableUtils.cpp \ @@ -45,20 +57,20 @@ 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 \ @@ -69,23 +81,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 \ @@ -105,10 +118,20 @@ hwui_src_files := \ 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 +141,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 +162,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 +180,8 @@ endef hwui_c_includes += \ external/skia/include/private \ external/skia/src/core \ + external/skia/src/effects \ + external/skia/src/image \ external/harfbuzz_ng/src \ external/freetype/include @@ -172,14 +193,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 +203,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 +225,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 +264,54 @@ 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/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 +323,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 +339,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/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..b463e45fb39b 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 "Layer.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,15 @@ 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.appendFormat(" Layer size %dx%d; texid=%u refs=%d\n", layer->getWidth(), layer->getHeight(), - layer->isTextureLayer(), layer->getTextureId(), - layer->getFbo(), layer->getStrongCount()); + layer->getTextureId(), + layer->getStrongCount()); memused += layer->getWidth() * layer->getHeight() * 4; } log.appendFormat(" Layers total %8d (numLayers = %zu)\n", @@ -242,7 +237,6 @@ void Caches::flush(FlushMode mode) { gradientCache.clear(); fontRenderer.clear(); fboCache.clear(); - dither.clear(); // fall through case FlushMode::Moderate: fontRenderer.flush(); @@ -251,7 +245,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..7e2c28c5eb41 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) @@ -205,19 +204,16 @@ void CanvasState::concatMatrix(const Matrix4& matrix) { bool CanvasState::clipRect(float left, float top, float right, float bottom, SkRegion::Op op) { mSnapshot->clip(Rect(left, top, right, bottom), op); - mDirtyClip = true; return !mSnapshot->clipIsEmpty(); } bool CanvasState::clipPath(const SkPath* path, SkRegion::Op 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(); } @@ -236,15 +232,6 @@ void CanvasState::setClippingOutline(LinearAllocator& allocator, const Outline* } } -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 +250,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 +278,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..22c7e8a96cc8 100644 --- a/libs/hwui/CanvasState.h +++ b/libs/hwui/CanvasState.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef ANDROID_HWUI_CANVAS_STATE_H -#define ANDROID_HWUI_CANVAS_STATE_H +#pragma once #include "Snapshot.h" @@ -62,11 +61,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. */ @@ -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/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..a7d5f6026d00 100644 --- a/libs/hwui/DeferredLayerUpdater.cpp +++ b/libs/hwui/DeferredLayerUpdater.cpp @@ -15,11 +15,9 @@ */ #include "DeferredLayerUpdater.h" -#include "OpenGLRenderer.h" - -#include "LayerRenderer.h" #include "renderthread/EglManager.h" #include "renderthread/RenderTask.h" +#include "utils/PaintUtils.h" namespace android { namespace uirenderer { @@ -29,10 +27,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,13 +45,12 @@ 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); @@ -110,8 +106,22 @@ 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::updateLayer(bool forceFilter, GLenum renderTarget, + const float* textureTransform) { + mLayer->setBlend(mBlend); + mLayer->setForceFilter(forceFilter); + mLayer->setSize(mWidth, mHeight); + mLayer->getTexTransform().load(textureTransform); + + if (renderTarget != mLayer->getRenderTarget()) { + mLayer->setRenderTarget(renderTarget); + mLayer->bindTexture(); + mLayer->setFilter(GL_NEAREST, false, true); + mLayer->setWrap(GL_CLAMP_TO_EDGE, false, true); } } diff --git a/libs/hwui/DeferredLayerUpdater.h b/libs/hwui/DeferredLayerUpdater.h index 40058223fb48..733500885e67 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> @@ -92,6 +92,8 @@ public: void detachSurfaceTexture(); + void updateLayer(bool forceFilter, GLenum renderTarget, const float* textureTransform); + private: // Generic properties int mWidth; @@ -99,20 +101,16 @@ 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(); }; } /* namespace uirenderer */ } /* namespace android */ - -#endif /* DEFERREDLAYERUPDATE_H_ */ diff --git a/libs/hwui/DeviceInfo.cpp b/libs/hwui/DeviceInfo.cpp index 4cfbb2a43198..700642ed7334 100644 --- a/libs/hwui/DeviceInfo.cpp +++ b/libs/hwui/DeviceInfo.cpp @@ -41,6 +41,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..9df73387c36a 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 pre-multiplied linear color + // if linear blending is enabled. + void setSRGB(uint32_t color) { + a = ((color >> 24) & 0xff) / 255.0f; + r = a * EOCF_sRGB(((color >> 16) & 0xff) / 255.0f); + g = a * EOCF_sRGB(((color >> 8) & 0xff) / 255.0f); + b = a * EOCF_sRGB(((color ) & 0xff) / 255.0f); } bool isNotBlack() { diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp index 681cf55066b4..effc65ea967f 100644 --- a/libs/hwui/FontRenderer.cpp +++ b/libs/hwui/FontRenderer.cpp @@ -16,6 +16,9 @@ #include "FontRenderer.h" +#include "BakedOpDispatcher.h" +#include "BakedOpRenderer.h" +#include "BakedOpState.h" #include "Caches.h" #include "Debug.h" #include "Extensions.h" @@ -27,15 +30,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 +60,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 +293,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..245db1dcec97 100644 --- a/libs/hwui/FrameBuilder.cpp +++ b/libs/hwui/FrameBuilder.cpp @@ -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/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..34e6d39c9104 100644 --- a/libs/hwui/GlopBuilder.cpp +++ b/libs/hwui/GlopBuilder.cpp @@ -17,8 +17,10 @@ #include "Caches.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 +167,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 +220,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 +246,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 +262,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 +271,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 +289,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 +308,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 +320,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 +329,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 +351,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 +372,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 +389,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 +399,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 +411,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 +421,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); @@ -467,7 +445,7 @@ GlopBuilder& GlopBuilder::setFillTextureLayer(Layer& layer, float alpha) { 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 +459,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 +469,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 +497,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 +520,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 +527,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 +546,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 +599,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 +645,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 +664,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 +685,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..8a8b652c393c 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" @@ -30,9 +29,13 @@ namespace uirenderer { class Caches; class Matrix4; +class Patch; class RenderState; class Texture; +class UvMapper; class VertexBuffer; +struct PathTexture; +struct ShadowTexture; namespace TextureFillFlags { enum { @@ -53,7 +56,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 +70,12 @@ public: GlopBuilder& setFillBlack(); GlopBuilder& setFillClear(); GlopBuilder& setFillLayer(Texture& texture, const SkColorFilter* colorFilter, - float alpha, SkXfermode::Mode mode, Blend::ModeOrderSwap modeUsage); + float alpha, SkBlendMode 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 + // 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 +104,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 +133,3 @@ private: } /* namespace uirenderer */ } /* namespace android */ - -#endif // RENDERSTATE_GLOPBUILDER_H diff --git a/libs/hwui/GradientCache.cpp b/libs/hwui/GradientCache.cpp index c8f5e9435594..0972ac1cafc2 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,56 @@ 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); + *dst++ = uint8_t(OECF_sRGB(start.r * oppAmount + end.r * amount) * 255.0f); + *dst++ = uint8_t(OECF_sRGB(start.g * oppAmount + end.g * amount) * 255.0f); + *dst++ = uint8_t(OECF_sRGB(start.b * oppAmount + end.b * amount) * 255.0f); + *dst++ = uint8_t( (start.a * oppAmount + end.a * amount) * 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* d = (float*) dst; - *d++ = a * (start.r * oppAmount + end.r * amount); - *d++ = a * (start.g * oppAmount + end.g * amount); - *d++ = a * (start.b * oppAmount + end.b * amount); - *d++ = a; - +#ifdef ANDROID_ENABLE_LINEAR_BLENDING + *d++ = start.r * oppAmount + end.r * amount; + *d++ = start.g * oppAmount + end.g * amount; + *d++ = start.b * oppAmount + end.b * amount; +#else + *d++ = OECF_sRGB(start.r * oppAmount + end.r * amount); + *d++ = OECF_sRGB(start.g * oppAmount + end.g * amount); + *d++ = OECF_sRGB(start.b * oppAmount + end.b * amount); +#endif + *d++ = start.a * oppAmount + end.a * amount; 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[] = { - &android::uirenderer::GradientCache::mixBytes, - &android::uirenderer::GradientCache::mixFloats, + &android::uirenderer::GradientCache::mixBytes, // colors are stored gamma-encoded + &android::uirenderer::GradientCache::mixFloats, // colors are stored in linear }; ChannelMixer mix = gMixers[mUseFloatTexture]; - GradientColor start; - (this->*split)(colors[0], start); + FloatColor start; + start.setSRGB(colors[0]); - GradientColor end; - (this->*split)(colors[1], end); + FloatColor end; + end.setSRGB(colors[1]); int currentPos = 1; float startPos = positions[0]; @@ -255,7 +241,7 @@ void GradientCache::generateTexture(uint32_t* colors, float* positions, currentPos++; - (this->*split)(colors[currentPos], end); + end.setSRGB(colors[currentPos]); distance = positions[currentPos] - startPos; } @@ -266,10 +252,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 bddb01b97865..f94a22d51d9f 100644 --- a/libs/hwui/Interpolator.cpp +++ b/libs/hwui/Interpolator.cpp @@ -88,6 +88,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..88817efa80ac 100644 --- a/libs/hwui/Layer.cpp +++ b/libs/hwui/Layer.cpp @@ -17,9 +17,6 @@ #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" @@ -35,17 +32,15 @@ namespace android { namespace uirenderer { -Layer::Layer(Type layerType, RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight) +Layer::Layer(RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight) : GpuMemoryTracker(GpuObjectType::Layer) , state(State::Uncached) , caches(Caches::getInstance()) , renderState(renderState) - , texture(caches) - , type(layerType) { + , texture(caches) { // 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); @@ -55,138 +50,22 @@ Layer::~Layer() { renderState.unregisterLayer(this); SkSafeUnref(colorFilter); - if (stencil || fbo || texture.mId) { - removeFbo(); + if (texture.mId) { 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); -} - 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(); + caches.textureState().bindTexture(texture.target(), texture.mId); } } @@ -206,86 +85,6 @@ void Layer::clearTexture() { 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); } diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h index 1e5498bb3d21..8e71cd11599d 100644 --- a/libs/hwui/Layer.h +++ b/libs/hwui/Layer.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef ANDROID_HWUI_LAYER_H -#define ANDROID_HWUI_LAYER_H +#pragma once #include <cutils/compiler.h> #include <sys/types.h> @@ -29,7 +28,7 @@ #include <ui/Region.h> #include <SkPaint.h> -#include <SkXfermode.h> +#include <SkBlendMode.h> #include "Matrix.h" #include "Rect.h" @@ -46,22 +45,13 @@ namespace uirenderer { // 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. */ class Layer : public VirtualLightRefBase, GpuMemoryTracker { public: - enum class Type { - Texture, - DisplayList, - }; - // layer lifecycle, controlled from outside enum class State { Uncached = 0, @@ -73,45 +63,9 @@ public: }; State state; // public for logging/debugging purposes - Layer(Type type, RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight); + Layer(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; } @@ -120,23 +74,11 @@ public: return texture.mHeight; } - /** - * 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); - void setSize(uint32_t width, uint32_t height) { - texture.updateSize(width, height, texture.format()); + texture.updateSize(width, height, texture.internalFormat(), texture.format(), + texture.target()); } - ANDROID_API void setPaint(const SkPaint* paint); - inline void setBlend(bool blend) { texture.blend = blend; } @@ -157,7 +99,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,40 +108,10 @@ 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(); } @@ -209,71 +121,40 @@ public: } inline GLenum getRenderTarget() const { - return renderTarget; + return texture.target(); } inline void setRenderTarget(GLenum renderTarget) { - this->renderTarget = renderTarget; + texture.mTarget = renderTarget; } inline bool isRenderable() const { - return renderTarget != GL_NONE; + return texture.target() != GL_NONE; } void setWrap(GLenum wrap, bool bindTexture = false, bool force = false) { - texture.setWrap(wrap, bindTexture, force, renderTarget); + texture.setWrap(wrap, bindTexture, force); } 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; + texture.setFilter(filter, bindTexture, force); } 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 setColorFilter(SkColorFilter* filter); 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 clearTexture(); inline mat4& getTexTransform() { return texTransform; @@ -283,11 +164,6 @@ 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. @@ -300,98 +176,17 @@ public: */ void onGlContextLost(); - /** - * 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; - 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; - - /** * Color filter used to draw this layer. Optional. */ SkColorFilter* colorFilter = nullptr; @@ -409,7 +204,7 @@ private: /** * Blending mode of the layer. */ - SkXfermode::Mode mode = SkXfermode::kSrcOver_Mode; + SkBlendMode mode = SkBlendMode::kSrcOver; /** * Optional texture coordinates transform. @@ -421,28 +216,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..408159b8f33a 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 "Layer.h" #include "renderstate/RenderState.h" #include "renderthread/EglManager.h" #include "utils/GLUtils.h" @@ -30,14 +31,82 @@ 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; + } + + CopyResult copyResult = copyImageInto(sourceImage, texTransform, graphicBuffer->getWidth(), + graphicBuffer->getHeight(), 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); +} + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +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 @@ -98,64 +167,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 +174,21 @@ CopyResult Readback::copySurfaceInto(renderthread::RenderThread& renderThread, renderState.blend().syncEnabled(); renderState.stencil().disable(); - Rect destRect(destWidth, destHeight); + Matrix4 croppedTexTransform(texTransform); + if (!srcRect.isEmpty()) { + croppedTexTransform.loadTranslate(srcRect.left / sourceTexture.width(), + srcRect.top / sourceTexture.height(), 0); + croppedTexTransform.scale(srcRect.getWidth() / sourceTexture.width(), + srcRect.getHeight() / sourceTexture.height(), 1); + croppedTexTransform.multiply(texTransform); + } 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 +202,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, + Layer& 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..f4ebabcdebe5 --- /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 Layer; + +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, Layer& 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..52c62cc34670 100644 --- a/libs/hwui/PatchCache.cpp +++ b/libs/hwui/PatchCache.cpp @@ -43,21 +43,6 @@ 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 +65,7 @@ void PatchCache::clear() { clearCache(); if (mMeshBuffer) { - mRenderState.meshState().unbindMeshBuffer(); - glDeleteBuffers(1, &mMeshBuffer); + mRenderState.meshState().deleteMeshBuffer(mMeshBuffer); mMeshBuffer = 0; mSize = 0; } @@ -170,7 +154,8 @@ 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 +167,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 +202,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 +225,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..0624c355332c 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(); @@ -187,5 +184,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 bbef36b72e4f..9536bc807fbc 100644 --- a/libs/hwui/PixelBuffer.h +++ b/libs/hwui/PixelBuffer.h @@ -99,12 +99,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. @@ -199,8 +193,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..9c4cb098b4c3 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" @@ -85,6 +85,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 +133,7 @@ struct ProgramDescription { // Shaders bool hasBitmap; - bool isBitmapNpot; + bool useShaderBasedWrap; bool hasVertexAlpha; bool useShadowAlphaInterp; @@ -140,7 +142,7 @@ struct ProgramDescription { Gradient gradientType; bool isSimpleGradient; - SkXfermode::Mode shadersMode; + SkBlendMode shadersMode; bool isBitmapFirst; GLenum bitmapWrapS; @@ -148,16 +150,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 +180,28 @@ struct ProgramDescription { modulate = false; hasBitmap = false; - isBitmapNpot = 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,7 +234,7 @@ 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; @@ -238,7 +244,7 @@ struct ProgramDescription { 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 +252,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 +268,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..0c2309faf4ea 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,12 +135,11 @@ 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" }; @@ -172,18 +159,57 @@ 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" + "\nvec4 gammaMix(const vec4 a, const vec4 b, float v) {\n" + " return pow(mix(a, b, v), vec4(vec3(1.0 / 2.2), 1.0));" + "}\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" + " return mix(a, b, v);" + "}\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 +228,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 +273,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 +307,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 +430,8 @@ const char* gBlendOps[18] = { /////////////////////////////////////////////////////////////////////////////// ProgramCache::ProgramCache(Extensions& extensions) - : mHasES3(extensions.getMajorGlVersion() >= 3) { + : mHasES3(extensions.getMajorGlVersion() >= 3) + , mHasSRGB(extensions.hasSRGB()) { } ProgramCache::~ProgramCache() { @@ -518,6 +564,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,7 +572,7 @@ 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); } @@ -570,13 +617,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 +657,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; } @@ -649,9 +707,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 +721,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 +734,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 +777,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 +809,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 6f68c2bdff80..848161e44604 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,27 @@ 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; +} + +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..b4a311864938 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,9 +307,12 @@ 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 @@ -306,6 +321,7 @@ public: private: static ProfileType sProfileType; static bool sDisableProfileBars; + static RenderPipelineType sRenderPipelineType; }; // class Caches 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..f9a7c36f2786 100644 --- a/libs/hwui/RecordedOp.h +++ b/libs/hwui/RecordedOp.h @@ -14,20 +14,18 @@ * 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 "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 +211,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 +226,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 +234,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 +256,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 +288,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; }; @@ -506,7 +503,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 +517,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 +526,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..9d18484f39f0 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() { @@ -247,7 +247,7 @@ bool RecordingCanvas::clipRegion(const SkRegion* region, SkRegion::Op 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 +468,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 +492,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 +508,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 +520,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 +532,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 +559,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) { @@ -660,7 +666,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..f93e8b892e81 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 @@ -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..6786a69c8dd1 100644 --- a/libs/hwui/SkiaCanvas.cpp +++ b/libs/hwui/SkiaCanvas.cpp @@ -14,181 +14,32 @@ * 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 +49,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; } // ---------------------------------------------------------------------------- @@ -235,11 +103,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 +146,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 +161,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) { @@ -353,13 +208,62 @@ int SkiaCanvas::saveLayer(float left, float top, float right, float bottom, 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, SkRegion::Op op, const SkMatrix& m) + : mType(Type::Rect), mOp(op), mMatrix(m), mRRect(SkRRect::MakeRect(rect)) {} + Clip(const SkRRect& rrect, SkRegion::Op op, const SkMatrix& m) + : mType(Type::RRect), mOp(op), mMatrix(m), mRRect(rrect) {} + Clip(const SkPath& path, SkRegion::Op 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; + SkRegion::Op 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 +282,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, SkRegion::Op 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); + } } // ---------------------------------------------------------------------------- @@ -492,12 +399,20 @@ bool SkiaCanvas::quickRejectPath(const SkPath& path) const { bool SkiaCanvas::clipRect(float left, float top, float right, float bottom, SkRegion::Op 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); + SkRRect roundRect; + if (path->isRRect(&roundRect)) { + this->recordClip(roundRect, op); + mCanvas->clipRRect(roundRect, op); + } else { + this->recordClip(*path, op); + mCanvas->clipPath(*path, op); + } return !mCanvas->isClipEmpty(); } @@ -507,10 +422,13 @@ bool SkiaCanvas::clipRegion(const SkRegion* region, SkRegion::Op op) { // The region is specified in device space. SkMatrix savedMatrix = mCanvas->getTotalMatrix(); mCanvas->resetMatrix(); + this->recordClip(rgnPath, op); mCanvas->clipPath(rgnPath, op); mCanvas->setMatrix(savedMatrix); } else { - mCanvas->clipRect(SkRect::MakeEmpty(), op); + const auto emptyClip = SkRect::MakeEmpty(); + this->recordClip(emptyClip, op); + mCanvas->clipRect(emptyClip, op); } return !mCanvas->isClipEmpty(); } @@ -531,7 +449,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 +463,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 +489,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 +551,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 +670,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 +718,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 +791,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..4f1d8572eb2d --- /dev/null +++ b/libs/hwui/SkiaCanvas.h @@ -0,0 +1,193 @@ +/* + * 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, + 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, 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&, SkRegion::Op); + 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 9df32b28bf3b..5978abc9efaa 100644 --- a/libs/hwui/SkiaCanvasProxy.cpp +++ b/libs/hwui/SkiaCanvasProxy.cpp @@ -16,6 +16,8 @@ #include "SkiaCanvasProxy.h" +#include "hwui/Bitmap.h" + #include <cutils/log.h> #include <SkPatchUtils.h> #include <SkPaint.h> @@ -23,6 +25,9 @@ #include <SkPixelRef.h> #include <SkRect.h> #include <SkRRect.h> +#include <SkRSXform.h> +#include <SkSurface.h> +#include <SkTextBlobRunIterator.h> #include <memory> @@ -103,16 +108,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, @@ -120,15 +121,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,8 +141,9 @@ void SkiaCanvasProxy::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& ce } 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; } @@ -153,7 +156,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; } @@ -322,9 +325,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); @@ -347,18 +350,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; } @@ -372,7 +425,7 @@ 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); } } diff --git a/libs/hwui/SkiaCanvasProxy.h b/libs/hwui/SkiaCanvasProxy.h index c03c523cd106..01118150a8f0 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; @@ -67,7 +67,7 @@ protected: virtual void onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst, const SkPaint*) override; 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,11 +81,13 @@ 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; diff --git a/libs/hwui/SkiaShader.cpp b/libs/hwui/SkiaShader.cpp index 6f4a6839be4e..34e6a06c3dc2 100644 --- a/libs/hwui/SkiaShader.cpp +++ b/libs/hwui/SkiaShader.cpp @@ -21,6 +21,7 @@ #include "Layer.h" #include "Matrix.h" #include "Texture.h" +#include "hwui/Bitmap.h" #include <SkMatrix.h> #include <utils/Log.h> @@ -57,7 +58,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); } @@ -173,15 +174,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.setSRGB(gradInfo.fColors[0]); + outData->endColor.setSRGB(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 +193,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 +207,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 +218,13 @@ bool tryStoreBitmap(Caches& caches, const SkShader& shader, const Matrix4& model const float height = outData->bitmapTexture->height(); description->hasBitmap = true; - if (!caches.extensions().hasNPot() + // 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,10 +312,7 @@ 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; - } + description->shadersMode = rec.fBlendMode; return true; } @@ -388,11 +383,12 @@ void SkiaShader::store(Caches& caches, const SkShader& shader, const Matrix4& mo 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); diff --git a/libs/hwui/SkiaShader.h b/libs/hwui/SkiaShader.h index 884196d9fc62..d2f37cda9cb3 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 { @@ -62,7 +61,6 @@ struct SkiaShaderData { } bitmapData; struct GradientShaderData { Matrix4 screenSpace; - GLuint ditherSampler; // simple gradient FloatColor startColor; @@ -72,7 +70,6 @@ struct SkiaShaderData { Texture* gradientTexture; GLuint gradientSampler; GLenum wrapST; - } gradientData; struct LayerShaderData { Layer* layer; @@ -90,7 +87,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..f61c3e9825f8 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,13 +66,6 @@ Snapshot::Snapshot(Snapshot* s, int saveFlags) } else { mClipArea = s->mClipArea; } - - if (s->flags & Snapshot::kFlagFboTarget) { - flags |= Snapshot::kFlagFboTarget; - region = s->region; - } else { - region = nullptr; - } } /////////////////////////////////////////////////////////////////////////////// @@ -127,58 +115,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 +163,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 +198,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..4ab58302df8f 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> @@ -63,18 +62,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 +100,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 }; /** @@ -179,11 +161,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 +184,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 +192,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 +215,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 +224,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 +245,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 +284,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..5b5b74e1c3f3 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,32 +190,39 @@ 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; default: @@ -199,9 +231,25 @@ static void colorTypeToGlFormatAndType(SkColorType colorType, } } -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 +272,32 @@ void Texture::upload(const SkBitmap& bitmap) { setDefaultParams = true; } - GLint format, type; - colorTypeToGlFormatAndType(bitmap.colorType(), &format, &type); + sk_sp<SkColorSpace> sRGB = SkColorSpace::MakeNamed(SkColorSpace::kSRGB_Named); + bool needSRGB = bitmap.info().colorSpace() == sRGB.get(); - if (updateSize(bitmap.width(), bitmap.height(), format)) { - needsAlloc = true; - } + GLint internalFormat, format, type; + colorTypeToGlFormatAndType(mCaches, bitmap.colorType(), needSRGB, &internalFormat, &format, &type); - blend = !bitmap.isOpaque(); - mCaches.textureState().bindTexture(mId); - - 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); + GLenum target = bitmap.isHardware() ? GL_TEXTURE_EXTERNAL_OES : GL_TEXTURE_2D; + needsAlloc |= updateSize(bitmap.width(), bitmap.height(), internalFormat, format, target); - SkCanvas canvas(rgbaBitmap); - canvas.drawBitmap(bitmap, 0.0f, 0.0f, nullptr); - - 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 +313,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..c75e88f5dd6e 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,7 +158,6 @@ 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 @@ -148,13 +165,18 @@ private: friend class Layer; // 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 aeee66106fb3..97b7dd722e08 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())); @@ -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 6c1815fe1301..e9a9c719975a 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; } @@ -684,11 +692,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. @@ -701,10 +713,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/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..99ac358163ff --- /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 <cutils/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..8366387b6d98 --- /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 <cutils/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/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..d3e765dbfbd1 --- /dev/null +++ b/libs/hwui/hwui/Bitmap.cpp @@ -0,0 +1,499 @@ +/* + * 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/RenderThread.h" +#include "renderthread/RenderProxy.h" + +#include <cutils/log.h> +#include <sys/mman.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_ALPHA: + return PIXEL_FORMAT_TRANSPARENT; + 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; + 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) { + ALOGW("unable to create hardware bitmap of configuration"); + 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(), 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) { + return nullptr; + } + SkImageInfo info = SkImageInfo::Make(graphicBuffer->getWidth(), graphicBuffer->getHeight(), + kRGBA_8888_SkColorType, kPremul_SkAlphaType); + 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..d7839b429648 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; @@ -170,7 +193,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 +218,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 +243,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 +266,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 a455f576e38d..f172473d1652 100644 --- a/libs/hwui/hwui/MinikinSkia.cpp +++ b/libs/hwui/hwui/MinikinSkia.cpp @@ -22,17 +22,14 @@ 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); @@ -43,7 +40,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; @@ -55,8 +52,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; @@ -68,23 +65,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; } @@ -117,8 +102,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..3ee916c6e8b1 100644 --- a/libs/hwui/hwui/MinikinSkia.h +++ b/libs/hwui/hwui/MinikinSkia.h @@ -19,29 +19,26 @@ #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; @@ -52,9 +49,10 @@ public: 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 67b775d98356..8dd165c46d21 100644 --- a/libs/hwui/hwui/MinikinUtils.cpp +++ b/libs/hwui/hwui/MinikinUtils.cpp @@ -24,17 +24,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 @@ -44,29 +45,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) { @@ -74,7 +76,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; @@ -88,7 +90,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..ca43156e88a1 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,66 +49,14 @@ 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) { @@ -133,16 +85,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 +118,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::FontFamily* family = new minikin::FontFamily(); + minikin::MinikinFont* font = new MinikinFontSkia(std::move(typeface), data, st.st_size, 0); + family->addFont(font); + 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..1be630c1e978 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(); @@ -45,9 +45,12 @@ struct ANDROID_API Typeface { 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..fb2134c51e22 --- /dev/null +++ b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp @@ -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. + */ + +#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(); + + 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..f2af4a891b12 --- /dev/null +++ b/libs/hwui/pipeline/skia/LayerDrawable.cpp @@ -0,0 +1,65 @@ +/* + * 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 "LayerDrawable.h" +#include "SkColorFilter.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); + } + GrGLTextureInfo externalTexture; + externalTexture.fTarget = layer->getRenderTarget(); + externalTexture.fID = layer->getTextureId(); + GrBackendTextureDesc textureDescription; + textureDescription.fWidth = layer->getWidth(); + textureDescription.fHeight = layer->getHeight(); + textureDescription.fConfig = kRGBA_8888_GrPixelConfig; + textureDescription.fOrigin = kTopLeft_GrSurfaceOrigin; + textureDescription.fTextureHandle = reinterpret_cast<GrBackendObject>(&externalTexture); + sk_sp<SkImage> layerImage = SkImage::MakeFromTexture(context, textureDescription); + 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..4b34c7c45c74 --- /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), SkRegion::kIntersect_Op, 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(), SkRegion::kIntersect_Op, 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..7f3474a1bdf3 --- /dev/null +++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp @@ -0,0 +1,193 @@ +/* + * 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 "LayerDrawable.h" +#include "renderthread/EglManager.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(); + Layer* layer = new Layer(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..0f2d09d69bd7 --- /dev/null +++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp @@ -0,0 +1,335 @@ +/* + * 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 <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->clipRect(layerDamage.toSkRect(), SkRegion::kReplace_Op); + + 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 { + return SkImageEncoder::EncodeData(pixmap.info(), pixmap.addr(), pixmap.rowBytes(), + SkImageEncoder::kPNG_Type, 100); + } +}; + +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(); +} + +void SkiaPipeline::renderFrameImpl(const LayerUpdateQueue& layers, const SkRect& clip, + const std::vector<sp<RenderNode>>& nodes, bool opaque, const Rect &contentDrawBounds, + SkCanvas* canvas) { + + canvas->clipRect(clip, SkRegion::kReplace_Op); + + if (!opaque) { + canvas->clear(SK_ColorTRANSPARENT); + } + + // If 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 = contentDrawBounds; + // 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. + int layer = (contentDrawBounds.isEmpty() || nodes.size() == 1) ? 2 : 0; + + for (const sp<RenderNode>& node : nodes) { + if (node->nothingToDraw()) continue; + + SkASSERT(node->getDisplayList()->isSkiaDL()); + + int count = canvas->save(); + + if (layer == 0) { + 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; + // 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); + } else if (layer == 1) { + // We shift and clip the content to match its final location in the window. + const SkRect clip = SkRect::MakeXYWH(contentDrawBounds.left, contentDrawBounds.top, + backdropBounds.getWidth(), backdropBounds.getHeight()); + const float dx = backdropBounds.left - contentDrawBounds.left; + const float dy = backdropBounds.top - contentDrawBounds.top; + canvas->translate(dx, dy); + // It gets cropped against the bounds of the backdrop to stay inside. + canvas->clipRect(clip, SkRegion::kIntersect_Op); + } + + RenderNodeDrawable root(node.get(), canvas); + root.draw(canvas); + canvas->restoreToCount(count); + layer++; + } +} + +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..95db2586f1bc --- /dev/null +++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp @@ -0,0 +1,247 @@ +/* + * 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" + +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..ca394b2d0b45 --- /dev/null +++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp @@ -0,0 +1,159 @@ +/* + * 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/EglManager.h" // needed for Frame +#include "Readback.h" +#include "renderstate/RenderState.h" +#include "SkiaPipeline.h" +#include "SkiaProfileRenderer.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); + } + + // TODO: support buffer age if Vulkan API can do it + Frame frame(backBuffer->width(), backBuffer->height(), 0); + 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(); + Layer* layer = new Layer(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..72af7c98e477 100644 --- a/libs/hwui/renderstate/RenderState.cpp +++ b/libs/hwui/renderstate/RenderState.cpp @@ -52,7 +52,6 @@ void RenderState::onGLContextCreated() { mCaches = &Caches::createInstance(*this); } mCaches->init(); - mCaches->textureCache.setAssetAtlas(&mAssetAtlas); } static void layerLostGlContext(Layer* layer) { @@ -60,43 +59,10 @@ static void layerLostGlContext(Layer* layer) { } 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(); @@ -179,9 +145,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 +278,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 +314,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..3d119dc9e290 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" @@ -92,7 +91,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; } @@ -120,7 +118,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..c561c86460c6 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -19,17 +19,18 @@ #include "AnimationContext.h" #include "Caches.h" -#include "DeferredLayerUpdater.h" #include "EglManager.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 +62,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 +161,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 +168,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 +186,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 +201,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 +226,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 +305,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 +330,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 +382,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 +393,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 +463,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 +497,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 +517,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 +525,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 +534,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 +568,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 +644,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 +656,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 +672,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 ac6a28fe6289..de95bee39763 100644 --- a/libs/hwui/renderthread/EglManager.cpp +++ b/libs/hwui/renderthread/EglManager.cpp @@ -16,6 +16,7 @@ #include "EglManager.h" +#include "Texture.h" #include "Caches.h" #include "DeviceInfo.h" #include "Properties.h" @@ -25,8 +26,14 @@ #include <cutils/log.h> #include <cutils/properties.h> #include <EGL/eglext.h> +#include <GrContextOptions.h> +#include <gl/GrGLInterface.h> #include <string> +#ifdef HWUI_GLES_WRAP_ENABLED +#include "debug/GlesDriver.h" +#endif + #define GLES_VERSION 2 // Android-specific addition that is used to show when frames began in systrace @@ -58,7 +65,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()); } @@ -91,9 +98,7 @@ EglManager::EglManager(RenderThread& thread) , 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() { @@ -103,11 +108,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); @@ -128,13 +133,33 @@ 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() { auto extensions = StringUtils::split( eglQueryString(mEglDisplay, EGL_EXTENSIONS)); - EglExtensions.bufferAge = extensions.has("EGL_EXT_buffer_age"); + // For our purposes we don't care if EGL_BUFFER_AGE is a result of + // EGL_EXT_buffer_age or EGL_KHR_partial_update as our usage is covered + // under EGL_KHR_partial_update and we don't need the expanded scope + // that EGL_EXT_buffer_age provides. + EglExtensions.bufferAge = extensions.has("EGL_EXT_buffer_age") + || extensions.has("EGL_KHR_partial_update"); EglExtensions.setDamage = extensions.has("EGL_KHR_partial_update"); LOG_ALWAYS_FATAL_IF(!extensions.has("EGL_KHR_swap_buffers_with_damage"), "Missing required extension EGL_KHR_swap_buffers_with_damage"); @@ -171,7 +196,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()); } } } @@ -183,33 +208,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() { @@ -224,15 +223,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; @@ -243,13 +251,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); @@ -277,7 +286,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; @@ -318,7 +327,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 @@ -372,14 +381,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..b12522e6bd41 100644 --- a/libs/hwui/renderthread/EglManager.h +++ b/libs/hwui/renderthread/EglManager.h @@ -31,6 +31,11 @@ class EglManager; class Frame { public: + Frame(EGLint width, EGLint height, EGLint bufferAge) + : mWidth(width) + , mHeight(height) + , mBufferAge(bufferAge) { } + EGLint width() const { return mWidth; } EGLint height() const { return mHeight; } @@ -39,6 +44,7 @@ public: EGLint bufferAge() const { return mBufferAge; } private: + Frame() {} friend class EglManager; EGLSurface mSurface; @@ -55,6 +61,7 @@ private: // 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 +86,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 +98,6 @@ private: void createPBufferSurface(); void loadConfig(); void createContext(); - void initAtlas(); EGLint queryBufferAge(EGLSurface surface); RenderThread& mRenderThread; @@ -106,10 +109,6 @@ private: EGLSurface mCurrentSurface; - sp<GraphicBuffer> mAtlasBuffer; - int64_t* mAtlasMap; - size_t mAtlasMapSize; - enum class SwapBehavior { Discard, Preserved, diff --git a/libs/hwui/renderthread/IRenderPipeline.h b/libs/hwui/renderthread/IRenderPipeline.h new file mode 100644 index 000000000000..0e4000b09e1a --- /dev/null +++ b/libs/hwui/renderthread/IRenderPipeline.h @@ -0,0 +1,85 @@ +/* + * 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 "EglManager.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..9dc2b596e3e8 --- /dev/null +++ b/libs/hwui/renderthread/OpenGLPipeline.cpp @@ -0,0 +1,263 @@ +/* + * 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 "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, *(layer->backingLayer()), bitmap); +} + +DeferredLayerUpdater* OpenGLPipeline::createTextureLayer() { + mEglManager.initialize(); + Layer* layer = new Layer(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..4d239bc616d0 --- /dev/null +++ b/libs/hwui/renderthread/VulkanManager.cpp @@ -0,0 +1,675 @@ +/* + * 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 "RenderThread.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; + + if (VK_NULL_HANDLE != mCommandPool) { + mDestroyCommandPool(mBackendContext->fDevice, mCommandPool, nullptr); + mCommandPool = VK_NULL_HANDLE; + } +} + +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()); +} + +// 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; + } + + // 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->mImageLayouts[backbuffer->mImageIndex]; + 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->mSurfaces[backbuffer->mImageIndex]; + 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->mSurfaces; + surface->mSurfaces = nullptr; + delete[] surface->mImageLayouts; + surface->mImageLayouts = 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->mImageLayouts = new VkImageLayout[surface->mImageCount]; + surface->mSurfaces = new sk_sp<SkSurface>[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; + + surface->mSurfaces[i] = SkSurface::MakeFromBackendRenderTarget(mRenderThread.getGrContext(), + desc, &props); + surface->mImageLayouts[i] = VK_IMAGE_LAYOUT_UNDEFINED; + } + + 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; + } + + SkAutoMalloc surfaceFormatAlloc(surfaceFormatCount * sizeof(VkSurfaceFormatKHR)); + VkSurfaceFormatKHR* surfaceFormats = (VkSurfaceFormatKHR*)surfaceFormatAlloc.get(); + res = mGetPhysicalDeviceSurfaceFormatsKHR(mBackendContext->fPhysicalDevice, surface->mVkSurface, + &surfaceFormatCount, surfaceFormats); + if (VK_SUCCESS != res) { + return false; + } + + uint32_t presentModeCount; + res = mGetPhysicalDeviceSurfacePresentModesKHR(mBackendContext->fPhysicalDevice, + surface->mVkSurface, &presentModeCount, nullptr); + if (VK_SUCCESS != res) { + return false; + } + + SkAutoMalloc presentModeAlloc(presentModeCount * sizeof(VkPresentModeKHR)); + VkPresentModeKHR* presentModes = (VkPresentModeKHR*)presentModeAlloc.get(); + res = mGetPhysicalDeviceSurfacePresentModesKHR(mBackendContext->fPhysicalDevice, + surface->mVkSurface, &presentModeCount, presentModes); + 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->mSurfaces[backbuffer->mImageIndex].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->mImageLayouts[backbuffer->mImageIndex] = 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(); +} + +} /* 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..f0e33206a576 --- /dev/null +++ b/libs/hwui/renderthread/VulkanManager.h @@ -0,0 +1,171 @@ +/* + * 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]; + }; + + sk_sp<SkSurface> mBackbuffer; + + VkSurfaceKHR mVkSurface = VK_NULL_HANDLE; + VkSwapchainKHR mSwapchain = VK_NULL_HANDLE; + + BackbufferInfo* mBackbuffers; + uint32_t mCurrentBackbufferIndex; + + uint32_t mImageCount; + VkImage* mImages; + VkImageLayout* mImageLayouts; + sk_sp<SkSurface>* mSurfaces; +}; + +// 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; } + + // 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; +}; + +} /* 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..6dadd3e364cf --- /dev/null +++ b/libs/hwui/tests/common/BitmapAllocationTestUtils.h @@ -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. + */ + +#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; + sk_sp<Bitmap> heapBitmap(TestUtils::createBitmap(width, height, &skBitmap)); + 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..9530c793e4e4 100644 --- a/libs/hwui/tests/common/TestUtils.cpp +++ b/libs/hwui/tests/common/TestUtils.cpp @@ -18,7 +18,10 @@ #include "hwui/Paint.h" #include "DeferredLayerUpdater.h" -#include "LayerRenderer.h" + +#include <renderthread/EglManager.h> +#include <renderthread/OpenGLPipeline.h> +#include <utils/Unicode.h> namespace android { namespace uirenderer { @@ -43,20 +46,14 @@ SkColor TestUtils::interpolateColor(float fraction, SkColor start, SkColor end) 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); + renderthread::OpenGLPipeline pipeline(renderThread); + sp<DeferredLayerUpdater> layerUpdater = pipeline.createTextureLayer(); + 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 +65,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 +107,13 @@ 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(); + renderThread.eglManager().initialize(); + + rtCallback(renderThread); - renderState.onGLContextCreated(); - rtCallback(renderthread::RenderThread::getInstance()); - renderState.flush(Caches::FlushMode::Full); - renderState.onGLContextDestroyed(); + renderThread.renderState().flush(Caches::FlushMode::Full); + renderThread.eglManager().destroy(); } std::unique_ptr<uint16_t[]> TestUtils::asciiToUtf16(const char* str) { @@ -124,5 +125,44 @@ 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; +} + } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/tests/common/TestUtils.h b/libs/hwui/tests/common/TestUtils.h index 5a4ab99012b9..0ce598d4dba4 100644 --- a/libs/hwui/tests/common/TestUtils.h +++ b/libs/hwui/tests/common/TestUtils.h @@ -13,37 +13,28 @@ * 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 <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)) @@ -132,14 +123,16 @@ public: 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( @@ -155,7 +148,29 @@ 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 + DeviceInfo::initialize(); +#endif + + sp<RenderNode> node = new RenderNode(); + RenderProperties& props = node->mutateStagingProperties(); + props.setLeftTopRightBottom(left, top, right, bottom); + if (setup) { + std::unique_ptr<Canvas> canvas(Canvas::create_recording_canvas(props.getWidth(), + props.getHeight())); + 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 @@ -166,7 +181,7 @@ public: RenderProperties& props = node->mutateStagingProperties(); props.setLeftTopRightBottom(left, top, right, bottom); if (setup) { - TestCanvas canvas(props.getWidth(), props.getHeight()); + RecordingCanvasType canvas(props.getWidth(), props.getHeight()); setup(props, canvas); node->setStagingDisplayList(canvas.finishRecording(), nullptr); } @@ -175,11 +190,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 +279,19 @@ 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); + private: static void syncHierarchyPropertiesAndDisplayListImpl(RenderNode* node) { node->syncProperties(); @@ -251,5 +308,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..9b0b9507b503 --- /dev/null +++ b/libs/hwui/tests/common/scenes/BitmapShaders.cpp @@ -0,0 +1,73 @@ +/* + * 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" + +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; +};
\ No newline at end of file diff --git a/libs/hwui/tests/common/scenes/ClippingAnimation.cpp b/libs/hwui/tests/common/scenes/ClippingAnimation.cpp index a5fd71266314..8f2ba2d2cddc 100644 --- a/libs/hwui/tests/common/scenes/ClippingAnimation.cpp +++ b/libs/hwui/tests/common/scenes/ClippingAnimation.cpp @@ -28,10 +28,10 @@ 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); @@ -39,7 +39,7 @@ public: 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.drawColor(Color::Blue_500, SkBlendMode::kSrcOver); } canvas.restore(); @@ -48,7 +48,7 @@ public: SkPath clipCircle; clipCircle.addCircle(100, 300, 100); canvas.clipPath(&clipCircle, SkRegion::kIntersect_Op); - canvas.drawColor(Color::Red_500, SkXfermode::kSrcOver_Mode); + 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/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..3630935a809f 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.drawColor(Color::Green_700, SkBlendMode::kSrcOver); canvas.clipRect(50, 50, 350, 350, SkRegion::kIntersect_Op); 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..5ef8773488b6 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); } @@ -83,7 +83,7 @@ public: 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.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 c5af06160b62..f3b7b51dbd31 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 <cutils/log.h> #include <gui/Surface.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..bbaf267a5798 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, SkRegion::kIntersect_Op); + + 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_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..85a91db58137 --- /dev/null +++ b/libs/hwui/tests/scripts/stopruntime.sh @@ -0,0 +1,26 @@ +#!/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 +adb shell daemonize surfaceflinger diff --git a/libs/hwui/tests/unit/BakedOpDispatcherTests.cpp b/libs/hwui/tests/unit/BakedOpDispatcherTests.cpp index 6b7b721eaec6..d44be7dfbc3a 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; @@ -85,9 +86,7 @@ RENDERTHREAD_TEST(BakedOpDispatcher, pathTexture_positionOvalArc) { 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) @@ -159,14 +158,14 @@ RENDERTHREAD_TEST(BakedOpDispatcher, offsetFlags) { } RENDERTHREAD_TEST(BakedOpDispatcher, renderTextWithShadow) { - 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) { 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(); @@ -273,3 +272,17 @@ RENDERTHREAD_TEST(BakedOpDispatcher, layerUpdateProperties) { } } } + +RENDERTHREAD_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/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..d3d80a96d5a3 --- /dev/null +++ b/libs/hwui/tests/unit/CanvasContextTests.cpp @@ -0,0 +1,50 @@ +/* + * 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); + ASSERT_EQ(functor.getLastMode(), DrawGlInfo::kModeProcess); +} diff --git a/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp b/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp new file mode 100644 index 000000000000..0326aa91bb18 --- /dev/null +++ b/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp @@ -0,0 +1,54 @@ +/* + * 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 "renderthread/OpenGLPipeline.h" +#include "tests/common/TestUtils.h" + +#include <gtest/gtest.h> + +using namespace android; +using namespace android::uirenderer; + +RENDERTHREAD_TEST(DeferredLayerUpdater, updateLayer) { + renderthread::OpenGLPipeline pipeline(renderThread); + sp<DeferredLayerUpdater> layerUpdater = pipeline.createTextureLayer(); + layerUpdater->setSize(100, 100); + layerUpdater->setBlend(true); + + + // updates are deferred so the backing layer should still be in its default state + EXPECT_EQ((uint32_t)GL_NONE, layerUpdater->backingLayer()->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. + EXPECT_EQ((uint32_t)GL_TEXTURE_EXTERNAL_OES, layerUpdater->backingLayer()->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/FrameBuilderTests.cpp b/libs/hwui/tests/unit/FrameBuilderTests.cpp index e2dc3a0a2c66..a1c225f544dd 100644 --- a/libs/hwui/tests/unit/FrameBuilderTests.cpp +++ b/libs/hwui/tests/unit/FrameBuilderTests.cpp @@ -127,11 +127,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()); @@ -155,7 +155,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); @@ -171,7 +171,7 @@ RENDERTHREAD_TEST(FrameBuilder, simpleStroke) { } RENDERTHREAD_TEST(FrameBuilder, simpleRejection) { - auto node = TestUtils::createNode(0, 0, 200, 200, + 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 @@ -198,10 +198,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 +210,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(); }); @@ -234,7 +235,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()); }); @@ -287,20 +288,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); })); @@ -346,7 +347,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 }); @@ -371,7 +372,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()); @@ -393,19 +394,19 @@ RENDERTHREAD_TEST(FrameBuilder, avoidOverdraw_rects) { } 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); + 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 +414,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()); @@ -447,25 +448,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.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.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.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.drawBitmap(*bitmap, 40, 70, nullptr); }); FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, @@ -482,8 +483,8 @@ RENDERTHREAD_TEST(FrameBuilder, regionClipStopsMerge) { 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); @@ -518,8 +519,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); @@ -549,7 +550,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); @@ -605,8 +606,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); @@ -645,7 +646,7 @@ 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); @@ -677,7 +678,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); @@ -699,7 +700,7 @@ RENDERTHREAD_TEST(FrameBuilder, textureLayer_reject) { SkMatrix::MakeTrans(5, 5)); layerUpdater->backingLayer()->setRenderTarget(GL_NONE); // Should be rejected - auto node = TestUtils::createNode(0, 0, 200, 200, + auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200, [&layerUpdater](RenderProperties& props, RecordingCanvas& canvas) { canvas.drawLayer(layerUpdater.get()); }); @@ -722,7 +723,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); @@ -747,10 +748,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, @@ -781,14 +782,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); @@ -820,10 +821,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 @@ -869,7 +870,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()); @@ -945,7 +946,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); { @@ -969,7 +970,7 @@ RENDERTHREAD_TEST(FrameBuilder, saveLayer_nested) { } RENDERTHREAD_TEST(FrameBuilder, saveLayer_contentRejection) { - auto node = TestUtils::createNode(0, 0, 200, 200, + 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); @@ -1003,7 +1004,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 +1021,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()); @@ -1053,7 +1054,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)); @@ -1105,7 +1106,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); @@ -1137,7 +1138,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 +1153,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)); @@ -1171,7 +1172,7 @@ RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_clearClip) { } RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_reject) { - auto node = TestUtils::createNode(0, 0, 200, 200, + 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)); @@ -1237,7 +1238,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 @@ -1289,7 +1290,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; @@ -1384,7 +1385,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 +1396,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; @@ -1459,10 +1460,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 +1487,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 +1504,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 +} - auto parent = TestUtils::createNode(0, 0, 100, 100, +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 + +RENDERTHREAD_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 +1544,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,7 +1559,7 @@ RENDERTHREAD_TEST(FrameBuilder, zReorder) { ZReorderTestRenderer renderer; frameBuilder.replayBakedOps<TestDispatcher>(renderer); - EXPECT_EQ(10, renderer.getIndex()); + EXPECT_EQ(13, renderer.getIndex()); }; RENDERTHREAD_TEST(FrameBuilder, projectionReorder) { @@ -1579,7 +1607,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 +1619,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 +1627,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); @@ -1660,7 +1688,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 +1698,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); @@ -1735,12 +1763,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,7 +1778,7 @@ 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); @@ -1758,7 +1786,7 @@ RENDERTHREAD_TEST(FrameBuilder, projectionChildScroll) { 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 +1803,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); @@ -1803,7 +1831,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()); @@ -1844,7 +1872,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); @@ -1887,7 +1915,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); @@ -1934,7 +1962,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()); @@ -1961,7 +1989,7 @@ 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. @@ -1983,7 +2011,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 +2020,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; @@ -2105,7 +2133,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 +2164,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 @@ -2222,10 +2250,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.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver); }); FrameBuilder frameBuilder(SkRect::MakeLTRB(10, 10, 40, 40), 50, 50, @@ -2237,5 +2265,349 @@ RENDERTHREAD_TEST(FrameBuilder, clip_replace) { EXPECT_EQ(1, renderer.getIndex()); } +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()); +} + +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()); +} + +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()); +} + +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()); +} + +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()); +} + +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()); +} + +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()); +} + +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()); +} + +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()); +} + +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()); +} + +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..ce1db0585be8 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,7 +112,7 @@ 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; } @@ -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/LeakCheckTests.cpp b/libs/hwui/tests/unit/LeakCheckTests.cpp index 6148b33eceb8..06599dde4957 100644 --- a/libs/hwui/tests/unit/LeakCheckTests.cpp +++ b/libs/hwui/tests/unit/LeakCheckTests.cpp @@ -31,7 +31,7 @@ const BakedOpRenderer::LightInfo sLightInfo = { 128, 128 }; RENDERTHREAD_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(); @@ -51,7 +51,7 @@ RENDERTHREAD_TEST(LeakCheck, saveLayer_overdrawRejection) { RENDERTHREAD_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..0881fa246afd --- /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_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); +}
\ No newline at end of file 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..dda432ea03ed 100644 --- a/libs/hwui/tests/unit/RecordingCanvasTests.cpp +++ b/libs/hwui/tests/unit/RecordingCanvasTests.cpp @@ -249,7 +249,7 @@ TEST(RecordingCanvas, drawGlyphs_forceAlignLeft) { 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"; @@ -261,8 +261,7 @@ TEST(RecordingCanvas, drawColor) { 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 +277,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(); @@ -562,7 +561,7 @@ TEST(RecordingCanvas, saveLayer_rejectBegin) { 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); @@ -577,7 +576,7 @@ TEST(RecordingCanvas, drawRenderNode_rejection) { 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); @@ -639,7 +638,7 @@ 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.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver); canvas.restore(); }); ASSERT_EQ(1u, dl->getOps().size()) << "Must have one op"; @@ -728,22 +727,27 @@ TEST(RecordingCanvas, refPaint) { } TEST(RecordingCanvas, refBitmap) { - SkBitmap bitmap = TestUtils::createSkBitmap(100, 100); + 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); + 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)); - paint.setShader(shader); + SkShader::TileMode::kClamp_TileMode, + nullptr, + kNever_SkCopyPixelsMode, + nullptr); + paint.setShader(std::move(shader)); canvas.drawRoundRect(0, 0, 100, 100, 20.0f, 20.0f, paint); }); auto& bitmaps = dl->getBitmapResources(); @@ -751,24 +755,29 @@ TEST(RecordingCanvas, refBitmapInShader_bitmapShader) { } TEST(RecordingCanvas, refBitmapInShader_composeShader) { - SkBitmap bitmap = TestUtils::createSkBitmap(100, 100); + 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(); @@ -782,7 +791,7 @@ TEST(RecordingCanvas, drawText) { 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; @@ -806,7 +815,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..f4b686d3b1e2 --- /dev/null +++ b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp @@ -0,0 +1,789 @@ +/* + * 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 <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, mIndex++) << "An op was drawn out of order"; + } + int getIndex() { return mIndex; } +protected: + int mIndex = 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 { +class ContextFactory : public IContextFactory { +public: + virtual AnimationContext* createAnimationContext(renderthread::TimeLord& clock) override { + return new AnimationContext(clock); + } +}; + +inline SkRect getBounds(const SkCanvas* canvas) { + SkClipStack::BoundsType boundType; + SkRect clipBounds; + canvas->getClipStack()->getBounds(&clipBounds, &boundType); + return clipBounds; +} +inline SkRect getLocalBounds(const SkCanvas* canvas) { + SkMatrix invertedTotalMatrix; + EXPECT_TRUE(canvas->getTotalMatrix().invert(&invertedTotalMatrix)); + SkRect outlineInDeviceCoord = getBounds(canvas); + SkRect outlineInLocalCoord; + invertedTotalMatrix.mapRect(&outlineInLocalCoord, outlineInDeviceCoord); + return outlineInLocalCoord; +} +} // 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 = mIndex++; + 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), getBounds(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), getLocalBounds(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), getBounds(this)); + break; + default: + ADD_FAILURE(); + } + EXPECT_EQ(expectedMatrix, getTotalMatrix()); + } + + int getIndex() { return mIndex; } + protected: + int mIndex = 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), getBounds(this)); + } + void onDrawRect(const SkRect& rect, const SkPaint& paint) override { + EXPECT_EQ(1, (*mDrawCounter)++); + EXPECT_EQ(SkRect::MakeLTRB(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT), getBounds(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), getLocalBounds(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), getBounds(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, SkCopyPixelsMode) 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, mIndex++); + EXPECT_TRUE(getTotalMatrix().isIdentity()); + } + void onDrawOval(const SkRect&, const SkPaint&) override { + EXPECT_EQ(1, mIndex++); + EXPECT_EQ(SkRect::MakeWH(CANVAS_WIDTH, CANVAS_HEIGHT), getBounds(this)); + EXPECT_TRUE(getTotalMatrix().isIdentity()); + } + int mIndex = 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, SkRegion::kIntersect_Op); + + 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->mIndex); +} + +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)); +} diff --git a/libs/hwui/tests/unit/RenderNodeTests.cpp b/libs/hwui/tests/unit/RenderNodeTests.cpp index cf76a8691dcd..331a6acc1268 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,40 @@ 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(); + VectorDrawableRoot* vectorDrawable = new VectorDrawableRoot(group); + auto rootNode = TestUtils::createNode(0, 0, 200, 400, + [&](RenderProperties& props, Canvas& canvas) { + canvas.drawVectorDrawable(vectorDrawable); + }); + 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); + + delete vectorDrawable; + 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..95f99746f7d3 100644 --- a/libs/hwui/tests/unit/SkiaCanvasTests.cpp +++ b/libs/hwui/tests/unit/SkiaCanvasTests.cpp @@ -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..899758a9e6fb --- /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_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..17801aff40ea --- /dev/null +++ b/libs/hwui/tests/unit/SkiaPipelineTests.cpp @@ -0,0 +1,231 @@ +/* + * 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 <SkLiteRecorder.h> +#include <string.h> + +using namespace android; +using namespace android::uirenderer; +using namespace android::uirenderer::renderthread; +using namespace android::uirenderer::skiapipeline; + +RENDERTHREAD_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_TEST(SkiaPipeline, renderFrameCheckBounds) { + auto backdropRedNode = TestUtils::createSkiaNode(1, 1, 4, 4, + [](RenderProperties& props, SkiaRecordingCanvas& redCanvas) { + redCanvas.drawColor(SK_ColorRED, SkBlendMode::kSrcOver); + }); + auto contentGreenNode = TestUtils::createSkiaNode(2, 2, 5, 5, + [](RenderProperties& props, SkiaRecordingCanvas& blueCanvas) { + blueCanvas.drawColor(SK_ColorGREEN, SkBlendMode::kSrcOver); + }); + LayerUpdateQueue layerUpdateQueue; + SkRect dirty = SkRect::MakeLargest(); + std::vector<sp<RenderNode>> renderNodes; + renderNodes.push_back(backdropRedNode); //first node is backdrop + renderNodes.push_back(contentGreenNode); //second node is content drawn on top of the backdrop + android::uirenderer::Rect contentDrawBounds(1, 1, 3, 3); + auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread); + auto surface = SkSurface::MakeRasterN32Premul(5, 5); + surface->getCanvas()->drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver); + ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE); + //backdropBounds is (1, 1, 3, 3), content clip is (1, 1, 3, 3), content translate is (0, 0) + pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, true, contentDrawBounds, surface); + ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE); + ASSERT_EQ(TestUtils::getColor(surface, 1, 1), SK_ColorRED); + ASSERT_EQ(TestUtils::getColor(surface, 2, 2), SK_ColorGREEN); + ASSERT_EQ(TestUtils::getColor(surface, 3, 3), SK_ColorRED); + ASSERT_EQ(TestUtils::getColor(surface, 4, 4), SK_ColorBLUE); + + surface->getCanvas()->drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver); + contentDrawBounds.set(0, 0, 5, 5); + //backdropBounds is (1, 1, 4, 4), content clip is (0, 0, 3, 3), content translate is (1, 1) + pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, true, contentDrawBounds, surface); + ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE); + ASSERT_EQ(TestUtils::getColor(surface, 1, 1), SK_ColorRED); + ASSERT_EQ(TestUtils::getColor(surface, 2, 2), SK_ColorRED); + ASSERT_EQ(TestUtils::getColor(surface, 3, 3), SK_ColorGREEN); + ASSERT_EQ(TestUtils::getColor(surface, 4, 4), SK_ColorBLUE); +} + +RENDERTHREAD_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_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_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_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; + android::uirenderer::Rect contentDrawBounds(0, 0, 1, 1); + 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); + 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); +} 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 b49c1eb1dc05..94818b2dc44f 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..fa3e13dd71ba 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,14 +86,9 @@ 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() { @@ -115,12 +110,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 +167,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/input/PointerController.cpp b/libs/input/PointerController.cpp index abef66f9ecd9..0b22ad5ba470 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 0bc832a8fb37..18ebd47558bc 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/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; } |