diff options
Diffstat (limited to 'libs')
205 files changed, 11493 insertions, 7616 deletions
diff --git a/libs/androidfw/Android.mk b/libs/androidfw/Android.mk index 20d5470..461160a 100644 --- a/libs/androidfw/Android.mk +++ b/libs/androidfw/Android.mk @@ -40,10 +40,12 @@ hostSources := $(commonSources) # For the host # ===================================================== include $(CLEAR_VARS) +LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk LOCAL_MODULE:= libandroidfw LOCAL_MODULE_TAGS := optional LOCAL_CFLAGS += -DSTATIC_ANDROIDFW_FOR_TOOLS +LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code LOCAL_SRC_FILES:= $(hostSources) LOCAL_C_INCLUDES := external/zlib @@ -54,6 +56,7 @@ include $(BUILD_HOST_STATIC_LIBRARY) # ===================================================== include $(CLEAR_VARS) +LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk LOCAL_MODULE:= libandroidfw LOCAL_MODULE_TAGS := optional @@ -63,11 +66,13 @@ LOCAL_C_INCLUDES := \ system/core/include LOCAL_STATIC_LIBRARIES := libziparchive LOCAL_SHARED_LIBRARIES := \ - libbinder \ - liblog \ - libcutils \ - libutils \ - libz + libbinder \ + liblog \ + libcutils \ + libutils \ + libz + +LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code include $(BUILD_SHARED_LIBRARY) diff --git a/libs/androidfw/Asset.cpp b/libs/androidfw/Asset.cpp index 589211f..782806e 100644 --- a/libs/androidfw/Asset.cpp +++ b/libs/androidfw/Asset.cpp @@ -45,6 +45,8 @@ using namespace android; # define O_BINARY 0 #endif +static const bool kIsDebug = false; + static Mutex gAssetLock; static int32_t gCount = 0; static Asset* gHead = NULL; @@ -89,7 +91,9 @@ Asset::Asset(void) gTail->mNext = this; gTail = this; } - //ALOGI("Creating Asset %p #%d\n", this, gCount); + if (kIsDebug) { + ALOGI("Creating Asset %p #%d\n", this, gCount); + } } Asset::~Asset(void) @@ -109,7 +113,9 @@ Asset::~Asset(void) mPrev->mNext = mNext; } mNext = mPrev = NULL; - //ALOGI("Destroying Asset in %p #%d\n", this, gCount); + if (kIsDebug) { + ALOGI("Destroying Asset in %p #%d\n", this, gCount); + } } /* @@ -526,7 +532,7 @@ off64_t _FileAsset::seek(off64_t offset, int whence) void _FileAsset::close(void) { if (mMap != NULL) { - mMap->release(); + delete mMap; mMap = NULL; } if (mBuf != NULL) { @@ -606,7 +612,7 @@ const void* _FileAsset::getBuffer(bool wordAligned) map = new FileMap; if (!map->create(NULL, fileno(mFp), mStart, mLength, true)) { - map->release(); + delete map; return NULL; } @@ -821,7 +827,7 @@ off64_t _CompressedAsset::seek(off64_t offset, int whence) void _CompressedAsset::close(void) { if (mMap != NULL) { - mMap->release(); + delete mMap; mMap = NULL; } diff --git a/libs/androidfw/AssetManager.cpp b/libs/androidfw/AssetManager.cpp index de6a33c..25cd363 100644 --- a/libs/androidfw/AssetManager.cpp +++ b/libs/androidfw/AssetManager.cpp @@ -64,6 +64,8 @@ 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. @@ -152,15 +154,19 @@ AssetManager::AssetManager(CacheMode cacheMode) mResources(NULL), mConfig(new ResTable_config), mCacheMode(cacheMode), mCacheValid(false) { - int count = android_atomic_inc(&gCount)+1; - //ALOGI("Creating AssetManager %p #%d\n", this, count); + int count = android_atomic_inc(&gCount) + 1; + if (kIsDebug) { + ALOGI("Creating AssetManager %p #%d\n", this, count); + } memset(mConfig, 0, sizeof(ResTable_config)); } AssetManager::~AssetManager(void) { int count = android_atomic_dec(&gCount); - //ALOGI("Destroying AssetManager in %p #%d\n", this, count); + if (kIsDebug) { + ALOGI("Destroying AssetManager in %p #%d\n", this, count); + } delete mConfig; delete mResources; @@ -296,6 +302,10 @@ bool AssetManager::addOverlayPath(const String8& packagePath, int32_t* cookie) mAssetPaths.add(oap); *cookie = static_cast<int32_t>(mAssetPaths.size()); + if (mResources != NULL) { + appendPathToResTable(oap); + } + return true; } @@ -601,6 +611,11 @@ FileType AssetManager::getFileType(const char* fileName) } bool AssetManager::appendPathToResTable(const asset_path& ap) const { + // skip those ap's that correspond to system overlays + if (ap.isSystemOverlay) { + return true; + } + Asset* ass = NULL; ResTable* sharedRes = NULL; bool shared = true; @@ -786,6 +801,7 @@ void AssetManager::addSystemOverlays(const char* pathOverlaysList, oap.path = String8(buf, space - buf); oap.type = kFileTypeRegular; oap.idmap = String8(space + 1, newline - space - 1); + oap.isSystemOverlay = true; Asset* oass = const_cast<AssetManager*>(this)-> openNonAssetInPathLocked("resources.arsc", @@ -1864,7 +1880,9 @@ AssetManager::SharedZip::SharedZip(const String8& path, time_t modWhen) : mPath(path), mZipFile(NULL), mModWhen(modWhen), mResourceTableAsset(NULL), mResourceTable(NULL) { - //ALOGI("Creating SharedZip %p %s\n", this, (const char*)mPath); + if (kIsDebug) { + ALOGI("Creating SharedZip %p %s\n", this, (const char*)mPath); + } ALOGV("+++ opening zip '%s'\n", mPath.string()); mZipFile = ZipFileRO::open(mPath.string()); if (mZipFile == NULL) { @@ -1958,7 +1976,9 @@ bool AssetManager::SharedZip::getOverlay(size_t idx, asset_path* out) const AssetManager::SharedZip::~SharedZip() { - //ALOGI("Destroying SharedZip %p %s\n", this, (const char*)mPath); + if (kIsDebug) { + ALOGI("Destroying SharedZip %p %s\n", this, (const char*)mPath); + } if (mResourceTable != NULL) { delete mResourceTable; } diff --git a/libs/androidfw/BackupData.cpp b/libs/androidfw/BackupData.cpp index d16d5498..ba4a4ff 100644 --- a/libs/androidfw/BackupData.cpp +++ b/libs/androidfw/BackupData.cpp @@ -27,7 +27,7 @@ namespace android { -static const bool DEBUG = false; +static const bool kIsDebug = false; /* * File Format (v1): @@ -45,12 +45,6 @@ static const bool DEBUG = false; const static int ROUND_UP[4] = { 0, 3, 2, 1 }; static inline size_t -round_up(size_t n) -{ - return n + ROUND_UP[n % 4]; -} - -static inline size_t padding_extra(size_t n) { return ROUND_UP[n % 4]; @@ -62,7 +56,7 @@ BackupDataWriter::BackupDataWriter(int fd) m_entityCount(0) { m_pos = (ssize_t) lseek(fd, 0, SEEK_CUR); - if (DEBUG) ALOGI("BackupDataWriter(%d) @ %ld", fd, (long)m_pos); + if (kIsDebug) ALOGI("BackupDataWriter(%d) @ %ld", fd, (long)m_pos); } BackupDataWriter::~BackupDataWriter() @@ -79,7 +73,7 @@ BackupDataWriter::write_padding_for(int n) paddingSize = padding_extra(n); if (paddingSize > 0) { uint32_t padding = 0xbcbcbcbc; - if (DEBUG) ALOGI("writing %zd padding bytes for %d", paddingSize, n); + if (kIsDebug) ALOGI("writing %zd padding bytes for %d", paddingSize, n); amt = write(m_fd, &padding, paddingSize); if (amt != paddingSize) { m_status = errno; @@ -112,7 +106,7 @@ BackupDataWriter::WriteEntityHeader(const String8& key, size_t dataSize) } else { k = key; } - if (DEBUG) { + if (kIsDebug) { ALOGD("Writing header: prefix='%s' key='%s' dataSize=%zu", m_keyPrefix.string(), key.string(), dataSize); } @@ -126,7 +120,7 @@ BackupDataWriter::WriteEntityHeader(const String8& key, size_t dataSize) header.keyLen = tolel(keyLen); header.dataSize = tolel(dataSize); - if (DEBUG) ALOGI("writing entity header, %zu bytes", sizeof(entity_header_v1)); + if (kIsDebug) ALOGI("writing entity header, %zu bytes", sizeof(entity_header_v1)); amt = write(m_fd, &header, sizeof(entity_header_v1)); if (amt != sizeof(entity_header_v1)) { m_status = errno; @@ -134,7 +128,7 @@ BackupDataWriter::WriteEntityHeader(const String8& key, size_t dataSize) } m_pos += amt; - if (DEBUG) ALOGI("writing entity header key, %zd bytes", keyLen+1); + if (kIsDebug) ALOGI("writing entity header key, %zd bytes", keyLen+1); amt = write(m_fd, k.string(), keyLen+1); if (amt != keyLen+1) { m_status = errno; @@ -152,10 +146,10 @@ BackupDataWriter::WriteEntityHeader(const String8& key, size_t dataSize) status_t BackupDataWriter::WriteEntityData(const void* data, size_t size) { - if (DEBUG) ALOGD("Writing data: size=%lu", (unsigned long) size); + if (kIsDebug) ALOGD("Writing data: size=%lu", (unsigned long) size); if (m_status != NO_ERROR) { - if (DEBUG) { + if (kIsDebug) { ALOGD("Not writing data - stream in error state %d (%s)", m_status, strerror(m_status)); } return m_status; @@ -167,7 +161,7 @@ BackupDataWriter::WriteEntityData(const void* data, size_t size) ssize_t amt = write(m_fd, data, size); if (amt != (ssize_t)size) { m_status = errno; - if (DEBUG) ALOGD("write returned error %d (%s)", m_status, strerror(m_status)); + if (kIsDebug) ALOGD("write returned error %d (%s)", m_status, strerror(m_status)); return m_status; } m_pos += amt; @@ -189,7 +183,7 @@ BackupDataReader::BackupDataReader(int fd) { memset(&m_header, 0, sizeof(m_header)); m_pos = (ssize_t) lseek(fd, 0, SEEK_CUR); - if (DEBUG) ALOGI("BackupDataReader(%d) @ %ld", fd, (long)m_pos); + if (kIsDebug) ALOGI("BackupDataReader(%d) @ %ld", fd, (long)m_pos); } BackupDataReader::~BackupDataReader() @@ -342,15 +336,19 @@ BackupDataReader::ReadEntityData(void* data, size_t size) return -1; } int remaining = m_dataEndPos - m_pos; - //ALOGD("ReadEntityData size=%d m_pos=0x%x m_dataEndPos=0x%x remaining=%d\n", - // size, m_pos, m_dataEndPos, remaining); + if (kIsDebug) { + ALOGD("ReadEntityData size=%zu m_pos=0x%zx m_dataEndPos=0x%zx remaining=%d\n", + size, m_pos, m_dataEndPos, remaining); + } if (remaining <= 0) { return 0; } if (((int)size) > remaining) { size = remaining; } - //ALOGD(" reading %d bytes", size); + if (kIsDebug) { + ALOGD(" reading %zu bytes", size); + } int amt = read(m_fd, data, size); if (amt < 0) { m_status = errno; diff --git a/libs/androidfw/BackupHelpers.cpp b/libs/androidfw/BackupHelpers.cpp index 33cf8ef..9300794 100644 --- a/libs/androidfw/BackupHelpers.cpp +++ b/libs/androidfw/BackupHelpers.cpp @@ -68,14 +68,11 @@ struct file_metadata_v1 { const static int CURRENT_METADATA_VERSION = 1; -#if 1 -#define LOGP(f, x...) -#else +static const bool kIsDebug = false; #if TEST_BACKUP_HELPERS -#define LOGP(f, x...) printf(f "\n", x) +#define LOGP(f, x...) if (kIsDebug) printf(f "\n", x) #else -#define LOGP(x...) ALOGD(x) -#endif +#define LOGP(x...) if (kIsDebug) ALOGD(x) #endif const static int ROUND_UP[4] = { 0, 3, 2, 1 }; @@ -445,18 +442,6 @@ back_up_files(int oldSnapshotFD, BackupDataWriter* dataStream, int newSnapshotFD return 0; } -// Utility function, equivalent to stpcpy(): perform a strcpy, but instead of -// returning the initial dest, return a pointer to the trailing NUL. -static char* strcpy_ptr(char* dest, const char* str) { - if (dest && str) { - while ((*dest = *str) != 0) { - dest++; - str++; - } - } - return dest; -} - static void calc_tar_checksum(char* buf) { // [ 148 : 8 ] checksum -- to be calculated with this field as space chars memset(buf + 148, ' ', 8); @@ -493,7 +478,8 @@ void send_tarfile_chunk(BackupDataWriter* writer, const char* buffer, size_t siz } int write_tarfile(const String8& packageName, const String8& domain, - const String8& rootpath, const String8& filepath, BackupDataWriter* writer) + const String8& rootpath, const String8& filepath, off_t* outSize, + BackupDataWriter* writer) { // In the output stream everything is stored relative to the root const char* relstart = filepath.string() + rootpath.length(); @@ -503,6 +489,7 @@ int write_tarfile(const String8& packageName, const String8& domain, // If relpath is empty, it means this is the top of one of the standard named // domain directories, so we should just skip it if (relpath.length() == 0) { + *outSize = 0; return 0; } @@ -532,12 +519,25 @@ int write_tarfile(const String8& packageName, const String8& domain, return err; } + // very large files need a pax extended size header + if (s.st_size > 077777777777LL) { + needExtended = true; + } + String8 fullname; // for pax later on String8 prefix; const int isdir = S_ISDIR(s.st_mode); if (isdir) s.st_size = 0; // directories get no actual data in the tar stream + // Report the size, including a rough tar overhead estimation: 512 bytes for the + // overall tar file-block header, plus 2 blocks if using the pax extended format, + // plus the raw content size rounded up to a multiple of 512. + *outSize = 512 + (needExtended ? 1024 : 0) + 512*((s.st_size + 511)/512); + + // Measure case: we've returned the size; now return without moving data + if (!writer) return 0; + // !!! TODO: use mmap when possible to avoid churning the buffer cache // !!! TODO: this will break with symlinks; need to use readlink(2) int fd = open(filepath.string(), O_RDONLY); @@ -575,14 +575,10 @@ int write_tarfile(const String8& packageName, const String8& domain, snprintf(buf + 116, 8, "0%lo", (unsigned long)s.st_gid); // [ 124 : 12 ] file size in bytes - if (s.st_size > 077777777777LL) { - // very large files need a pax extended size header - needExtended = true; - } snprintf(buf + 124, 12, "%011llo", (isdir) ? 0LL : s.st_size); // [ 136 : 12 ] last mod time as a UTC time_t - snprintf(buf + 136, 12, "%0lo", s.st_mtime); + snprintf(buf + 136, 12, "%0lo", (unsigned long)s.st_mtime); // [ 156 : 1 ] link/file type uint8_t type; @@ -638,7 +634,6 @@ int write_tarfile(const String8& packageName, const String8& domain, // construct the pax extended header data block memset(paxData, 0, BUFSIZE - (paxData - buf)); - int len; // size header -- calc len in digits by actually rendering the number // to a string - brute force but simple @@ -1203,7 +1198,6 @@ test_read_header_and_entity(BackupDataReader& reader, const char* str) size_t bufSize = strlen(str)+1; char* buf = (char*)malloc(bufSize); String8 string; - int cookie = 0x11111111; size_t actualSize; bool done; int type; @@ -1336,23 +1330,12 @@ get_mod_time(const char* filename, struct timeval times[2]) fprintf(stderr, "stat '%s' failed: %s\n", filename, strerror(errno)); return errno; } - times[0].tv_sec = st.st_atime; - times[1].tv_sec = st.st_mtime; - // If st_atime is a macro then struct stat64 uses struct timespec - // to store the access and modif time values and typically - // st_*time_nsec is not defined. In glibc, this is controlled by - // __USE_MISC. -#ifdef __USE_MISC -#if !defined(st_atime) || defined(st_atime_nsec) -#error "Check if this __USE_MISC conditional is still needed." -#endif + times[0].tv_sec = st.st_atim.tv_sec; times[0].tv_usec = st.st_atim.tv_nsec / 1000; + + times[1].tv_sec = st.st_mtim.tv_sec; times[1].tv_usec = st.st_mtim.tv_nsec / 1000; -#else - times[0].tv_usec = st.st_atime_nsec / 1000; - times[1].tv_usec = st.st_mtime_nsec / 1000; -#endif return 0; } @@ -1493,7 +1476,6 @@ int backup_helper_test_null_base() { int err; - int oldSnapshotFD; int dataStreamFD; int newSnapshotFD; @@ -1542,7 +1524,6 @@ int backup_helper_test_missing_file() { int err; - int oldSnapshotFD; int dataStreamFD; int newSnapshotFD; diff --git a/libs/androidfw/ObbFile.cpp b/libs/androidfw/ObbFile.cpp index ec59f06..195fa9a 100644 --- a/libs/androidfw/ObbFile.cpp +++ b/libs/androidfw/ObbFile.cpp @@ -122,7 +122,7 @@ bool ObbFile::parseObbFile(int fd) if (fileLength < 0) { ALOGW("error seeking in ObbFile: %s\n", strerror(errno)); } else { - ALOGW("file is only %lld (less than %d minimum)\n", fileLength, kFooterMinSize); + ALOGW("file is only %lld (less than %d minimum)\n", (long long int)fileLength, kFooterMinSize); } return false; } @@ -150,8 +150,8 @@ bool ObbFile::parseObbFile(int fd) footerSize = get4LE((unsigned char*)footer); if (footerSize > (size_t)fileLength - kFooterTagSize || footerSize > kMaxBufSize) { - ALOGW("claimed footer size is too large (0x%08zx; file size is 0x%08llx)\n", - footerSize, fileLength); + ALOGW("claimed footer size is too large (0x%08zx; file size is 0x%08lld)\n", + footerSize, (long long int)fileLength); return false; } @@ -164,7 +164,7 @@ bool ObbFile::parseObbFile(int fd) off64_t fileOffset = fileLength - footerSize - kFooterTagSize; if (lseek64(fd, fileOffset, SEEK_SET) != fileOffset) { - ALOGW("seek %lld failed: %s\n", fileOffset, strerror(errno)); + ALOGW("seek %lld failed: %s\n", (long long int)fileOffset, strerror(errno)); return false; } diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp index bdb53c3..04ebe70 100644 --- a/libs/androidfw/ResourceTypes.cpp +++ b/libs/androidfw/ResourceTypes.cpp @@ -17,6 +17,16 @@ #define LOG_TAG "ResourceType" //#define LOG_NDEBUG 0 +#include <ctype.h> +#include <memory.h> +#include <stddef.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> + +#include <limits> +#include <type_traits> + #include <androidfw/ByteBucketArray.h> #include <androidfw/ResourceTypes.h> #include <androidfw/TypeWrappers.h> @@ -27,43 +37,23 @@ #include <utils/String16.h> #include <utils/String8.h> -#include <stdlib.h> -#include <string.h> -#include <memory.h> -#include <ctype.h> -#include <stdint.h> -#include <stddef.h> +#ifdef __ANDROID__ +#include <binder/TextOutput.h> +#endif #ifndef INT32_MAX #define INT32_MAX ((int32_t)(2147483647)) #endif -#define STRING_POOL_NOISY(x) //x -#define XML_NOISY(x) //x -#define TABLE_NOISY(x) //x -#define TABLE_GETENTRY(x) //x -#define TABLE_SUPER_NOISY(x) //x -#define LOAD_TABLE_NOISY(x) //x -#define TABLE_THEME(x) //x -#define LIB_NOISY(x) //x - namespace android { #ifdef HAVE_WINSOCK #undef nhtol #undef htonl - -#ifdef HAVE_LITTLE_ENDIAN #define ntohl(x) ( ((x) << 24) | (((x) >> 24) & 255) | (((x) << 8) & 0xff0000) | (((x) >> 8) & 0xff00) ) #define htonl(x) ntohl(x) #define ntohs(x) ( (((x) << 8) & 0xff00) | (((x) >> 8) & 255) ) #define htons(x) ntohs(x) -#else -#define ntohl(x) (x) -#define htonl(x) (x) -#define ntohs(x) (x) -#define htons(x) (x) -#endif #endif #define IDMAP_MAGIC 0x504D4449 @@ -72,6 +62,19 @@ namespace android { #define APP_PACKAGE_ID 0x7f #define SYS_PACKAGE_ID 0x01 +static const bool kDebugStringPoolNoisy = false; +static const bool kDebugXMLNoisy = false; +static const bool kDebugTableNoisy = false; +static const bool kDebugTableGetEntry = false; +static const bool kDebugTableSuperNoisy = false; +static const bool kDebugLoadTableNoisy = false; +static const bool kDebugLoadTableSuperNoisy = false; +static const bool kDebugTableTheme = false; +static const bool kDebugResXMLTree = false; +static const bool kDebugLibNoisy = false; + +// TODO: This code uses 0xFFFFFFFF converted to bag_set* as a sentinel value. This is bad practice. + // Standard C isspace() is only required to look at the low byte of its input, so // produces incorrect results for UTF-16 characters. For safety's sake, assume that // any high-byte UTF-16 code point is not whitespace. @@ -701,6 +704,12 @@ const char16_t* ResStringPool::stringAt(size_t idx, size_t* u16len) const *u16len = decodeLength(&str); if ((uint32_t)(str+*u16len-strings) < mStringPoolSize) { + // Reject malformed (non null-terminated) strings + if (str[*u16len] != 0x0000) { + ALOGW("Bad string block: string #%d is not null-terminated", + (int)idx); + return NULL; + } return reinterpret_cast<const char16_t*>(str); } else { ALOGW("Bad string block: string #%d extends to %d, past end at %d\n", @@ -719,12 +728,14 @@ const char16_t* ResStringPool::stringAt(size_t idx, size_t* u16len) const if (mCache == NULL) { #ifndef HAVE_ANDROID_OS - STRING_POOL_NOISY(ALOGI("CREATING STRING CACHE OF %d bytes", - mHeader->stringCount*sizeof(char16_t**))); + if (kDebugStringPoolNoisy) { + ALOGI("CREATING STRING CACHE OF %zu bytes", + mHeader->stringCount*sizeof(char16_t**)); + } #else // We do not want to be in this case when actually running Android. - ALOGW("CREATING STRING CACHE OF %d bytes", - mHeader->stringCount*sizeof(char16_t**)); + ALOGW("CREATING STRING CACHE OF %zu bytes", + static_cast<size_t>(mHeader->stringCount*sizeof(char16_t**))); #endif mCache = (char16_t**)calloc(mHeader->stringCount, sizeof(char16_t**)); if (mCache == NULL) { @@ -746,6 +757,13 @@ const char16_t* ResStringPool::stringAt(size_t idx, size_t* u16len) const return NULL; } + // Reject malformed (non null-terminated) strings + if (u8str[u8len] != 0x00) { + ALOGW("Bad string block: string #%d is not null-terminated", + (int)idx); + return NULL; + } + char16_t *u16str = (char16_t *)calloc(*u16len+1, sizeof(char16_t)); if (!u16str) { ALOGW("No memory when trying to allocate decode cache for string #%d\n", @@ -753,7 +771,9 @@ const char16_t* ResStringPool::stringAt(size_t idx, size_t* u16len) const return NULL; } - STRING_POOL_NOISY(ALOGI("Caching UTF8 string: %s", u8str)); + if (kDebugStringPoolNoisy) { + ALOGI("Caching UTF8 string: %s", u8str); + } utf8_to_utf16(u8str, u8len, u16str); mCache[idx] = u16str; return u16str; @@ -843,7 +863,9 @@ ssize_t ResStringPool::indexOfString(const char16_t* str, size_t strLen) const size_t len; if ((mHeader->flags&ResStringPool_header::UTF8_FLAG) != 0) { - STRING_POOL_NOISY(ALOGI("indexOfString UTF-8: %s", String8(str, strLen).string())); + if (kDebugStringPoolNoisy) { + ALOGI("indexOfString UTF-8: %s", String8(str, strLen).string()); + } // The string pool contains UTF 8 strings; we don't want to cause // temporary UTF-16 strings to be created as we search. @@ -869,10 +891,14 @@ ssize_t ResStringPool::indexOfString(const char16_t* str, size_t strLen) const } else { c = -1; } - STRING_POOL_NOISY(ALOGI("Looking at %s, cmp=%d, l/mid/h=%d/%d/%d\n", - (const char*)s, c, (int)l, (int)mid, (int)h)); + if (kDebugStringPoolNoisy) { + ALOGI("Looking at %s, cmp=%d, l/mid/h=%d/%d/%d\n", + (const char*)s, c, (int)l, (int)mid, (int)h); + } if (c == 0) { - STRING_POOL_NOISY(ALOGI("MATCH!")); + if (kDebugStringPoolNoisy) { + ALOGI("MATCH!"); + } free(convBuffer); return mid; } else if (c < 0) { @@ -891,18 +917,22 @@ ssize_t ResStringPool::indexOfString(const char16_t* str, size_t strLen) const const size_t str8Len = str8.size(); for (int i=mHeader->stringCount-1; i>=0; i--) { const char* s = string8At(i, &len); - STRING_POOL_NOISY(ALOGI("Looking at %s, i=%d\n", - String8(s).string(), - i)); + if (kDebugStringPoolNoisy) { + ALOGI("Looking at %s, i=%d\n", String8(s).string(), i); + } if (s && str8Len == len && memcmp(s, str8.string(), str8Len) == 0) { - STRING_POOL_NOISY(ALOGI("MATCH!")); + if (kDebugStringPoolNoisy) { + ALOGI("MATCH!"); + } return i; } } } } else { - STRING_POOL_NOISY(ALOGI("indexOfString UTF-16: %s", String8(str, strLen).string())); + if (kDebugStringPoolNoisy) { + ALOGI("indexOfString UTF-16: %s", String8(str, strLen).string()); + } if (mHeader->flags&ResStringPool_header::SORTED_FLAG) { // Do a binary search for the string... @@ -914,11 +944,14 @@ ssize_t ResStringPool::indexOfString(const char16_t* str, size_t strLen) const mid = l + (h - l)/2; const char16_t* s = stringAt(mid, &len); int c = s ? strzcmp16(s, len, str, strLen) : -1; - STRING_POOL_NOISY(ALOGI("Looking at %s, cmp=%d, l/mid/h=%d/%d/%d\n", - String8(s).string(), - c, (int)l, (int)mid, (int)h)); + if (kDebugStringPoolNoisy) { + ALOGI("Looking at %s, cmp=%d, l/mid/h=%d/%d/%d\n", + String8(s).string(), c, (int)l, (int)mid, (int)h); + } if (c == 0) { - STRING_POOL_NOISY(ALOGI("MATCH!")); + if (kDebugStringPoolNoisy) { + ALOGI("MATCH!"); + } return mid; } else if (c < 0) { l = mid + 1; @@ -933,11 +966,13 @@ ssize_t ResStringPool::indexOfString(const char16_t* str, size_t strLen) const // block, start searching at the back. for (int i=mHeader->stringCount-1; i>=0; i--) { const char16_t* s = stringAt(i, &len); - STRING_POOL_NOISY(ALOGI("Looking at %s, i=%d\n", - String8(s).string(), - i)); + if (kDebugStringPoolNoisy) { + ALOGI("Looking at %s, i=%d\n", String8(s).string(), i); + } if (s && strLen == len && strzcmp16(s, len, str, strLen) == 0) { - STRING_POOL_NOISY(ALOGI("MATCH!")); + if (kDebugStringPoolNoisy) { + ALOGI("MATCH!"); + } return i; } } @@ -1138,7 +1173,9 @@ const char16_t* ResXMLParser::getAttributeNamespace(size_t idx, size_t* outLen) { int32_t id = getAttributeNamespaceID(idx); //printf("attribute namespace=%d idx=%d event=%p\n", id, idx, mEventCode); - //XML_NOISY(printf("getAttributeNamespace 0x%x=0x%x\n", idx, id)); + if (kDebugXMLNoisy) { + printf("getAttributeNamespace 0x%zx=0x%x\n", idx, id); + } return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; } @@ -1146,7 +1183,9 @@ const char* ResXMLParser::getAttributeNamespace8(size_t idx, size_t* outLen) con { int32_t id = getAttributeNamespaceID(idx); //printf("attribute namespace=%d idx=%d event=%p\n", id, idx, mEventCode); - //XML_NOISY(printf("getAttributeNamespace 0x%x=0x%x\n", idx, id)); + if (kDebugXMLNoisy) { + printf("getAttributeNamespace 0x%zx=0x%x\n", idx, id); + } return id >= 0 ? mTree.mStrings.string8At(id, outLen) : NULL; } @@ -1169,7 +1208,9 @@ const char16_t* ResXMLParser::getAttributeName(size_t idx, size_t* outLen) const { int32_t id = getAttributeNameID(idx); //printf("attribute name=%d idx=%d event=%p\n", id, idx, mEventCode); - //XML_NOISY(printf("getAttributeName 0x%x=0x%x\n", idx, id)); + if (kDebugXMLNoisy) { + printf("getAttributeName 0x%zx=0x%x\n", idx, id); + } return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; } @@ -1177,7 +1218,9 @@ const char* ResXMLParser::getAttributeName8(size_t idx, size_t* outLen) const { int32_t id = getAttributeNameID(idx); //printf("attribute name=%d idx=%d event=%p\n", id, idx, mEventCode); - //XML_NOISY(printf("getAttributeName 0x%x=0x%x\n", idx, id)); + if (kDebugXMLNoisy) { + printf("getAttributeName 0x%zx=0x%x\n", idx, id); + } return id >= 0 ? mTree.mStrings.string8At(id, outLen) : NULL; } @@ -1212,7 +1255,9 @@ int32_t ResXMLParser::getAttributeValueStringID(size_t idx) const const char16_t* ResXMLParser::getAttributeStringValue(size_t idx, size_t* outLen) const { int32_t id = getAttributeValueStringID(idx); - //XML_NOISY(printf("getAttributeValue 0x%x=0x%x\n", idx, id)); + if (kDebugXMLNoisy) { + printf("getAttributeValue 0x%zx=0x%x\n", idx, id); + } return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; } @@ -1303,54 +1348,69 @@ ssize_t ResXMLParser::indexOfAttribute(const char16_t* ns, size_t nsLen, ns8 = String8(ns, nsLen); } attr8 = String8(attr, attrLen); - STRING_POOL_NOISY(ALOGI("indexOfAttribute UTF8 %s (%d) / %s (%d)", ns8.string(), nsLen, - attr8.string(), attrLen)); + if (kDebugStringPoolNoisy) { + ALOGI("indexOfAttribute UTF8 %s (%zu) / %s (%zu)", ns8.string(), nsLen, + attr8.string(), attrLen); + } for (size_t i=0; i<N; i++) { size_t curNsLen = 0, curAttrLen = 0; const char* curNs = getAttributeNamespace8(i, &curNsLen); const char* curAttr = getAttributeName8(i, &curAttrLen); - STRING_POOL_NOISY(ALOGI(" curNs=%s (%d), curAttr=%s (%d)", curNs, curNsLen, - curAttr, curAttrLen)); + if (kDebugStringPoolNoisy) { + ALOGI(" curNs=%s (%zu), curAttr=%s (%zu)", curNs, curNsLen, curAttr, curAttrLen); + } if (curAttr != NULL && curNsLen == nsLen && curAttrLen == attrLen && memcmp(attr8.string(), curAttr, attrLen) == 0) { if (ns == NULL) { if (curNs == NULL) { - STRING_POOL_NOISY(ALOGI(" FOUND!")); + if (kDebugStringPoolNoisy) { + ALOGI(" FOUND!"); + } return i; } } else if (curNs != NULL) { //printf(" --> ns=%s, curNs=%s\n", // String8(ns).string(), String8(curNs).string()); if (memcmp(ns8.string(), curNs, nsLen) == 0) { - STRING_POOL_NOISY(ALOGI(" FOUND!")); + if (kDebugStringPoolNoisy) { + ALOGI(" FOUND!"); + } return i; } } } } } else { - STRING_POOL_NOISY(ALOGI("indexOfAttribute UTF16 %s (%d) / %s (%d)", - String8(ns, nsLen).string(), nsLen, - String8(attr, attrLen).string(), attrLen)); + if (kDebugStringPoolNoisy) { + ALOGI("indexOfAttribute UTF16 %s (%zu) / %s (%zu)", + String8(ns, nsLen).string(), nsLen, + String8(attr, attrLen).string(), attrLen); + } for (size_t i=0; i<N; i++) { size_t curNsLen = 0, curAttrLen = 0; const char16_t* curNs = getAttributeNamespace(i, &curNsLen); const char16_t* curAttr = getAttributeName(i, &curAttrLen); - STRING_POOL_NOISY(ALOGI(" curNs=%s (%d), curAttr=%s (%d)", - String8(curNs, curNsLen).string(), curNsLen, - String8(curAttr, curAttrLen).string(), curAttrLen)); + if (kDebugStringPoolNoisy) { + ALOGI(" curNs=%s (%zu), curAttr=%s (%zu)", + String8(curNs, curNsLen).string(), curNsLen, + String8(curAttr, curAttrLen).string(), curAttrLen); + } if (curAttr != NULL && curNsLen == nsLen && curAttrLen == attrLen && (memcmp(attr, curAttr, attrLen*sizeof(char16_t)) == 0)) { if (ns == NULL) { if (curNs == NULL) { - STRING_POOL_NOISY(ALOGI(" FOUND!")); + if (kDebugStringPoolNoisy) { + ALOGI(" FOUND!"); + } return i; } } else if (curNs != NULL) { //printf(" --> ns=%s, curNs=%s\n", // String8(ns).string(), String8(curNs).string()); if (memcmp(ns, curNs, nsLen*sizeof(char16_t)) == 0) { - STRING_POOL_NOISY(ALOGI(" FOUND!")); + if (kDebugStringPoolNoisy) { + ALOGI(" FOUND!"); + } return i; } } @@ -1398,7 +1458,9 @@ ResXMLParser::event_code_t ResXMLParser::nextNode() do { const ResXMLTree_node* next = (const ResXMLTree_node*) (((const uint8_t*)mCurNode) + dtohl(mCurNode->header.size)); - //ALOGW("Next node: prev=%p, next=%p\n", mCurNode, next); + if (kDebugXMLNoisy) { + ALOGI("Next node: prev=%p, next=%p\n", mCurNode, next); + } if (((const uint8_t*)next) >= mTree.mDataEnd) { mCurNode = NULL; @@ -1475,7 +1537,9 @@ ResXMLTree::ResXMLTree(const DynamicRefTable* dynamicRefTable) , mDynamicRefTable(dynamicRefTable) , mError(NO_INIT), mOwnedData(NULL) { - //ALOGI("Creating ResXMLTree %p #%d\n", this, android_atomic_inc(&gCount)+1); + if (kDebugResXMLTree) { + ALOGI("Creating ResXMLTree %p #%d\n", this, android_atomic_inc(&gCount)+1); + } restart(); } @@ -1484,13 +1548,17 @@ ResXMLTree::ResXMLTree() , mDynamicRefTable(NULL) , mError(NO_INIT), mOwnedData(NULL) { - //ALOGI("Creating ResXMLTree %p #%d\n", this, android_atomic_inc(&gCount)+1); + if (kDebugResXMLTree) { + ALOGI("Creating ResXMLTree %p #%d\n", this, android_atomic_inc(&gCount)+1); + } restart(); } ResXMLTree::~ResXMLTree() { - //ALOGI("Destroying ResXMLTree in %p #%d\n", this, android_atomic_dec(&gCount)-1); + if (kDebugResXMLTree) { + ALOGI("Destroying ResXMLTree in %p #%d\n", this, android_atomic_dec(&gCount)-1); + } uninit(); } @@ -1543,8 +1611,10 @@ status_t ResXMLTree::setTo(const void* data, size_t size, bool copyData) } const uint16_t type = dtohs(chunk->type); const size_t size = dtohl(chunk->size); - XML_NOISY(printf("Scanning @ %p: type=0x%x, size=0x%x\n", - (void*)(((uint32_t)chunk)-((uint32_t)mHeader)), type, size)); + if (kDebugXMLNoisy) { + printf("Scanning @ %p: type=0x%x, size=0x%zx\n", + (void*)(((uintptr_t)chunk)-((uintptr_t)mHeader)), type, size); + } if (type == RES_STRING_POOL_TYPE) { mStrings.setTo(chunk, size); } else if (type == RES_XML_RESOURCE_MAP_TYPE) { @@ -1567,7 +1637,9 @@ status_t ResXMLTree::setTo(const void* data, size_t size, bool copyData) mRootCode = mEventCode; break; } else { - XML_NOISY(printf("Skipping unknown chunk!\n")); + if (kDebugXMLNoisy) { + printf("Skipping unknown chunk!\n"); + } } lastChunk = chunk; chunk = (const ResChunk_header*) @@ -2151,9 +2223,11 @@ bool ResTable_config::isBetterThan(const ResTable_config& o, myDelta += requested->screenHeightDp - screenHeightDp; otherDelta += requested->screenHeightDp - o.screenHeightDp; } - //ALOGI("Comparing this %dx%d to other %dx%d in %dx%d: myDelta=%d otherDelta=%d", - // screenWidthDp, screenHeightDp, o.screenWidthDp, o.screenHeightDp, - // requested->screenWidthDp, requested->screenHeightDp, myDelta, otherDelta); + if (kDebugTableSuperNoisy) { + ALOGI("Comparing this %dx%d to other %dx%d in %dx%d: myDelta=%d otherDelta=%d", + screenWidthDp, screenHeightDp, o.screenWidthDp, o.screenHeightDp, + requested->screenWidthDp, requested->screenHeightDp, myDelta, otherDelta); + } if (myDelta != otherDelta) { return myDelta < otherDelta; } @@ -2408,11 +2482,17 @@ bool ResTable_config::match(const ResTable_config& settings) const { } if (screenSizeDp != 0) { if (screenWidthDp != 0 && screenWidthDp > settings.screenWidthDp) { - //ALOGI("Filtering out width %d in requested %d", screenWidthDp, settings.screenWidthDp); + if (kDebugTableSuperNoisy) { + ALOGI("Filtering out width %d in requested %d", screenWidthDp, + settings.screenWidthDp); + } return false; } if (screenHeightDp != 0 && screenHeightDp > settings.screenHeightDp) { - //ALOGI("Filtering out height %d in requested %d", screenHeightDp, settings.screenHeightDp); + if (kDebugTableSuperNoisy) { + ALOGI("Filtering out height %d in requested %d", screenHeightDp, + settings.screenHeightDp); + } return false; } } @@ -2432,9 +2512,13 @@ bool ResTable_config::match(const ResTable_config& settings) const { // For compatibility, we count a request for KEYSHIDDEN_NO as also // matching the more recent KEYSHIDDEN_SOFT. Basically // KEYSHIDDEN_NO means there is some kind of keyboard available. - //ALOGI("Matching keysHidden: have=%d, config=%d\n", keysHidden, setKeysHidden); + if (kDebugTableSuperNoisy) { + ALOGI("Matching keysHidden: have=%d, config=%d\n", keysHidden, setKeysHidden); + } if (keysHidden != KEYSHIDDEN_NO || setKeysHidden != KEYSHIDDEN_SOFT) { - //ALOGI("No match!"); + if (kDebugTableSuperNoisy) { + ALOGI("No match!"); + } return false; } } @@ -2469,6 +2553,58 @@ bool ResTable_config::match(const ResTable_config& settings) const { return true; } +void ResTable_config::appendDirLocale(String8& out) const { + if (!language[0]) { + return; + } + + if (!localeScript[0] && !localeVariant[0]) { + // Legacy format. + if (out.size() > 0) { + out.append("-"); + } + + char buf[4]; + size_t len = unpackLanguage(buf); + out.append(buf, len); + + if (country[0]) { + out.append("-r"); + len = unpackRegion(buf); + out.append(buf, len); + } + return; + } + + // We are writing the modified bcp47 tag. + // It starts with 'b+' and uses '+' as a separator. + + if (out.size() > 0) { + out.append("-"); + } + out.append("b+"); + + char buf[4]; + size_t len = unpackLanguage(buf); + out.append(buf, len); + + if (localeScript[0]) { + out.append("+"); + out.append(localeScript, sizeof(localeScript)); + } + + if (country[0]) { + out.append("+"); + len = unpackRegion(buf); + out.append(buf, len); + } + + if (localeVariant[0]) { + out.append("+"); + out.append(localeVariant, sizeof(localeVariant)); + } +} + void ResTable_config::getBcp47Locale(char str[RESTABLE_MAX_LOCALE_LEN]) const { memset(str, 0, RESTABLE_MAX_LOCALE_LEN); @@ -2569,12 +2705,7 @@ String8 ResTable_config::toString() const { res.appendFormat("mnc%d", dtohs(mnc)); } - char localeStr[RESTABLE_MAX_LOCALE_LEN]; - getBcp47Locale(localeStr); - if (strlen(localeStr) > 0) { - if (res.size() > 0) res.append("-"); - res.append(localeStr); - } + appendDirLocale(res); if ((screenLayout&MASK_LAYOUTDIR) != 0) { if (res.size() > 0) res.append("-"); @@ -2939,17 +3070,25 @@ struct ResTable::PackageGroup void clearBagCache() { if (bags) { - TABLE_NOISY(printf("bags=%p\n", bags)); + if (kDebugTableNoisy) { + printf("bags=%p\n", bags); + } for (size_t i = 0; i < bags->size(); i++) { - TABLE_NOISY(printf("type=%d\n", i)); + if (kDebugTableNoisy) { + printf("type=%zu\n", i); + } const TypeList& typeList = types[i]; if (!typeList.isEmpty()) { bag_set** typeBags = bags->get(i); - TABLE_NOISY(printf("typeBags=%p\n", typeBags)); + if (kDebugTableNoisy) { + printf("typeBags=%p\n", typeBags); + } if (typeBags) { const size_t N = typeList[0]->entryCount; - TABLE_NOISY(printf("type->entryCount=%x\n", N)); - for (size_t j=0; j<N; j++) { + if (kDebugTableNoisy) { + printf("type->entryCount=%zu\n", N); + } + for (size_t j = 0; j < N; j++) { if (typeBags[j] && typeBags[j] != (bag_set*)0xFFFFFFFF) free(typeBags[j]); } @@ -3040,7 +3179,8 @@ ResTable::Theme::package_info* ResTable::Theme::copy_package(package_info* pi) size_t cnt = pi->types[j].numEntries; newpi->types[j].numEntries = cnt; theme_entry* te = pi->types[j].entries; - if (te != NULL) { + size_t cnt_max = SIZE_MAX / sizeof(theme_entry); + if (te != NULL && (cnt < 0xFFFFFFFF-1) && (cnt < cnt_max)) { theme_entry* newte = (theme_entry*)malloc(cnt*sizeof(theme_entry)); newpi->types[j].entries = newte; memcpy(newte, te, cnt*sizeof(theme_entry)); @@ -3057,7 +3197,9 @@ status_t ResTable::Theme::applyStyle(uint32_t resID, bool force) uint32_t bagTypeSpecFlags = 0; mTable.lock(); const ssize_t N = mTable.getBagLocked(resID, &bag, &bagTypeSpecFlags); - TABLE_NOISY(ALOGV("Applying style 0x%08x to theme %p, count=%d", resID, this, N)); + if (kDebugTableNoisy) { + ALOGV("Applying style 0x%08x to theme %p, count=%zu", resID, this, N); + } if (N < 0) { mTable.unlock(); return N; @@ -3088,7 +3230,6 @@ status_t ResTable::Theme::applyStyle(uint32_t resID, bool force) curPackageIndex = pidx; curPI = mPackages[pidx]; if (curPI == NULL) { - PackageGroup* const grp = mTable.mPackageGroups[pidx]; curPI = (package_info*)malloc(sizeof(package_info)); memset(curPI, 0, sizeof(*curPI)); mPackages[pidx] = curPI; @@ -3106,9 +3247,12 @@ status_t ResTable::Theme::applyStyle(uint32_t resID, bool force) if (curEntries == NULL) { PackageGroup* const grp = mTable.mPackageGroups[curPackageIndex]; const TypeList& typeList = grp->types[t]; - int cnt = typeList.isEmpty() ? 0 : typeList[0]->entryCount; - curEntries = (theme_entry*)malloc(cnt*sizeof(theme_entry)); - memset(curEntries, Res_value::TYPE_NULL, cnt*sizeof(theme_entry)); + size_t cnt = typeList.isEmpty() ? 0 : typeList[0]->entryCount; + size_t cnt_max = SIZE_MAX / sizeof(theme_entry); + size_t buff_size = (cnt < cnt_max && cnt < 0xFFFFFFFF-1) ? + cnt*sizeof(theme_entry) : 0; + curEntries = (theme_entry*)malloc(buff_size); + memset(curEntries, Res_value::TYPE_NULL, buff_size); curPI->types[t].numEntries = cnt; curPI->types[t].entries = curEntries; } @@ -3120,9 +3264,11 @@ status_t ResTable::Theme::applyStyle(uint32_t resID, bool force) continue; } theme_entry* curEntry = curEntries + e; - TABLE_NOISY(ALOGV("Attr 0x%08x: type=0x%x, data=0x%08x; curType=0x%x", - attrRes, bag->map.value.dataType, bag->map.value.data, - curEntry->value.dataType)); + if (kDebugTableNoisy) { + ALOGV("Attr 0x%08x: type=0x%x, data=0x%08x; curType=0x%x", + attrRes, bag->map.value.dataType, bag->map.value.data, + curEntry->value.dataType); + } if (force || curEntry->value.dataType == Res_value::TYPE_NULL) { curEntry->stringBlock = bag->stringBlock; curEntry->typeSpecFlags |= bagTypeSpecFlags; @@ -3134,17 +3280,21 @@ status_t ResTable::Theme::applyStyle(uint32_t resID, bool force) mTable.unlock(); - //ALOGI("Applying style 0x%08x (force=%d) theme %p...\n", resID, force, this); - //dumpToLog(); + if (kDebugTableTheme) { + ALOGI("Applying style 0x%08x (force=%d) theme %p...\n", resID, force, this); + dumpToLog(); + } return NO_ERROR; } status_t ResTable::Theme::setTo(const Theme& other) { - //ALOGI("Setting theme %p from theme %p...\n", this, &other); - //dumpToLog(); - //other.dumpToLog(); + if (kDebugTableTheme) { + ALOGI("Setting theme %p from theme %p...\n", this, &other); + dumpToLog(); + other.dumpToLog(); + } if (&mTable == &other.mTable) { for (size_t i=0; i<Res_MAXPACKAGE; i++) { @@ -3173,8 +3323,10 @@ status_t ResTable::Theme::setTo(const Theme& other) } } - //ALOGI("Final theme:"); - //dumpToLog(); + if (kDebugTableTheme) { + ALOGI("Final theme:"); + dumpToLog(); + } return NO_ERROR; } @@ -3191,23 +3343,33 @@ ssize_t ResTable::Theme::getAttribute(uint32_t resID, Res_value* outValue, const uint32_t t = Res_GETTYPE(resID); const uint32_t e = Res_GETENTRY(resID); - TABLE_THEME(ALOGI("Looking up attr 0x%08x in theme %p", resID, this)); + if (kDebugTableTheme) { + ALOGI("Looking up attr 0x%08x in theme %p", resID, this); + } if (p >= 0) { const package_info* const pi = mPackages[p]; - TABLE_THEME(ALOGI("Found package: %p", pi)); + if (kDebugTableTheme) { + ALOGI("Found package: %p", pi); + } if (pi != NULL) { - TABLE_THEME(ALOGI("Desired type index is %ld in avail %d", t, Res_MAXTYPE + 1)); + if (kDebugTableTheme) { + ALOGI("Desired type index is %zd in avail %zu", t, Res_MAXTYPE + 1); + } if (t <= Res_MAXTYPE) { const type_info& ti = pi->types[t]; - TABLE_THEME(ALOGI("Desired entry index is %ld in avail %d", e, ti.numEntries)); + if (kDebugTableTheme) { + ALOGI("Desired entry index is %u in avail %zu", e, ti.numEntries); + } if (e < ti.numEntries) { const theme_entry& te = ti.entries[e]; if (outTypeSpecFlags != NULL) { *outTypeSpecFlags |= te.typeSpecFlags; } - TABLE_THEME(ALOGI("Theme value: type=0x%x, data=0x%08x", - te.value.dataType, te.value.data)); + if (kDebugTableTheme) { + ALOGI("Theme value: type=0x%x, data=0x%08x", + te.value.dataType, te.value.data); + } const uint8_t type = te.value.dataType; if (type == Res_value::TYPE_ATTRIBUTE) { if (cnt > 0) { @@ -3241,8 +3403,10 @@ ssize_t ResTable::Theme::resolveAttributeReference(Res_value* inOutValue, if (inOutValue->dataType == Res_value::TYPE_ATTRIBUTE) { uint32_t newTypeSpecFlags; blockIndex = getAttribute(inOutValue->data, inOutValue, &newTypeSpecFlags); - TABLE_THEME(ALOGI("Resolving attr reference: blockIndex=%d, type=0x%x, data=%p\n", - (int)blockIndex, (int)inOutValue->dataType, (void*)inOutValue->data)); + if (kDebugTableTheme) { + ALOGI("Resolving attr reference: blockIndex=%d, type=0x%x, data=0x%x\n", + (int)blockIndex, (int)inOutValue->dataType, inOutValue->data); + } if (inoutTypeSpecFlags != NULL) *inoutTypeSpecFlags |= newTypeSpecFlags; //printf("Retrieved attribute new type=0x%x\n", inOutValue->dataType); if (blockIndex < 0) { @@ -3281,7 +3445,9 @@ ResTable::ResTable() { memset(&mParams, 0, sizeof(mParams)); memset(mPackageMap, 0, sizeof(mPackageMap)); - //ALOGI("Creating ResTable %p\n", this); + if (kDebugTableSuperNoisy) { + ALOGI("Creating ResTable %p\n", this); + } } ResTable::ResTable(const void* data, size_t size, const int32_t cookie, bool copyData) @@ -3291,12 +3457,16 @@ ResTable::ResTable(const void* data, size_t size, const int32_t cookie, bool cop memset(mPackageMap, 0, sizeof(mPackageMap)); addInternal(data, size, NULL, 0, cookie, copyData); LOG_FATAL_IF(mError != NO_ERROR, "Error parsing resource table"); - //ALOGI("Creating ResTable %p\n", this); + if (kDebugTableSuperNoisy) { + ALOGI("Creating ResTable %p\n", this); + } } ResTable::~ResTable() { - //ALOGI("Destroying ResTable in %p\n", this); + if (kDebugTableSuperNoisy) { + ALOGI("Destroying ResTable in %p\n", this); + } uninit(); } @@ -3425,9 +3595,10 @@ status_t ResTable::addInternal(const void* data, size_t dataSize, const void* id const bool notDeviceEndian = htods(0xf0) != 0xf0; - LOAD_TABLE_NOISY( - ALOGV("Adding resources to ResTable: data=%p, size=0x%x, cookie=%d, copy=%d " - "idmap=%p\n", data, dataSize, cookie, copyData, idmap)); + if (kDebugLoadTableNoisy) { + ALOGV("Adding resources to ResTable: data=%p, size=%zu, cookie=%d, copy=%d " + "idmap=%p\n", data, dataSize, cookie, copyData, idmapData); + } if (copyData || notDeviceEndian) { header->ownedData = malloc(dataSize); @@ -3440,9 +3611,13 @@ status_t ResTable::addInternal(const void* data, size_t dataSize, const void* id header->header = (const ResTable_header*)data; header->size = dtohl(header->header->header.size); - //ALOGI("Got size 0x%x, again size 0x%x, raw size 0x%x\n", header->size, - // dtohl(header->header->header.size), header->header->header.size); - LOAD_TABLE_NOISY(ALOGV("Loading ResTable @%p:\n", header->header)); + if (kDebugLoadTableSuperNoisy) { + ALOGI("Got size %zu, again size 0x%x, raw size 0x%x\n", header->size, + dtohl(header->header->header.size), header->header->header.size); + } + if (kDebugLoadTableNoisy) { + ALOGV("Loading ResTable @%p:\n", header->header); + } if (dtohs(header->header->header.headerSize) > header->size || header->size > dataSize) { ALOGW("Bad resource table: header size 0x%x or total size 0x%x is larger than data size 0x%x\n", @@ -3470,9 +3645,11 @@ status_t ResTable::addInternal(const void* data, size_t dataSize, const void* id if (err != NO_ERROR) { return (mError=err); } - TABLE_NOISY(ALOGV("Chunk: type=0x%x, headerSize=0x%x, size=0x%x, pos=%p\n", - dtohs(chunk->type), dtohs(chunk->headerSize), dtohl(chunk->size), - (void*)(((const uint8_t*)chunk) - ((const uint8_t*)header->header)))); + if (kDebugTableNoisy) { + ALOGV("Chunk: type=0x%x, headerSize=0x%x, size=0x%x, pos=%p\n", + dtohs(chunk->type), dtohs(chunk->headerSize), dtohl(chunk->size), + (void*)(((const uint8_t*)chunk) - ((const uint8_t*)header->header))); + } const size_t csize = dtohl(chunk->size); const uint16_t ctype = dtohs(chunk->type); if (ctype == RES_STRING_POOL_TYPE) { @@ -3516,7 +3693,9 @@ status_t ResTable::addInternal(const void* data, size_t dataSize, const void* id ALOGW("No string values found in resource table!"); } - TABLE_NOISY(ALOGV("Returning from add with mError=%d\n", mError)); + if (kDebugTableNoisy) { + ALOGV("Returning from add with mError=%d\n", mError); + } return mError; } @@ -3682,15 +3861,16 @@ ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag return BAD_VALUE; } - TABLE_NOISY(size_t len; - printf("Found value: pkg=%d, type=%d, str=%s, int=%d\n", - entry.package->header->index, - outValue->dataType, - outValue->dataType == Res_value::TYPE_STRING - ? String8(entry.package->header->values.stringAt( - outValue->data, &len)).string() - : "", - outValue->data)); + if (kDebugTableNoisy) { + size_t len; + printf("Found value: pkg=%zu, type=%d, str=%s, int=%d\n", + entry.package->header->index, + outValue->dataType, + outValue->dataType == Res_value::TYPE_STRING ? + String8(entry.package->header->values.stringAt(outValue->data, &len)).string() : + "", + outValue->data); + } if (outSpecFlags != NULL) { *outSpecFlags = entry.specFlags; @@ -3711,15 +3891,16 @@ ssize_t ResTable::resolveReference(Res_value* value, ssize_t blockIndex, while (blockIndex >= 0 && value->dataType == Res_value::TYPE_REFERENCE && value->data != 0 && count < 20) { if (outLastRef) *outLastRef = value->data; - uint32_t lastRef = value->data; uint32_t newFlags = 0; const ssize_t newIndex = getResource(value->data, value, true, 0, &newFlags, outConfig); if (newIndex == BAD_INDEX) { return BAD_INDEX; } - TABLE_THEME(ALOGI("Resolving reference %p: newIndex=%d, type=0x%x, data=%p\n", - (void*)lastRef, (int)newIndex, (int)value->dataType, (void*)value->data)); + if (kDebugTableTheme) { + ALOGI("Resolving reference 0x%x: newIndex=%d, type=0x%x, data=0x%x\n", + value->data, (int)newIndex, (int)value->dataType, value->data); + } //printf("Getting reference 0x%08x: newIndex=%d\n", value->data, newIndex); if (inoutTypeSpecFlags != NULL) *inoutTypeSpecFlags |= newFlags; if (newIndex < 0) { @@ -3826,7 +4007,9 @@ ssize_t ResTable::getBagLocked(uint32_t resID, const bag_entry** outBag, *outTypeSpecFlags = set->typeSpecFlags; } *outBag = (bag_entry*)(set+1); - //ALOGI("Found existing bag for: %p\n", (void*)resID); + if (kDebugTableSuperNoisy) { + ALOGI("Found existing bag for: 0x%x\n", resID); + } return set->numAttrs; } ALOGW("Attempt to retrieve bag 0x%08x which is invalid or in a cycle.", @@ -3852,7 +4035,9 @@ ssize_t ResTable::getBagLocked(uint32_t resID, const bag_entry** outBag, // Mark that we are currently working on this one. typeSet[e] = (bag_set*)0xFFFFFFFF; - TABLE_NOISY(ALOGI("Building bag: %p\n", (void*)resID)); + if (kDebugTableNoisy) { + ALOGI("Building bag: %x\n", resID); + } // Now collect all bag attributes Entry entry; @@ -3869,13 +4054,13 @@ ssize_t ResTable::getBagLocked(uint32_t resID, const bag_entry** outBag, size_t N = count; - TABLE_NOISY(ALOGI("Found map: size=%p parent=%p count=%d\n", - entrySize, parent, count)); + if (kDebugTableNoisy) { + ALOGI("Found map: size=%x parent=%x count=%d\n", entrySize, parent, count); // If this map inherits from another, we need to start // with its parent's values. Otherwise start out empty. - TABLE_NOISY(printf("Creating new bag, entrySize=0x%08x, parent=0x%08x\n", - entrySize, parent)); + ALOGI("Creating new bag, entrySize=0x%08x, parent=0x%08x\n", entrySize, parent); + } // This is what we are building. bag_set* set = NULL; @@ -3904,9 +4089,13 @@ ssize_t ResTable::getBagLocked(uint32_t resID, const bag_entry** outBag, if (NP > 0) { memcpy(set+1, parentBag, NP*sizeof(bag_entry)); set->numAttrs = NP; - TABLE_NOISY(ALOGI("Initialized new bag with %d inherited attributes.\n", NP)); + if (kDebugTableNoisy) { + ALOGI("Initialized new bag with %zd inherited attributes.\n", NP); + } } else { - TABLE_NOISY(ALOGI("Initialized new bag with no inherited attributes.\n")); + if (kDebugTableNoisy) { + ALOGI("Initialized new bag with no inherited attributes.\n"); + } set->numAttrs = 0; } set->availAttrs = NT; @@ -3930,10 +4119,13 @@ ssize_t ResTable::getBagLocked(uint32_t resID, const bag_entry** outBag, bag_entry* entries = (bag_entry*)(set+1); size_t curEntry = 0; uint32_t pos = 0; - TABLE_NOISY(ALOGI("Starting with set %p, entries=%p, avail=%d\n", - set, entries, set->availAttrs)); + if (kDebugTableNoisy) { + ALOGI("Starting with set %p, entries=%p, avail=%zu\n", set, entries, set->availAttrs); + } while (pos < count) { - TABLE_NOISY(printf("Now at %p\n", (void*)curOff)); + if (kDebugTableNoisy) { + ALOGI("Now at %p\n", (void*)curOff); + } if (curOff > (dtohl(entry.type->header.size)-sizeof(ResTable_map))) { ALOGW("ResTable_map at %d is beyond type chunk data %d", @@ -3958,8 +4150,10 @@ ssize_t ResTable::getBagLocked(uint32_t resID, const bag_entry** outBag, uint32_t oldName = 0; while ((isInside=(curEntry < set->numAttrs)) && (oldName=entries[curEntry].map.name.ident) < newName) { - TABLE_NOISY(printf("#%d: Keeping existing attribute: 0x%08x\n", - curEntry, entries[curEntry].map.name.ident)); + if (kDebugTableNoisy) { + ALOGI("#%zu: Keeping existing attribute: 0x%08x\n", + curEntry, entries[curEntry].map.name.ident); + } curEntry++; } @@ -3976,8 +4170,10 @@ ssize_t ResTable::getBagLocked(uint32_t resID, const bag_entry** outBag, } set->availAttrs = newAvail; entries = (bag_entry*)(set+1); - TABLE_NOISY(printf("Reallocated set %p, entries=%p, avail=%d\n", - set, entries, set->availAttrs)); + if (kDebugTableNoisy) { + ALOGI("Reallocated set %p, entries=%p, avail=%zu\n", + set, entries, set->availAttrs); + } } if (isInside) { // Going in the middle, need to make space. @@ -3985,11 +4181,13 @@ ssize_t ResTable::getBagLocked(uint32_t resID, const bag_entry** outBag, sizeof(bag_entry)*(set->numAttrs-curEntry)); set->numAttrs++; } - TABLE_NOISY(printf("#%d: Inserting new attribute: 0x%08x\n", - curEntry, newName)); + if (kDebugTableNoisy) { + ALOGI("#%zu: Inserting new attribute: 0x%08x\n", curEntry, newName); + } } else { - TABLE_NOISY(printf("#%d: Replacing existing attribute: 0x%08x\n", - curEntry, oldName)); + if (kDebugTableNoisy) { + ALOGI("#%zu: Replacing existing attribute: 0x%08x\n", curEntry, oldName); + } } bag_entry* cur = entries+curEntry; @@ -4003,9 +4201,11 @@ ssize_t ResTable::getBagLocked(uint32_t resID, const bag_entry** outBag, return UNKNOWN_ERROR; } - TABLE_NOISY(printf("Setting entry #%d %p: block=%d, name=0x%08x, type=%d, data=0x%08x\n", - curEntry, cur, cur->stringBlock, cur->map.name.ident, - cur->map.value.dataType, cur->map.value.data)); + if (kDebugTableNoisy) { + ALOGI("Setting entry #%zu %p: block=%zd, name=0x%08d, type=%d, data=0x%08x\n", + curEntry, cur, cur->stringBlock, cur->map.name.ident, + cur->map.value.dataType, cur->map.value.data); + } // On to the next! curEntry++; @@ -4025,7 +4225,9 @@ ssize_t ResTable::getBagLocked(uint32_t resID, const bag_entry** outBag, *outTypeSpecFlags = set->typeSpecFlags; } *outBag = (bag_entry*)(set+1); - TABLE_NOISY(ALOGI("Returning %d attrs\n", set->numAttrs)); + if (kDebugTableNoisy) { + ALOGI("Returning %zu attrs\n", set->numAttrs); + } return set->numAttrs; } return BAD_INDEX; @@ -4034,10 +4236,14 @@ ssize_t ResTable::getBagLocked(uint32_t resID, const bag_entry** outBag, void ResTable::setParameters(const ResTable_config* params) { mLock.lock(); - TABLE_GETENTRY(ALOGI("Setting parameters: %s\n", params->toString().string())); + if (kDebugTableGetEntry) { + ALOGI("Setting parameters: %s\n", params->toString().string()); + } mParams = *params; for (size_t i=0; i<mPackageGroups.size(); i++) { - TABLE_NOISY(ALOGI("CLEARING BAGS FOR GROUP %d!", i)); + if (kDebugTableNoisy) { + ALOGI("CLEARING BAGS FOR GROUP %zu!", i); + } mPackageGroups[i]->clearBagCache(); } mLock.unlock(); @@ -4075,7 +4281,9 @@ uint32_t ResTable::identifierForName(const char16_t* name, size_t nameLen, size_t packageLen, uint32_t* outTypeSpecFlags) const { - TABLE_SUPER_NOISY(printf("Identifier for name: error=%d\n", mError)); + if (kDebugTableSuperNoisy) { + printf("Identifier for name: error=%d\n", mError); + } // Check for internal resource identifier as the very first thing, so // that we will always find them even when there are no resources. @@ -4168,10 +4376,12 @@ nope: } nameLen = nameEnd-name; - TABLE_NOISY(printf("Looking for identifier: type=%s, name=%s, package=%s\n", - String8(type, typeLen).string(), - String8(name, nameLen).string(), - String8(package, packageLen).string())); + if (kDebugTableNoisy) { + printf("Looking for identifier: type=%s, name=%s, package=%s\n", + String8(type, typeLen).string(), + String8(name, nameLen).string(), + String8(package, packageLen).string()); + } const String16 attr("attr"); const String16 attrPrivate("^attr-private"); @@ -4182,7 +4392,9 @@ nope: if (strzcmp16(package, packageLen, group->name.string(), group->name.size())) { - TABLE_NOISY(printf("Skipping package group: %s\n", String8(group->name).string())); + if (kDebugTableNoisy) { + printf("Skipping package group: %s\n", String8(group->name).string()); + } continue; } @@ -4406,8 +4618,7 @@ static bool parse_unit(const char* str, Res_value* outValue, return false; } - -bool ResTable::stringToInt(const char16_t* s, size_t len, Res_value* outValue) +bool U16StringToInt(const char16_t* s, size_t len, Res_value* outValue) { while (len > 0 && isspace16(*s)) { s++; @@ -4419,7 +4630,7 @@ bool ResTable::stringToInt(const char16_t* s, size_t len, Res_value* outValue) } size_t i = 0; - int32_t val = 0; + int64_t val = 0; bool neg = false; if (*s == '-') { @@ -4431,28 +4642,50 @@ bool ResTable::stringToInt(const char16_t* s, size_t len, Res_value* outValue) return false; } + static_assert(std::is_same<uint32_t, Res_value::data_type>::value, + "Res_value::data_type has changed. The range checks in this " + "function are no longer correct."); + // Decimal or hex? - if (s[i] == '0' && s[i+1] == 'x') { - if (outValue) - outValue->dataType = outValue->TYPE_INT_HEX; + bool isHex; + if (len > 1 && s[i] == '0' && s[i+1] == 'x') { + isHex = true; i += 2; + + if (neg) { + return false; + } + + if (i == len) { + // Just u"0x" + return false; + } + bool error = false; while (i < len && !error) { val = (val*16) + get_hex(s[i], &error); i++; + + if (val > std::numeric_limits<uint32_t>::max()) { + return false; + } } if (error) { return false; } } else { - if (outValue) - outValue->dataType = outValue->TYPE_INT_DEC; + isHex = false; while (i < len) { if (s[i] < '0' || s[i] > '9') { return false; } val = (val*10) + s[i]-'0'; i++; + + if ((neg && -val < std::numeric_limits<int32_t>::min()) || + (!neg && val > std::numeric_limits<int32_t>::max())) { + return false; + } } } @@ -4462,13 +4695,21 @@ bool ResTable::stringToInt(const char16_t* s, size_t len, Res_value* outValue) i++; } - if (i == len) { - if (outValue) - outValue->data = val; - return true; + if (i != len) { + return false; } - return false; + if (outValue) { + outValue->dataType = + isHex ? outValue->TYPE_INT_HEX : outValue->TYPE_INT_DEC; + outValue->data = static_cast<Res_value::data_type>(val); + } + return true; +} + +bool ResTable::stringToInt(const char16_t* s, size_t len, Res_value* outValue) +{ + return U16StringToInt(s, len, outValue); } bool ResTable::stringToFloat(const char16_t* s, size_t len, Res_value* outValue) @@ -4495,7 +4736,7 @@ bool ResTable::stringToFloat(const char16_t* s, size_t len, Res_value* outValue) if (len > 0) { return false; } - if (buf[0] < '0' && buf[0] > '9' && buf[0] != '.') { + if ((buf[0] < '0' || buf[0] > '9') && buf[0] != '.' && buf[0] != '-' && buf[0] != '+') { return false; } @@ -4712,9 +4953,11 @@ bool ResTable::stringToValue(Res_value* outValue, String16* outString, rid = Res_MAKEID( accessor->getRemappedPackage(Res_GETPACKAGE(rid)), Res_GETTYPE(rid), Res_GETENTRY(rid)); - TABLE_NOISY(printf("Incl %s:%s/%s: 0x%08x\n", - String8(package).string(), String8(type).string(), - String8(name).string(), rid)); + if (kDebugTableNoisy) { + ALOGI("Incl %s:%s/%s: 0x%08x\n", + String8(package).string(), String8(type).string(), + String8(name).string(), rid); + } } uint32_t packageId = Res_GETPACKAGE(rid) + 1; @@ -4729,9 +4972,11 @@ bool ResTable::stringToValue(Res_value* outValue, String16* outString, uint32_t rid = accessor->getCustomResourceWithCreation(package, type, name, createIfNotFound); if (rid != 0) { - TABLE_NOISY(printf("Pckg %s:%s/%s: 0x%08x\n", - String8(package).string(), String8(type).string(), - String8(name).string(), rid)); + if (kDebugTableNoisy) { + ALOGI("Pckg %s:%s/%s: 0x%08x\n", + String8(package).string(), String8(type).string(), + String8(name).string(), rid); + } uint32_t packageId = Res_GETPACKAGE(rid) + 1; if (packageId == 0x00) { outValue->data = rid; @@ -5540,8 +5785,6 @@ status_t ResTable::getEntry( } // Check if there is the desired entry in this type. - const uint8_t* const end = reinterpret_cast<const uint8_t*>(thisType) - + dtohl(thisType->header.size); const uint32_t* const eindex = reinterpret_cast<const uint32_t*>( reinterpret_cast<const uint8_t*>(thisType) + dtohs(thisType->header.headerSize)); @@ -5730,9 +5973,11 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg, const uint8_t* endPos = ((const uint8_t*)pkg) + dtohs(pkg->header.size); while (((const uint8_t*)chunk) <= (endPos-sizeof(ResChunk_header)) && ((const uint8_t*)chunk) <= (endPos-dtohl(chunk->size))) { - TABLE_NOISY(ALOGV("PackageChunk: type=0x%x, headerSize=0x%x, size=0x%x, pos=%p\n", - dtohs(chunk->type), dtohs(chunk->headerSize), dtohl(chunk->size), - (void*)(((const uint8_t*)chunk) - ((const uint8_t*)header->header)))); + if (kDebugTableNoisy) { + ALOGV("PackageChunk: type=0x%x, headerSize=0x%x, size=0x%x, pos=%p\n", + dtohs(chunk->type), dtohs(chunk->headerSize), dtohl(chunk->size), + (void*)(((const uint8_t*)chunk) - ((const uint8_t*)header->header))); + } const size_t csize = dtohl(chunk->size); const uint16_t ctype = dtohs(chunk->type); if (ctype == RES_TABLE_TYPE_SPEC_TYPE) { @@ -5746,11 +5991,13 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg, const size_t typeSpecSize = dtohl(typeSpec->header.size); const size_t newEntryCount = dtohl(typeSpec->entryCount); - LOAD_TABLE_NOISY(printf("TypeSpec off %p: type=0x%x, headerSize=0x%x, size=%p\n", - (void*)(base-(const uint8_t*)chunk), - dtohs(typeSpec->header.type), - dtohs(typeSpec->header.headerSize), - (void*)typeSpecSize)); + if (kDebugLoadTableNoisy) { + ALOGI("TypeSpec off %p: type=0x%x, headerSize=0x%x, size=%p\n", + (void*)(base-(const uint8_t*)chunk), + dtohs(typeSpec->header.type), + dtohs(typeSpec->header.headerSize), + (void*)typeSpecSize); + } // look for block overrun or int overflow when multiplying by 4 if ((dtohl(typeSpec->entryCount) > (INT32_MAX/sizeof(uint32_t)) || dtohs(typeSpec->header.headerSize)+(sizeof(uint32_t)*newEntryCount) @@ -5808,13 +6055,14 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg, const uint32_t typeSize = dtohl(type->header.size); const size_t newEntryCount = dtohl(type->entryCount); - LOAD_TABLE_NOISY(printf("Type off %p: type=0x%x, headerSize=0x%x, size=%p\n", - (void*)(base-(const uint8_t*)chunk), - dtohs(type->header.type), - dtohs(type->header.headerSize), - (void*)typeSize)); - if (dtohs(type->header.headerSize)+(sizeof(uint32_t)*newEntryCount) - > typeSize) { + if (kDebugLoadTableNoisy) { + printf("Type off %p: type=0x%x, headerSize=0x%x, size=%u\n", + (void*)(base-(const uint8_t*)chunk), + dtohs(type->header.type), + dtohs(type->header.headerSize), + typeSize); + } + if (dtohs(type->header.headerSize)+(sizeof(uint32_t)*newEntryCount) > typeSize) { ALOGW("ResTable_type entry index to %p extends beyond chunk end 0x%x.", (void*)(dtohs(type->header.headerSize) + (sizeof(uint32_t)*newEntryCount)), typeSize); @@ -5860,11 +6108,12 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg, t->configs.add(type); - TABLE_GETENTRY( + if (kDebugTableGetEntry) { ResTable_config thisConfig; thisConfig.copyFromDtoH(type->config); - ALOGI("Adding config to type %d: %s\n", - type->id, thisConfig.toString().string())); + ALOGI("Adding config to type %d: %s\n", type->id, + thisConfig.toString().string()); + } } else { ALOGV("Skipping empty ResTable_type for type %d", type->id); } @@ -5925,8 +6174,10 @@ status_t DynamicRefTable::load(const ResTable_lib_header* const header) uint32_t packageId = dtohl(entry->packageId); char16_t tmpName[sizeof(entry->packageName) / sizeof(char16_t)]; strcpy16_dtoh(tmpName, entry->packageName, sizeof(entry->packageName) / sizeof(char16_t)); - LIB_NOISY(ALOGV("Found lib entry %s with id %d\n", String8(tmpName).string(), - dtohl(entry->packageId))); + if (kDebugLibNoisy) { + ALOGV("Found lib entry %s with id %d\n", String8(tmpName).string(), + dtohl(entry->packageId)); + } if (packageId >= 256) { ALOGE("Bad package id 0x%08x", packageId); return UNKNOWN_ERROR; @@ -6109,20 +6360,20 @@ status_t ResTable::createIdmap(const ResTable& overlay, if (Res_GETTYPE(overlayResID) + 1 != static_cast<size_t>(typeMap.overlayTypeId)) { ALOGE("idmap: can't mix type ids in entry map. Resource 0x%08x maps to 0x%08x" - " but entries should map to resources of type %02x", + " but entries should map to resources of type %02zx", resID, overlayResID, typeMap.overlayTypeId); return BAD_TYPE; } if (typeMap.entryOffset + typeMap.entryMap.size() < entryIndex) { - // Resize to accomodate this entry and the 0's in between. - if (typeMap.entryMap.resize((entryIndex - typeMap.entryOffset) + 1) < 0) { + // pad with 0xffffffff's (indicating non-existing entries) before adding this entry + size_t index = typeMap.entryMap.size(); + size_t numItems = entryIndex - (typeMap.entryOffset + index); + if (typeMap.entryMap.insertAt(0xffffffff, index, numItems) < 0) { return NO_MEMORY; } - typeMap.entryMap.editTop() = Res_GETENTRY(overlayResID); - } else { - typeMap.entryMap.add(Res_GETENTRY(overlayResID)); } + typeMap.entryMap.add(Res_GETENTRY(overlayResID)); } if (!typeMap.entryMap.isEmpty()) { @@ -6445,9 +6696,6 @@ void ResTable::print(bool inclValues) const continue; } for (size_t entryIndex=0; entryIndex<entryCount; entryIndex++) { - - const uint8_t* const end = ((const uint8_t*)type) - + dtohl(type->header.size); const uint32_t* const eindex = (const uint32_t*) (((const uint8_t*)type) + dtohs(type->header.headerSize)); diff --git a/libs/androidfw/StreamingZipInflater.cpp b/libs/androidfw/StreamingZipInflater.cpp index 1dfec23..b39b5f0 100644 --- a/libs/androidfw/StreamingZipInflater.cpp +++ b/libs/androidfw/StreamingZipInflater.cpp @@ -41,6 +41,8 @@ _rc; }) #endif +static const bool kIsDebug = false; + static inline size_t min_of(size_t a, size_t b) { return (a < b) ? a : b; } using namespace android; @@ -209,7 +211,9 @@ int StreamingZipInflater::readNextChunk() { size_t toRead = min_of(mInBufSize, mInTotalSize - mInNextChunkOffset); if (toRead > 0) { ssize_t didRead = TEMP_FAILURE_RETRY(::read(mFd, mInBuf, toRead)); - //ALOGV("Reading input chunk, size %08x didread %08x", toRead, didRead); + if (kIsDebug) { + ALOGV("Reading input chunk, size %08zx didread %08zx", toRead, didRead); + } if (didRead < 0) { ALOGE("Error reading asset data: %s", strerror(errno)); return didRead; diff --git a/libs/androidfw/ZipFileRO.cpp b/libs/androidfw/ZipFileRO.cpp index 1ab18ad..af3d9b3 100644 --- a/libs/androidfw/ZipFileRO.cpp +++ b/libs/androidfw/ZipFileRO.cpp @@ -34,14 +34,6 @@ #include <assert.h> #include <unistd.h> -/* - * We must open binary files using open(path, ... | O_BINARY) under Windows. - * Otherwise strange read errors will happen. - */ -#ifndef O_BINARY -# define O_BINARY 0 -#endif - using namespace android; class _ZipEntryRO { @@ -50,7 +42,10 @@ public: ZipEntryName name; void *cookie; - _ZipEntryRO() : cookie(NULL) { + _ZipEntryRO() : cookie(NULL) {} + + ~_ZipEntryRO() { + EndIteration(cookie); } private: @@ -83,15 +78,15 @@ ZipFileRO::~ZipFileRO() { ZipEntryRO ZipFileRO::findEntryByName(const char* entryName) const { _ZipEntryRO* data = new _ZipEntryRO; - const int32_t error = FindEntry(mHandle, entryName, &(data->entry)); + + data->name = ZipEntryName(entryName); + + const int32_t error = FindEntry(mHandle, data->name, &(data->entry)); if (error) { delete data; return NULL; } - data->name.name = entryName; - data->name.name_length = strlen(entryName); - return (ZipEntryRO) data; } @@ -205,7 +200,7 @@ FileMap* ZipFileRO::createEntryFileMap(ZipEntryRO entry) const FileMap* newMap = new FileMap(); if (!newMap->create(mFileName, fd, ze.offset, actualLen, true)) { - newMap->release(); + delete newMap; return NULL; } diff --git a/libs/androidfw/misc.cpp b/libs/androidfw/misc.cpp index ea54cc5..5285420 100644 --- a/libs/androidfw/misc.cpp +++ b/libs/androidfw/misc.cpp @@ -56,9 +56,11 @@ FileType getFileType(const char* fileName) return kFileTypeBlockDev; else if (S_ISFIFO(sb.st_mode)) return kFileTypeFifo; -#ifdef HAVE_SYMLINKS +#if defined(S_ISLNK) else if (S_ISLNK(sb.st_mode)) return kFileTypeSymlink; +#endif +#if defined(S_ISSOCK) else if (S_ISSOCK(sb.st_mode)) return kFileTypeSocket; #endif diff --git a/libs/androidfw/tests/Android.mk b/libs/androidfw/tests/Android.mk index c1014be..a353575 100644 --- a/libs/androidfw/tests/Android.mk +++ b/libs/androidfw/tests/Android.mk @@ -19,6 +19,7 @@ # targets here. # ========================================================== LOCAL_PATH:= $(call my-dir) + testFiles := \ AttributeFinder_test.cpp \ ByteBucketArray_test.cpp \ @@ -32,22 +33,33 @@ testFiles := \ TypeWrappers_test.cpp \ ZipUtils_test.cpp +androidfw_test_cflags := \ + -Wall \ + -Werror \ + -Wunused \ + -Wunreachable-code \ + -Wno-missing-field-initializers \ + +# gtest is broken. +androidfw_test_cflags += -Wno-unnamed-type-template-args + # ========================================================== # Build the host tests: libandroidfw_tests # ========================================================== include $(CLEAR_VARS) LOCAL_MODULE := libandroidfw_tests +LOCAL_CFLAGS := $(androidfw_test_cflags) LOCAL_SRC_FILES := $(testFiles) LOCAL_STATIC_LIBRARIES := \ libandroidfw \ libutils \ libcutils \ - liblog + liblog \ + libz \ include $(BUILD_HOST_NATIVE_TEST) - # ========================================================== # Build the device tests: libandroidfw_tests # ========================================================== @@ -55,15 +67,16 @@ ifneq ($(SDK_ONLY),true) include $(CLEAR_VARS) LOCAL_MODULE := libandroidfw_tests +LOCAL_CFLAGS := $(androidfw_test_cflags) LOCAL_SRC_FILES := $(testFiles) \ BackupData_test.cpp \ - ObbFile_test.cpp + ObbFile_test.cpp \ + LOCAL_SHARED_LIBRARIES := \ libandroidfw \ libcutils \ libutils \ libui \ - libstlport include $(BUILD_NATIVE_TEST) endif # Not SDK_ONLY diff --git a/libs/androidfw/tests/ResTable_test.cpp b/libs/androidfw/tests/ResTable_test.cpp index 6a9314e..dcfe91e 100644 --- a/libs/androidfw/tests/ResTable_test.cpp +++ b/libs/androidfw/tests/ResTable_test.cpp @@ -16,6 +16,10 @@ #include <androidfw/ResourceTypes.h> +#include <codecvt> +#include <locale> +#include <string> + #include <utils/String8.h> #include <utils/String16.h> #include "TestHelpers.h" @@ -201,4 +205,81 @@ TEST(ResTableTest, emptyTableHasSensibleDefaults) { ASSERT_LT(table.getResource(base::R::integer::number1, &val, MAY_NOT_BE_BAG), 0); } +void testU16StringToInt(const char16_t* str, uint32_t expectedValue, + bool expectSuccess, bool expectHex) { + size_t len = std::char_traits<char16_t>::length(str); + + // Gtest can't print UTF-16 strings, so we have to convert to UTF-8 :( + std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> convert; + std::string s = convert.to_bytes(std::u16string(str, len)); + + Res_value out = {}; + ASSERT_EQ(expectSuccess, U16StringToInt(str, len, &out)) + << "Failed with " << s; + + if (!expectSuccess) { + ASSERT_EQ(out.TYPE_NULL, out.dataType) << "Failed with " << s; + return; + } + + if (expectHex) { + ASSERT_EQ(out.TYPE_INT_HEX, out.dataType) << "Failed with " << s; + } else { + ASSERT_EQ(out.TYPE_INT_DEC, out.dataType) << "Failed with " << s; + } + + ASSERT_EQ(expectedValue, out.data) << "Failed with " << s; +} + +TEST(ResTableTest, U16StringToInt) { + testU16StringToInt(u"", 0U, false, false); + testU16StringToInt(u" ", 0U, false, false); + testU16StringToInt(u"\t\n", 0U, false, false); + + testU16StringToInt(u"abcd", 0U, false, false); + testU16StringToInt(u"10abcd", 0U, false, false); + testU16StringToInt(u"42 42", 0U, false, false); + testU16StringToInt(u"- 42", 0U, false, false); + testU16StringToInt(u"-", 0U, false, false); + + testU16StringToInt(u"0x", 0U, false, true); + testU16StringToInt(u"0xnope", 0U, false, true); + testU16StringToInt(u"0X42", 0U, false, true); + testU16StringToInt(u"0x42 0x42", 0U, false, true); + testU16StringToInt(u"-0x0", 0U, false, true); + testU16StringToInt(u"-0x42", 0U, false, true); + testU16StringToInt(u"- 0x42", 0U, false, true); + + // Note that u" 42" would pass. This preserves the old behavior, but it may + // not be desired. + testU16StringToInt(u"42 ", 0U, false, false); + testU16StringToInt(u"0x42 ", 0U, false, true); + + // Decimal cases. + testU16StringToInt(u"0", 0U, true, false); + testU16StringToInt(u"-0", 0U, true, false); + testU16StringToInt(u"42", 42U, true, false); + testU16StringToInt(u" 42", 42U, true, false); + testU16StringToInt(u"-42", static_cast<uint32_t>(-42), true, false); + testU16StringToInt(u" -42", static_cast<uint32_t>(-42), true, false); + testU16StringToInt(u"042", 42U, true, false); + testU16StringToInt(u"-042", static_cast<uint32_t>(-42), true, false); + + // Hex cases. + testU16StringToInt(u"0x0", 0x0, true, true); + testU16StringToInt(u"0x42", 0x42, true, true); + testU16StringToInt(u" 0x42", 0x42, true, true); + + // Just before overflow cases: + testU16StringToInt(u"2147483647", INT_MAX, true, false); + testU16StringToInt(u"-2147483648", static_cast<uint32_t>(INT_MIN), true, + false); + testU16StringToInt(u"0xffffffff", UINT_MAX, true, true); + + // Overflow cases: + testU16StringToInt(u"2147483648", 0U, false, false); + testU16StringToInt(u"-2147483649", 0U, false, false); + testU16StringToInt(u"0x1ffffffff", 0U, false, true); +} + } diff --git a/libs/common_time/Android.mk b/libs/common_time/Android.mk index 75eb528..1fec504 100644 --- a/libs/common_time/Android.mk +++ b/libs/common_time/Android.mk @@ -33,4 +33,6 @@ LOCAL_SHARED_LIBRARIES := \ LOCAL_MODULE_TAGS := optional LOCAL_MODULE := common_time +LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code + include $(BUILD_EXECUTABLE) diff --git a/libs/common_time/clock_recovery.cpp b/libs/common_time/clock_recovery.cpp index 3a7c70c..392caa0 100644 --- a/libs/common_time/clock_recovery.cpp +++ b/libs/common_time/clock_recovery.cpp @@ -22,6 +22,7 @@ #define __STDC_LIMIT_MACROS #define LOG_TAG "common_time" #include <utils/Log.h> +#include <inttypes.h> #include <stdint.h> #include <common_time/local_clock.h> @@ -280,7 +281,7 @@ bool ClockRecoveryLoop::pushDisciplineEvent(int64_t local_time, // system. setTargetCorrection_l(tgt_correction); - LOG_TS("clock_loop %lld %f %f %f %d\n", raw_delta, delta_f, CO, CObias, tgt_correction); + LOG_TS("clock_loop %" PRId64 " %f %f %f %d\n", raw_delta, delta_f, CO, CObias, tgt_correction); #ifdef TIME_SERVICE_DEBUG diag_thread_->pushDisciplineEvent( @@ -335,7 +336,6 @@ void ClockRecoveryLoop::setTargetCorrection_l(int32_t tgt) { // 300mSec. if (tgt_correction_ != tgt) { int64_t now = local_clock_->getLocalTime(); - status_t res; tgt_correction_ = tgt; diff --git a/libs/common_time/clock_recovery.h b/libs/common_time/clock_recovery.h index b6c87ff..278a75e 100644 --- a/libs/common_time/clock_recovery.h +++ b/libs/common_time/clock_recovery.h @@ -111,7 +111,6 @@ class ClockRecoveryLoop { bool last_error_est_valid_; int32_t last_error_est_usec_; float last_delta_f_; - int32_t integrated_error_; int32_t tgt_correction_; int32_t cur_correction_; LinearTransform time_to_cur_slew_; diff --git a/libs/common_time/common_clock.cpp b/libs/common_time/common_clock.cpp index c9eb388..ee326e1 100644 --- a/libs/common_time/common_clock.cpp +++ b/libs/common_time/common_clock.cpp @@ -19,6 +19,7 @@ #define LOG_TAG "common_time" #include <utils/Log.h> +#include <inttypes.h> #include <stdint.h> #include <utils/Errors.h> @@ -50,7 +51,7 @@ bool CommonClock::init(uint64_t local_freq) { LinearTransform::reduce(&numer, &denom); if ((numer > UINT32_MAX) || (denom > UINT32_MAX)) { - ALOGE("Overflow in CommonClock::init while trying to reduce %lld/%lld", + ALOGE("Overflow in CommonClock::init while trying to reduce %" PRIu64 "/%" PRIu64, kCommonFreq, local_freq); return false; } diff --git a/libs/common_time/common_clock_service.cpp b/libs/common_time/common_clock_service.cpp index 9ca6f35..592ab1d 100644 --- a/libs/common_time/common_clock_service.cpp +++ b/libs/common_time/common_clock_service.cpp @@ -99,14 +99,14 @@ status_t CommonClockService::registerListener( Mutex::Autolock lock(mCallbackLock); // check whether this is a duplicate for (size_t i = 0; i < mListeners.size(); i++) { - if (mListeners[i]->asBinder() == listener->asBinder()) + if (IInterface::asBinder(mListeners[i]) == IInterface::asBinder(listener)) return ALREADY_EXISTS; } } mListeners.add(listener); mTimeServer.reevaluateAutoDisableState(0 != mListeners.size()); - return listener->asBinder()->linkToDeath(this); + return IInterface::asBinder(listener)->linkToDeath(this); } status_t CommonClockService::unregisterListener( @@ -117,8 +117,8 @@ status_t CommonClockService::unregisterListener( { // scoping for autolock pattern Mutex::Autolock lock(mCallbackLock); for (size_t i = 0; i < mListeners.size(); i++) { - if (mListeners[i]->asBinder() == listener->asBinder()) { - mListeners[i]->asBinder()->unlinkToDeath(this); + if (IInterface::asBinder(mListeners[i]) == IInterface::asBinder(listener)) { + IInterface::asBinder(mListeners[i])->unlinkToDeath(this); mListeners.removeAt(i); ret_val = OK; break; @@ -136,7 +136,7 @@ void CommonClockService::binderDied(const wp<IBinder>& who) { { // scoping for autolock pattern Mutex::Autolock lock(mCallbackLock); for (size_t i = 0; i < mListeners.size(); i++) { - if (mListeners[i]->asBinder() == who) { + if (IInterface::asBinder(mListeners[i]) == who) { mListeners.removeAt(i); break; } diff --git a/libs/common_time/common_time_server.cpp b/libs/common_time/common_time_server.cpp index 3e11987..01372e0 100644 --- a/libs/common_time/common_time_server.cpp +++ b/libs/common_time/common_time_server.cpp @@ -25,6 +25,7 @@ #include <arpa/inet.h> #include <assert.h> #include <fcntl.h> +#include <inttypes.h> #include <linux/if_ether.h> #include <net/if.h> #include <net/if_arp.h> @@ -969,13 +970,14 @@ bool CommonTimeServer::handleSyncResponse( // if the RTT of the packet is significantly larger than the panic // threshold, we should simply discard it. Its better to do nothing // than to take cues from a packet like that. - int rttCommon = mCommonClock.localDurationToCommonDuration(rtt); + int64_t rttCommon = mCommonClock.localDurationToCommonDuration(rtt); if (rttCommon > (static_cast<int64_t>(mPanicThresholdUsec) * kRTTDiscardPanicThreshMultiplier)) { - ALOGV("Dropping sync response with RTT of %lld uSec", rttCommon); + ALOGV("Dropping sync response with RTT of %" PRId64 " uSec", rttCommon); mClient_ExpiredSyncRespsRXedFromCurMaster++; if (shouldPanicNotGettingGoodData()) return becomeInitial("RX panic, no good data"); + return true; } else { result = mClockRecovery.pushDisciplineEvent(avgLocal, avgCommon, rttCommon); mClient_LastGoodSyncRX = clientRxLocalTime; diff --git a/libs/common_time/common_time_server_api.cpp b/libs/common_time/common_time_server_api.cpp index e157071..e0f35a9 100644 --- a/libs/common_time/common_time_server_api.cpp +++ b/libs/common_time/common_time_server_api.cpp @@ -27,6 +27,8 @@ #include "common_time_server.h" +#include <inttypes.h> + namespace android { // @@ -286,7 +288,7 @@ void CommonTimeServer::reevaluateAutoDisableState(bool commonClockHasClients) { #define checked_percentage(a, b) ((0 == b) ? 0.0f : ((100.0f * a) / b)) status_t CommonTimeServer::dumpClockInterface(int fd, - const Vector<String16>& args, + const Vector<String16>& /* args */, size_t activeClients) { AutoMutex _lock(&mLock); const size_t SIZE = 256; @@ -308,15 +310,15 @@ status_t CommonTimeServer::dumpClockInterface(int fd, synced = (OK == mCommonClock.localToCommon(localTime, &commonTime)); sockaddrToString(mMasterEP, mMasterEPValid, maStr, sizeof(maStr)); - dump_printf("Common Clock Service Status\nLocal time : %lld\n", + dump_printf("Common Clock Service Status\nLocal time : %" PRId64 "\n", localTime); if (synced) - dump_printf("Common time : %lld\n", commonTime); + dump_printf("Common time : %" PRId64 "\n", commonTime); else dump_printf("Common time : %s\n", "not synced"); - dump_printf("Timeline ID : %016llx\n", mTimelineID); + dump_printf("Timeline ID : %016" PRIu64 "\n", mTimelineID); dump_printf("State : %s\n", stateToString(mState)); dump_printf("Master Addr : %s\n", maStr); @@ -349,10 +351,10 @@ status_t CommonTimeServer::dumpClockInterface(int fd, int64_t localDelta, usecDelta; localDelta = localTime - mClient_LastGoodSyncRX; usecDelta = mCommonClock.localDurationToCommonDuration(localDelta); - dump_printf("Last Good RX : %lld uSec ago\n", usecDelta); + dump_printf("Last Good RX : %" PRId64 " uSec ago\n", usecDelta); } - dump_printf("Active Clients : %u\n", activeClients); + dump_printf("Active Clients : %zu\n", activeClients); mClient_PacketRTTLog.dumpLog(fd, mCommonClock); mStateChangeLog.dumpLog(fd); mElectionLog.dumpLog(fd); @@ -363,7 +365,7 @@ status_t CommonTimeServer::dumpClockInterface(int fd, } status_t CommonTimeServer::dumpConfigInterface(int fd, - const Vector<String16>& args) { + const Vector<String16>& /* args */) { AutoMutex _lock(&mLock); const size_t SIZE = 256; char buffer[SIZE]; @@ -383,7 +385,7 @@ status_t CommonTimeServer::dumpConfigInterface(int fd, "Bound Interface : %s\n", mBindIfaceValid ? mBindIface.string() : "<unbound>"); dump_printf("Master Election Endpoint : %s\n", meStr); - dump_printf("Master Election Group ID : %016llx\n", mSyncGroupID); + dump_printf("Master Election Group ID : %016" PRIu64 "\n", mSyncGroupID); dump_printf("Master Announce Interval : %d mSec\n", mMasterAnnounceIntervalMs); dump_printf("Client Sync Interval : %d mSec\n", @@ -419,12 +421,12 @@ void CommonTimeServer::PacketRTTLog::dumpLog(int fd, const CommonClock& cclk) { if (rxTimes[i]) { int64_t delta = rxTimes[i] - txTimes[i]; int64_t deltaUsec = cclk.localDurationToCommonDuration(delta); - dump_printf("pkt[%2d] : localTX %12lld localRX %12lld " + dump_printf("pkt[%2d] : localTX %12" PRId64 " localRX %12" PRId64 " " "(%.3f msec RTT)\n", ndx, txTimes[i], rxTimes[i], static_cast<float>(deltaUsec) / 1000.0); } else { - dump_printf("pkt[%2d] : localTX %12lld localRX never\n", + dump_printf("pkt[%2d] : localTX %12" PRId64 " localRX never\n", ndx, txTimes[i]); } i = (i + 1) % RTT_LOG_SIZE; diff --git a/libs/common_time/main.cpp b/libs/common_time/main.cpp index 49eb30a..ac52c85 100644 --- a/libs/common_time/main.cpp +++ b/libs/common_time/main.cpp @@ -27,7 +27,7 @@ #include "common_time_server.h" -int main(int argc, char *argv[]) { +int main() { using namespace android; sp<CommonTimeServer> service = new CommonTimeServer(); diff --git a/libs/hwui/AmbientShadow.cpp b/libs/hwui/AmbientShadow.cpp index b2dba00..a4100a2 100644 --- a/libs/hwui/AmbientShadow.cpp +++ b/libs/hwui/AmbientShadow.cpp @@ -45,8 +45,9 @@ /** * Other constants: */ -// For the edge of the penumbra, the opacity is 0. -#define OUTER_OPACITY (0.0f) +// For the edge of the penumbra, the opacity is 0. After transform (1 - alpha), +// it is 1. +#define TRANSFORMED_OUTER_OPACITY (1.0f) // Once the alpha difference is greater than this threshold, we will allocate extra // edge vertices. @@ -60,6 +61,7 @@ #include "AmbientShadow.h" #include "ShadowTessellator.h" #include "Vertex.h" +#include "VertexBuffer.h" #include "utils/MathUtils.h" namespace android { @@ -82,11 +84,13 @@ inline float getAlphaFromFactoredZ(float factoredZ) { return 1.0 / (1 + MathUtils::max(factoredZ, 0.0f)); } +// The shader is using gaussian function e^-(1-x)*(1-x)*4, therefore, we transform +// the alpha value to (1 - alpha) inline float getTransformedAlphaFromAlpha(float alpha) { - return acosf(1.0f - 2.0f * alpha); + return 1.0f - alpha; } -// The output is ranged from 0 to M_PI. +// The output is ranged from 0 to 1. inline float getTransformedAlphaFromFactoredZ(float factoredZ) { return getTransformedAlphaFromAlpha(getAlphaFromFactoredZ(factoredZ)); } @@ -121,14 +125,14 @@ inline void computeBufferSize(int* totalVertexCount, int* totalIndexCount, *totalUmbraCount = 0; if (!isCasterOpaque) { // Add the centroid if occluder is translucent. - *totalVertexCount++; + (*totalVertexCount)++; *totalIndexCount += 2 * innerVertexCount + 1; *totalUmbraCount = innerVertexCount; } } inline bool needsExtraForEdge(float firstAlpha, float secondAlpha) { - return abs(firstAlpha - secondAlpha) > ALPHA_THRESHOLD; + return fabsf(firstAlpha - secondAlpha) > ALPHA_THRESHOLD; } /** @@ -178,7 +182,7 @@ inline bool needsExtraForEdge(float firstAlpha, float secondAlpha) { void AmbientShadow::createAmbientShadow(bool isCasterOpaque, const Vector3* casterVertices, int casterVertexCount, const Vector3& centroid3d, float heightFactor, float geomFactor, VertexBuffer& shadowVertexBuffer) { - shadowVertexBuffer.setMode(VertexBuffer::kIndices); + shadowVertexBuffer.setMeshFeatureFlags(VertexBuffer::kAlpha | VertexBuffer::kIndices); // In order to computer the outer vertices in one loop, we need pre-compute // the normal by the vertex (n - 1) to vertex 0, and the spike and alpha value @@ -248,7 +252,7 @@ void AmbientShadow::createAmbientShadow(bool isCasterOpaque, indexBuffer[indexBufferIndex++] = vertexBufferIndex; indexBuffer[indexBufferIndex++] = currentInnerVertexIndex; AlphaVertex::set(&shadowVertices[vertexBufferIndex++], outerVertex.x, - outerVertex.y, OUTER_OPACITY); + outerVertex.y, TRANSFORMED_OUTER_OPACITY); if (j == 0) { outerStart = outerVertex; @@ -284,7 +288,7 @@ void AmbientShadow::createAmbientShadow(bool isCasterOpaque, (outerLast * startWeight + outerNext * k) / extraVerticesNumber; indexBuffer[indexBufferIndex++] = vertexBufferIndex; AlphaVertex::set(&shadowVertices[vertexBufferIndex++], currentOuter.x, - currentOuter.y, OUTER_OPACITY); + currentOuter.y, TRANSFORMED_OUTER_OPACITY); if (!isCasterOpaque) { umbraVertices[umbraIndex++] = vertexBufferIndex; diff --git a/libs/hwui/AmbientShadow.h b/libs/hwui/AmbientShadow.h index 9660dc0..8eb1048 100644 --- a/libs/hwui/AmbientShadow.h +++ b/libs/hwui/AmbientShadow.h @@ -19,13 +19,13 @@ #define ANDROID_HWUI_AMBIENT_SHADOW_H #include "Debug.h" -#include "OpenGLRenderer.h" #include "Vector.h" -#include "VertexBuffer.h" namespace android { namespace uirenderer { +class VertexBuffer; + /** * AmbientShadow is used to calculate the ambient shadow value around a polygon. */ diff --git a/libs/hwui/Android.common.mk b/libs/hwui/Android.common.mk new file mode 100644 index 0000000..836f868 --- /dev/null +++ b/libs/hwui/Android.common.mk @@ -0,0 +1,119 @@ +# getConfig in external/skia/include/core/SkBitmap.h is deprecated. +# Allow Gnu extension: in-class initializer of static 'const float' member. +LOCAL_CLANG_CFLAGS += \ + -Wno-deprecated-declarations \ + -Wno-gnu-static-float-init + +LOCAL_SRC_FILES := \ + font/CacheTexture.cpp \ + font/Font.cpp \ + renderstate/Blend.cpp \ + renderstate/MeshState.cpp \ + renderstate/PixelBufferState.cpp \ + renderstate/RenderState.cpp \ + renderstate/Scissor.cpp \ + renderstate/Stencil.cpp \ + renderstate/TextureState.cpp \ + renderthread/CanvasContext.cpp \ + renderthread/DrawFrameTask.cpp \ + renderthread/EglManager.cpp \ + renderthread/RenderProxy.cpp \ + renderthread/RenderTask.cpp \ + renderthread/RenderThread.cpp \ + renderthread/TimeLord.cpp \ + thread/TaskManager.cpp \ + utils/Blur.cpp \ + utils/GLUtils.cpp \ + utils/LinearAllocator.cpp \ + utils/SortedListImpl.cpp \ + AmbientShadow.cpp \ + AnimationContext.cpp \ + Animator.cpp \ + AnimatorManager.cpp \ + AssetAtlas.cpp \ + Caches.cpp \ + CanvasState.cpp \ + ClipArea.cpp \ + DamageAccumulator.cpp \ + DeferredDisplayList.cpp \ + DeferredLayerUpdater.cpp \ + DisplayList.cpp \ + DisplayListCanvas.cpp \ + Dither.cpp \ + DrawProfiler.cpp \ + Extensions.cpp \ + FboCache.cpp \ + FontRenderer.cpp \ + FrameInfo.cpp \ + GammaFontRenderer.cpp \ + GlopBuilder.cpp \ + GradientCache.cpp \ + Image.cpp \ + Interpolator.cpp \ + JankTracker.cpp \ + Layer.cpp \ + LayerCache.cpp \ + LayerRenderer.cpp \ + Matrix.cpp \ + OpenGLRenderer.cpp \ + Patch.cpp \ + PatchCache.cpp \ + PathCache.cpp \ + PathTessellator.cpp \ + PixelBuffer.cpp \ + Program.cpp \ + ProgramCache.cpp \ + RenderBufferCache.cpp \ + RenderNode.cpp \ + RenderProperties.cpp \ + ResourceCache.cpp \ + ShadowTessellator.cpp \ + SkiaCanvas.cpp \ + SkiaCanvasProxy.cpp \ + SkiaShader.cpp \ + Snapshot.cpp \ + SpotShadow.cpp \ + TessellationCache.cpp \ + TextDropShadowCache.cpp \ + Texture.cpp \ + TextureCache.cpp + +intermediates := $(call intermediates-dir-for,STATIC_LIBRARIES,libRS,TARGET,) + +LOCAL_C_INCLUDES += \ + external/skia/src/core + +LOCAL_CFLAGS += -DEGL_EGLEXT_PROTOTYPES -DGL_GLEXT_PROTOTYPES +LOCAL_SHARED_LIBRARIES := liblog libcutils libutils libEGL libGLESv2 libskia libui libgui + +ifneq (false,$(ANDROID_ENABLE_RENDERSCRIPT)) + LOCAL_CFLAGS += -DANDROID_ENABLE_RENDERSCRIPT + LOCAL_SHARED_LIBRARIES += libRS libRScpp + LOCAL_C_INCLUDES += \ + $(intermediates) \ + frameworks/rs/cpp \ + frameworks/rs \ + +endif + +ifndef HWUI_COMPILE_SYMBOLS + LOCAL_CFLAGS += -fvisibility=hidden +endif + +ifdef HWUI_COMPILE_FOR_PERF + # TODO: Non-arm? + LOCAL_CFLAGS += -fno-omit-frame-pointer -marm -mapcs +endif + +ifeq (true, $(HWUI_NULL_GPU)) + LOCAL_SRC_FILES += \ + tests/nullegl.cpp \ + tests/nullgles.cpp + + LOCAL_CFLAGS += -DHWUI_NULL_GPU +endif + +# Defaults for ATRACE_TAG and LOG_TAG for libhwui +LOCAL_CFLAGS += -DATRACE_TAG=ATRACE_TAG_VIEW -DLOG_TAG=\"OpenGLRenderer\" + +LOCAL_CFLAGS += -Wall -Werror -Wno-unused-parameter -Wunreachable-code diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk index 49560ff..91e289c 100644 --- a/libs/hwui/Android.mk +++ b/libs/hwui/Android.mk @@ -1,110 +1,12 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) +LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk -# Only build libhwui when USE_OPENGL_RENDERER is -# defined in the current device/board configuration -ifeq ($(USE_OPENGL_RENDERER),true) - LOCAL_SRC_FILES := \ - utils/Blur.cpp \ - utils/GLUtils.cpp \ - utils/SortedListImpl.cpp \ - thread/TaskManager.cpp \ - font/CacheTexture.cpp \ - font/Font.cpp \ - AmbientShadow.cpp \ - AnimationContext.cpp \ - Animator.cpp \ - AnimatorManager.cpp \ - AssetAtlas.cpp \ - DamageAccumulator.cpp \ - FontRenderer.cpp \ - GammaFontRenderer.cpp \ - Caches.cpp \ - DisplayList.cpp \ - DeferredDisplayList.cpp \ - DeferredLayerUpdater.cpp \ - DisplayListLogBuffer.cpp \ - DisplayListRenderer.cpp \ - Dither.cpp \ - DrawProfiler.cpp \ - Extensions.cpp \ - FboCache.cpp \ - GradientCache.cpp \ - Image.cpp \ - Interpolator.cpp \ - Layer.cpp \ - LayerCache.cpp \ - LayerRenderer.cpp \ - Matrix.cpp \ - OpenGLRenderer.cpp \ - Patch.cpp \ - PatchCache.cpp \ - PathCache.cpp \ - PathTessellator.cpp \ - PixelBuffer.cpp \ - Program.cpp \ - ProgramCache.cpp \ - RenderBufferCache.cpp \ - RenderNode.cpp \ - RenderProperties.cpp \ - RenderState.cpp \ - ResourceCache.cpp \ - ShadowTessellator.cpp \ - SkiaShader.cpp \ - Snapshot.cpp \ - SpotShadow.cpp \ - StatefulBaseRenderer.cpp \ - Stencil.cpp \ - TessellationCache.cpp \ - Texture.cpp \ - TextureCache.cpp \ - TextDropShadowCache.cpp +LOCAL_MODULE_CLASS := SHARED_LIBRARIES +LOCAL_MODULE := libhwui -# RenderThread stuff - LOCAL_SRC_FILES += \ - renderthread/CanvasContext.cpp \ - renderthread/DrawFrameTask.cpp \ - renderthread/EglManager.cpp \ - renderthread/RenderProxy.cpp \ - renderthread/RenderTask.cpp \ - renderthread/RenderThread.cpp \ - renderthread/TimeLord.cpp +include $(LOCAL_PATH)/Android.common.mk - intermediates := $(call intermediates-dir-for,STATIC_LIBRARIES,libRS,TARGET,) +include $(BUILD_SHARED_LIBRARY) - LOCAL_C_INCLUDES += \ - external/skia/src/core - - LOCAL_CFLAGS += -DUSE_OPENGL_RENDERER -DEGL_EGLEXT_PROTOTYPES -DGL_GLEXT_PROTOTYPES - LOCAL_CFLAGS += -Wno-unused-parameter - LOCAL_MODULE_CLASS := SHARED_LIBRARIES - LOCAL_SHARED_LIBRARIES := liblog libcutils libutils libEGL libGLESv2 libskia libui libgui - LOCAL_MODULE := libhwui - LOCAL_MODULE_TAGS := optional - - include external/stlport/libstlport.mk - - ifneq (false,$(ANDROID_ENABLE_RENDERSCRIPT)) - LOCAL_CFLAGS += -DANDROID_ENABLE_RENDERSCRIPT - LOCAL_SHARED_LIBRARIES += libRS libRScpp - LOCAL_C_INCLUDES += \ - $(intermediates) \ - frameworks/rs/cpp \ - frameworks/rs - endif - - ifndef HWUI_COMPILE_SYMBOLS - LOCAL_CFLAGS += -fvisibility=hidden - endif - - ifdef HWUI_COMPILE_FOR_PERF - LOCAL_CFLAGS += -fno-omit-frame-pointer -marm -mapcs - endif - - # Defaults for ATRACE_TAG and LOG_TAG for libhwui - LOCAL_CFLAGS += -DATRACE_TAG=ATRACE_TAG_VIEW -DLOG_TAG=\"OpenGLRenderer\" - - include $(BUILD_SHARED_LIBRARY) - - include $(call all-makefiles-under,$(LOCAL_PATH)) -endif +include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/libs/hwui/AnimationContext.cpp b/libs/hwui/AnimationContext.cpp index a20bdae..097be08 100644 --- a/libs/hwui/AnimationContext.cpp +++ b/libs/hwui/AnimationContext.cpp @@ -59,7 +59,7 @@ void AnimationContext::startFrame(TreeInfo::TraversalMode mode) { "Missed running animations last frame!"); AnimationHandle* head = mNextFrameAnimations.mNextHandle; if (head) { - mNextFrameAnimations.mNextHandle = NULL; + mNextFrameAnimations.mNextHandle = nullptr; mCurrentFrameAnimations.mNextHandle = head; head->mPreviousHandle = &mCurrentFrameAnimations; } @@ -84,15 +84,15 @@ void AnimationContext::callOnFinished(BaseRenderNodeAnimator* animator, AnimationHandle::AnimationHandle(AnimationContext& context) : mContext(context) - , mPreviousHandle(NULL) - , mNextHandle(NULL) { + , mPreviousHandle(nullptr) + , mNextHandle(nullptr) { } AnimationHandle::AnimationHandle(RenderNode& animatingNode, AnimationContext& context) : mRenderNode(&animatingNode) , mContext(context) - , mPreviousHandle(NULL) - , mNextHandle(NULL) { + , mPreviousHandle(nullptr) + , mNextHandle(nullptr) { mRenderNode->animators().setAnimationHandle(this); } @@ -114,7 +114,7 @@ void AnimationHandle::release() { LOG_ALWAYS_FATAL_IF(mRenderNode->animators().hasAnimators(), "Releasing the handle for an RenderNode with outstanding animators!"); removeFromList(); - mRenderNode->animators().setAnimationHandle(NULL); + mRenderNode->animators().setAnimationHandle(nullptr); delete this; } @@ -135,8 +135,8 @@ void AnimationHandle::removeFromList() { if (mNextHandle) { mNextHandle->mPreviousHandle = mPreviousHandle; } - mPreviousHandle = NULL; - mNextHandle = NULL; + mPreviousHandle = nullptr; + mNextHandle = nullptr; } } /* namespace uirenderer */ diff --git a/libs/hwui/Animator.cpp b/libs/hwui/Animator.cpp index 8bf2107..512e0e2 100644 --- a/libs/hwui/Animator.cpp +++ b/libs/hwui/Animator.cpp @@ -20,6 +20,7 @@ #include <set> #include "AnimationContext.h" +#include "Interpolator.h" #include "RenderNode.h" #include "RenderProperties.h" @@ -31,11 +32,10 @@ namespace uirenderer { ************************************************************/ BaseRenderNodeAnimator::BaseRenderNodeAnimator(float finalValue) - : mTarget(NULL) + : mTarget(nullptr) , mFinalValue(finalValue) , mDeltaValue(0) , mFromValue(0) - , mInterpolator(0) , mStagingPlayState(NOT_STARTED) , mPlayState(NOT_STARTED) , mHasStartValue(false) @@ -46,7 +46,6 @@ BaseRenderNodeAnimator::BaseRenderNodeAnimator(float finalValue) } BaseRenderNodeAnimator::~BaseRenderNodeAnimator() { - delete mInterpolator; } void BaseRenderNodeAnimator::checkMutable() { @@ -57,8 +56,7 @@ void BaseRenderNodeAnimator::checkMutable() { void BaseRenderNodeAnimator::setInterpolator(Interpolator* interpolator) { checkMutable(); - delete mInterpolator; - mInterpolator = interpolator; + mInterpolator.reset(interpolator); } void BaseRenderNodeAnimator::setStartValue(float value) { @@ -118,7 +116,7 @@ void BaseRenderNodeAnimator::transitionToRunning(AnimationContext& context) { } // No interpolator was set, use the default if (!mInterpolator) { - mInterpolator = Interpolator::createDefaultInterpolator(); + mInterpolator.reset(Interpolator::createDefaultInterpolator()); } if (mDuration < 0 || mDuration > 50000) { ALOGW("Your duration is strange and confusing: %" PRId64, mDuration); diff --git a/libs/hwui/Animator.h b/libs/hwui/Animator.h index 35a4a09..1b3d8e7 100644 --- a/libs/hwui/Animator.h +++ b/libs/hwui/Animator.h @@ -16,13 +16,12 @@ #ifndef ANIMATOR_H #define ANIMATOR_H +#include <memory> #include <cutils/compiler.h> #include <utils/RefBase.h> #include <utils/StrongPointer.h> +#include <utils/Timers.h> -#include "CanvasProperty.h" -#include "Interpolator.h" -#include "TreeInfo.h" #include "utils/Macros.h" namespace android { @@ -30,6 +29,9 @@ namespace uirenderer { class AnimationContext; class BaseRenderNodeAnimator; +class CanvasPropertyPrimitive; +class CanvasPropertyPaint; +class Interpolator; class RenderNode; class RenderProperties; @@ -62,7 +64,7 @@ public: void attach(RenderNode* target); virtual void onAttached() {} - void detach() { mTarget = 0; } + void detach() { mTarget = nullptr; } void pushStaging(AnimationContext& context); bool animate(AnimationContext& context); @@ -98,7 +100,7 @@ protected: float mDeltaValue; float mFromValue; - Interpolator* mInterpolator; + std::unique_ptr<Interpolator> mInterpolator; PlayState mStagingPlayState; PlayState mPlayState; bool mHasStartValue; @@ -137,10 +139,10 @@ public: ANDROID_API virtual uint32_t dirtyMask(); protected: - virtual float getValue(RenderNode* target) const; - virtual void setValue(RenderNode* target, float value); - virtual void onAttached(); - virtual void onStagingPlayStateChanged(); + virtual float getValue(RenderNode* target) const override; + virtual void setValue(RenderNode* target, float value) override; + virtual void onAttached() override; + virtual void onStagingPlayStateChanged() override; private: typedef bool (RenderProperties::*SetFloatProperty)(float value); @@ -160,8 +162,8 @@ public: ANDROID_API virtual uint32_t dirtyMask(); protected: - virtual float getValue(RenderNode* target) const; - virtual void setValue(RenderNode* target, float value); + virtual float getValue(RenderNode* target) const override; + virtual void setValue(RenderNode* target, float value) override; private: sp<CanvasPropertyPrimitive> mProperty; }; @@ -179,8 +181,8 @@ public: ANDROID_API virtual uint32_t dirtyMask(); protected: - virtual float getValue(RenderNode* target) const; - virtual void setValue(RenderNode* target, float value); + virtual float getValue(RenderNode* target) const override; + virtual void setValue(RenderNode* target, float value) override; private: sp<CanvasPropertyPaint> mProperty; PaintField mField; @@ -194,8 +196,8 @@ public: ANDROID_API virtual uint32_t dirtyMask(); protected: - virtual float getValue(RenderNode* target) const; - virtual void setValue(RenderNode* target, float value); + virtual float getValue(RenderNode* target) const override; + virtual void setValue(RenderNode* target, float value) override; private: int mCenterX, mCenterY; diff --git a/libs/hwui/AnimatorManager.cpp b/libs/hwui/AnimatorManager.cpp index c28fb88..966959a 100644 --- a/libs/hwui/AnimatorManager.cpp +++ b/libs/hwui/AnimatorManager.cpp @@ -17,7 +17,9 @@ #include <algorithm> +#include "Animator.h" #include "AnimationContext.h" +#include "DamageAccumulator.h" #include "RenderNode.h" namespace android { @@ -27,12 +29,12 @@ using namespace std; static void unref(BaseRenderNodeAnimator* animator) { animator->detach(); - animator->decStrong(0); + animator->decStrong(nullptr); } AnimatorManager::AnimatorManager(RenderNode& parent) : mParent(parent) - , mAnimationHandle(NULL) { + , mAnimationHandle(nullptr) { } AnimatorManager::~AnimatorManager() { @@ -41,7 +43,7 @@ AnimatorManager::~AnimatorManager() { } void AnimatorManager::addAnimator(const sp<BaseRenderNodeAnimator>& animator) { - animator->incStrong(0); + animator->incStrong(nullptr); animator->attach(&mParent); mNewAnimators.push_back(animator.get()); } @@ -85,7 +87,7 @@ public: dirtyMask |= animator->dirtyMask(); bool remove = animator->animate(mContext); if (remove) { - animator->decStrong(0); + animator->decStrong(nullptr); } else { if (animator->isRunning()) { mInfo.out.hasAnimations = true; @@ -142,7 +144,7 @@ static void endStagingAnimator(BaseRenderNodeAnimator* animator) { if (animator->listener()) { animator->listener()->onAnimationFinished(animator); } - animator->decStrong(0); + animator->decStrong(nullptr); } void AnimatorManager::endAllStagingAnimators() { @@ -159,7 +161,7 @@ public: void operator() (BaseRenderNodeAnimator* animator) { animator->forceEndNow(mContext); - animator->decStrong(0); + animator->decStrong(nullptr); } private: diff --git a/libs/hwui/AnimatorManager.h b/libs/hwui/AnimatorManager.h index d03d427..fb75eb8 100644 --- a/libs/hwui/AnimatorManager.h +++ b/libs/hwui/AnimatorManager.h @@ -21,7 +21,6 @@ #include <cutils/compiler.h> #include <utils/StrongPointer.h> -#include "TreeInfo.h" #include "utils/Macros.h" namespace android { @@ -30,6 +29,7 @@ namespace uirenderer { class AnimationHandle; class BaseRenderNodeAnimator; class RenderNode; +class TreeInfo; // Responsible for managing the animators for a single RenderNode class AnimatorManager { diff --git a/libs/hwui/AssetAtlas.cpp b/libs/hwui/AssetAtlas.cpp index 52ca92d..4d2e3a0 100644 --- a/libs/hwui/AssetAtlas.cpp +++ b/libs/hwui/AssetAtlas.cpp @@ -18,6 +18,7 @@ #include "AssetAtlas.h" #include "Caches.h" +#include "Image.h" #include <GLES2/gl2ext.h> @@ -47,7 +48,7 @@ void AssetAtlas::init(sp<GraphicBuffer> buffer, int64_t* map, int count) { } else { ALOGW("Could not create atlas image"); delete mImage; - mImage = NULL; + mImage = nullptr; } updateTextureId(); @@ -56,7 +57,7 @@ void AssetAtlas::init(sp<GraphicBuffer> buffer, int64_t* map, int count) { void AssetAtlas::terminate() { if (mImage) { delete mImage; - mImage = NULL; + mImage = nullptr; updateTextureId(); } } @@ -82,12 +83,12 @@ void AssetAtlas::updateTextureId() { AssetAtlas::Entry* AssetAtlas::getEntry(const SkBitmap* bitmap) const { ssize_t index = mEntries.indexOfKey(bitmap); - return index >= 0 ? mEntries.valueAt(index) : NULL; + return index >= 0 ? mEntries.valueAt(index) : nullptr; } Texture* AssetAtlas::getEntryTexture(const SkBitmap* bitmap) const { ssize_t index = mEntries.indexOfKey(bitmap); - return index >= 0 ? mEntries.valueAt(index)->texture : NULL; + return index >= 0 ? mEntries.valueAt(index)->texture : nullptr; } /** @@ -98,12 +99,12 @@ 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) { + 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) { + bool force = false, GLenum renderTarget = GL_TEXTURE_2D) override { mDelegate->setFilterMinMag(min, mag, bindTexture, force, renderTarget); } diff --git a/libs/hwui/AssetAtlas.h b/libs/hwui/AssetAtlas.h index fffd740..1772eff 100644 --- a/libs/hwui/AssetAtlas.h +++ b/libs/hwui/AssetAtlas.h @@ -27,7 +27,6 @@ #include <SkBitmap.h> -#include "Image.h" #include "Texture.h" #include "UvMapper.h" @@ -35,6 +34,7 @@ namespace android { namespace uirenderer { class Caches; +class Image; /** * An asset atlas holds a collection of framework bitmaps in a single OpenGL @@ -106,7 +106,7 @@ public: friend class AssetAtlas; }; - AssetAtlas(): mTexture(NULL), mImage(NULL), + AssetAtlas(): mTexture(nullptr), mImage(nullptr), mBlendKey(true), mOpaqueKey(false) { } ~AssetAtlas() { terminate(); } diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp index cdf8150..fd5a2ce 100644 --- a/libs/hwui/Caches.cpp +++ b/libs/hwui/Caches.cpp @@ -16,25 +16,22 @@ #define LOG_TAG "OpenGLRenderer" -#include <utils/Log.h> -#include <utils/String8.h> - #include "Caches.h" -#include "DisplayListRenderer.h" -#include "Properties.h" + +#include "GammaFontRenderer.h" #include "LayerRenderer.h" +#include "Properties.h" +#include "renderstate/RenderState.h" #include "ShadowTessellator.h" -#include "RenderState.h" - -namespace android { -#ifdef USE_OPENGL_RENDERER -using namespace uirenderer; -ANDROID_SINGLETON_STATIC_INSTANCE(Caches); -#endif +#include <utils/Log.h> +#include <utils/String8.h> +namespace android { namespace uirenderer { +Caches* Caches::sInstance = nullptr; + /////////////////////////////////////////////////////////////////////////////// // Macros /////////////////////////////////////////////////////////////////////////////// @@ -49,8 +46,14 @@ namespace uirenderer { // Constructors/destructor /////////////////////////////////////////////////////////////////////////////// -Caches::Caches(): Singleton<Caches>(), - mExtensions(Extensions::getInstance()), mInitialized(false), mRenderState(NULL) { +Caches::Caches(RenderState& renderState) + : gradientCache(mExtensions) + , patchCache(renderState) + , programCache(mExtensions) + , dither(*this) + , mRenderState(&renderState) + , mInitialized(false) { + INIT_LOGD("Creating OpenGL renderer caches"); init(); initFont(); initConstraints(); @@ -60,7 +63,8 @@ Caches::Caches(): Singleton<Caches>(), initTempProperties(); mDebugLevel = readDebugLevel(); - ALOGD("Enabling debug mode %d", mDebugLevel); + ALOGD_IF(mDebugLevel != kDebugDisabled, + "Enabling debug mode %d", mDebugLevel); } bool Caches::init() { @@ -68,33 +72,8 @@ bool Caches::init() { ATRACE_NAME("Caches::init"); - glGenBuffers(1, &meshBuffer); - glBindBuffer(GL_ARRAY_BUFFER, meshBuffer); - glBufferData(GL_ARRAY_BUFFER, sizeof(gMeshVertices), gMeshVertices, GL_STATIC_DRAW); - - mCurrentBuffer = meshBuffer; - mCurrentIndicesBuffer = 0; - mCurrentPositionPointer = this; - mCurrentPositionStride = 0; - mCurrentTexCoordsPointer = this; - mCurrentPixelBuffer = 0; - - mTexCoordsArrayEnabled = false; - - glDisable(GL_SCISSOR_TEST); - scissorEnabled = false; - mScissorX = mScissorY = mScissorWidth = mScissorHeight = 0; - - glActiveTexture(gTextureUnits[0]); - mTextureUnit = 0; - - mRegionMesh = NULL; - mMeshIndices = 0; - mShadowStripsIndices = 0; - blend = false; - lastSrcMode = GL_ZERO; - lastDstMode = GL_ZERO; - currentProgram = NULL; + mRegionMesh = nullptr; + mProgram = nullptr; mFunctorsCount = 0; @@ -102,11 +81,12 @@ bool Caches::init() { debugOverdraw = false; debugStencilClip = kStencilHide; - patchCache.init(*this); + patchCache.init(); mInitialized = true; - resetBoundTextures(); + mPixelBufferState = new PixelBufferState(); + mTextureState = new TextureState(); return true; } @@ -126,23 +106,9 @@ void Caches::initExtensions() { startMark = startMarkNull; endMark = endMarkNull; } - - if (mExtensions.hasDebugLabel() && (drawDeferDisabled || drawReorderDisabled)) { - setLabel = glLabelObjectEXT; - getLabel = glGetObjectLabelEXT; - } else { - setLabel = setLabelNull; - getLabel = getLabelNull; - } } void Caches::initConstraints() { - GLint maxTextureUnits; - glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxTextureUnits); - if (maxTextureUnits < REQUIRED_TEXTURE_UNITS_COUNT) { - ALOGW("At least %d texture units are required!", REQUIRED_TEXTURE_UNITS_COUNT); - } - glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize); } @@ -164,7 +130,7 @@ bool Caches::initProperties() { StencilClipDebug prevDebugStencilClip = debugStencilClip; char property[PROPERTY_VALUE_MAX]; - if (property_get(PROPERTY_DEBUG_LAYERS_UPDATES, property, NULL) > 0) { + if (property_get(PROPERTY_DEBUG_LAYERS_UPDATES, property, nullptr) > 0) { INIT_LOGD(" Layers updates debug enabled: %s", property); debugLayersUpdates = !strcmp(property, "true"); } else { @@ -172,7 +138,7 @@ bool Caches::initProperties() { } debugOverdraw = false; - if (property_get(PROPERTY_DEBUG_OVERDRAW, property, NULL) > 0) { + if (property_get(PROPERTY_DEBUG_OVERDRAW, property, nullptr) > 0) { INIT_LOGD(" Overdraw debug enabled: %s", property); if (!strcmp(property, "show")) { debugOverdraw = true; @@ -184,7 +150,7 @@ bool Caches::initProperties() { } // See Properties.h for valid values - if (property_get(PROPERTY_DEBUG_STENCIL_CLIP, property, NULL) > 0) { + if (property_get(PROPERTY_DEBUG_STENCIL_CLIP, property, nullptr) > 0) { INIT_LOGD(" Stencil clip debug enabled: %s", property); if (!strcmp(property, "hide")) { debugStencilClip = kStencilHide; @@ -213,37 +179,47 @@ bool Caches::initProperties() { INIT_LOGD(" Draw reorder enabled"); } - return (prevDebugLayersUpdates != debugLayersUpdates) || - (prevDebugOverdraw != debugOverdraw) || - (prevDebugStencilClip != debugStencilClip); + return (prevDebugLayersUpdates != debugLayersUpdates) + || (prevDebugOverdraw != debugOverdraw) + || (prevDebugStencilClip != debugStencilClip); } void Caches::terminate() { if (!mInitialized) return; - - glDeleteBuffers(1, &meshBuffer); - mCurrentBuffer = 0; - - glDeleteBuffers(1, &mMeshIndices); - delete[] mRegionMesh; - mMeshIndices = 0; - mRegionMesh = NULL; - - glDeleteBuffers(1, &mShadowStripsIndices); - mShadowStripsIndices = 0; + mRegionMesh.release(); fboCache.clear(); programCache.clear(); - currentProgram = NULL; + mProgram = nullptr; patchCache.clear(); clearGarbage(); + delete mPixelBufferState; + mPixelBufferState = nullptr; + delete mTextureState; + mTextureState = nullptr; mInitialized = false; } +void Caches::setProgram(const ProgramDescription& description) { + setProgram(programCache.get(description)); +} + +void Caches::setProgram(Program* program) { + if (!program || !program->isInUse()) { + if (mProgram) { + mProgram->remove(); + } + if (program) { + program->use(); + } + mProgram = program; + } +} + /////////////////////////////////////////////////////////////////////////////// // Debug /////////////////////////////////////////////////////////////////////////////// @@ -278,7 +254,7 @@ void Caches::dumpMemoryUsage(String8 &log) { const Layer* layer = *it; log.appendFormat(" Layer size %dx%d; isTextureLayer()=%d; texid=%u fbo=%u; refs=%d\n", layer->getWidth(), layer->getHeight(), - layer->isTextureLayer(), layer->getTexture(), + layer->isTextureLayer(), layer->getTextureId(), layer->getFbo(), layer->getStrongCount()); memused += layer->getWidth() * layer->getHeight() * 4; } @@ -371,285 +347,6 @@ void Caches::flush(FlushMode mode) { } /////////////////////////////////////////////////////////////////////////////// -// VBO -/////////////////////////////////////////////////////////////////////////////// - -bool Caches::bindMeshBuffer() { - return bindMeshBuffer(meshBuffer); -} - -bool Caches::bindMeshBuffer(const GLuint buffer) { - if (mCurrentBuffer != buffer) { - glBindBuffer(GL_ARRAY_BUFFER, buffer); - mCurrentBuffer = buffer; - return true; - } - return false; -} - -bool Caches::unbindMeshBuffer() { - if (mCurrentBuffer) { - glBindBuffer(GL_ARRAY_BUFFER, 0); - mCurrentBuffer = 0; - return true; - } - return false; -} - -bool Caches::bindIndicesBufferInternal(const GLuint buffer) { - if (mCurrentIndicesBuffer != buffer) { - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer); - mCurrentIndicesBuffer = buffer; - return true; - } - return false; -} - -bool Caches::bindQuadIndicesBuffer() { - if (!mMeshIndices) { - uint16_t* regionIndices = new uint16_t[gMaxNumberOfQuads * 6]; - for (uint32_t i = 0; i < gMaxNumberOfQuads; i++) { - uint16_t quad = i * 4; - int index = i * 6; - regionIndices[index ] = quad; // top-left - regionIndices[index + 1] = quad + 1; // top-right - regionIndices[index + 2] = quad + 2; // bottom-left - regionIndices[index + 3] = quad + 2; // bottom-left - regionIndices[index + 4] = quad + 1; // top-right - regionIndices[index + 5] = quad + 3; // bottom-right - } - - glGenBuffers(1, &mMeshIndices); - bool force = bindIndicesBufferInternal(mMeshIndices); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, gMaxNumberOfQuads * 6 * sizeof(uint16_t), - regionIndices, GL_STATIC_DRAW); - - delete[] regionIndices; - return force; - } - - return bindIndicesBufferInternal(mMeshIndices); -} - -bool Caches::bindShadowIndicesBuffer() { - if (!mShadowStripsIndices) { - uint16_t* shadowIndices = new uint16_t[MAX_SHADOW_INDEX_COUNT]; - ShadowTessellator::generateShadowIndices(shadowIndices); - glGenBuffers(1, &mShadowStripsIndices); - bool force = bindIndicesBufferInternal(mShadowStripsIndices); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, MAX_SHADOW_INDEX_COUNT * sizeof(uint16_t), - shadowIndices, GL_STATIC_DRAW); - - delete[] shadowIndices; - return force; - } - - return bindIndicesBufferInternal(mShadowStripsIndices); -} - -bool Caches::unbindIndicesBuffer() { - if (mCurrentIndicesBuffer) { - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - mCurrentIndicesBuffer = 0; - return true; - } - return false; -} - -/////////////////////////////////////////////////////////////////////////////// -// PBO -/////////////////////////////////////////////////////////////////////////////// - -bool Caches::bindPixelBuffer(const GLuint buffer) { - if (mCurrentPixelBuffer != buffer) { - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, buffer); - mCurrentPixelBuffer = buffer; - return true; - } - return false; -} - -bool Caches::unbindPixelBuffer() { - if (mCurrentPixelBuffer) { - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); - mCurrentPixelBuffer = 0; - return true; - } - return false; -} - -/////////////////////////////////////////////////////////////////////////////// -// Meshes and textures -/////////////////////////////////////////////////////////////////////////////// - -void Caches::bindPositionVertexPointer(bool force, const GLvoid* vertices, GLsizei stride) { - if (force || vertices != mCurrentPositionPointer || stride != mCurrentPositionStride) { - GLuint slot = currentProgram->position; - glVertexAttribPointer(slot, 2, GL_FLOAT, GL_FALSE, stride, vertices); - mCurrentPositionPointer = vertices; - mCurrentPositionStride = stride; - } -} - -void Caches::bindTexCoordsVertexPointer(bool force, const GLvoid* vertices, GLsizei stride) { - if (force || vertices != mCurrentTexCoordsPointer || stride != mCurrentTexCoordsStride) { - GLuint slot = currentProgram->texCoords; - glVertexAttribPointer(slot, 2, GL_FLOAT, GL_FALSE, stride, vertices); - mCurrentTexCoordsPointer = vertices; - mCurrentTexCoordsStride = stride; - } -} - -void Caches::resetVertexPointers() { - mCurrentPositionPointer = this; - mCurrentTexCoordsPointer = this; -} - -void Caches::resetTexCoordsVertexPointer() { - mCurrentTexCoordsPointer = this; -} - -void Caches::enableTexCoordsVertexArray() { - if (!mTexCoordsArrayEnabled) { - glEnableVertexAttribArray(Program::kBindingTexCoords); - mCurrentTexCoordsPointer = this; - mTexCoordsArrayEnabled = true; - } -} - -void Caches::disableTexCoordsVertexArray() { - if (mTexCoordsArrayEnabled) { - glDisableVertexAttribArray(Program::kBindingTexCoords); - mTexCoordsArrayEnabled = false; - } -} - -void Caches::activeTexture(GLuint textureUnit) { - if (mTextureUnit != textureUnit) { - glActiveTexture(gTextureUnits[textureUnit]); - mTextureUnit = textureUnit; - } -} - -void Caches::resetActiveTexture() { - mTextureUnit = -1; -} - -void Caches::bindTexture(GLuint texture) { - if (mBoundTextures[mTextureUnit] != texture) { - glBindTexture(GL_TEXTURE_2D, texture); - mBoundTextures[mTextureUnit] = texture; - } -} - -void Caches::bindTexture(GLenum target, GLuint texture) { - if (target == GL_TEXTURE_2D) { - bindTexture(texture); - } else { - // GLConsumer directly calls glBindTexture() with - // target=GL_TEXTURE_EXTERNAL_OES, don't cache this target - // since the cached state could be stale - glBindTexture(target, texture); - } -} - -void Caches::deleteTexture(GLuint texture) { - // When glDeleteTextures() is called on a currently bound texture, - // OpenGL ES specifies that the texture is then considered unbound - // Consider the following series of calls: - // - // glGenTextures -> creates texture name 2 - // glBindTexture(2) - // glDeleteTextures(2) -> 2 is now unbound - // glGenTextures -> can return 2 again - // - // If we don't call glBindTexture(2) after the second glGenTextures - // call, any texture operation will be performed on the default - // texture (name=0) - - unbindTexture(texture); - - glDeleteTextures(1, &texture); -} - -void Caches::resetBoundTextures() { - memset(mBoundTextures, 0, REQUIRED_TEXTURE_UNITS_COUNT * sizeof(GLuint)); -} - -void Caches::unbindTexture(GLuint texture) { - for (int i = 0; i < REQUIRED_TEXTURE_UNITS_COUNT; i++) { - if (mBoundTextures[i] == texture) { - mBoundTextures[i] = 0; - } - } -} - -/////////////////////////////////////////////////////////////////////////////// -// Scissor -/////////////////////////////////////////////////////////////////////////////// - -bool Caches::setScissor(GLint x, GLint y, GLint width, GLint height) { - if (scissorEnabled && (x != mScissorX || y != mScissorY || - width != mScissorWidth || height != mScissorHeight)) { - - if (x < 0) { - width += x; - x = 0; - } - if (y < 0) { - height += y; - y = 0; - } - if (width < 0) { - width = 0; - } - if (height < 0) { - height = 0; - } - glScissor(x, y, width, height); - - mScissorX = x; - mScissorY = y; - mScissorWidth = width; - mScissorHeight = height; - - return true; - } - return false; -} - -bool Caches::enableScissor() { - if (!scissorEnabled) { - glEnable(GL_SCISSOR_TEST); - scissorEnabled = true; - resetScissor(); - return true; - } - return false; -} - -bool Caches::disableScissor() { - if (scissorEnabled) { - glDisable(GL_SCISSOR_TEST); - scissorEnabled = false; - return true; - } - return false; -} - -void Caches::setScissorEnabled(bool enabled) { - if (scissorEnabled != enabled) { - if (enabled) glEnable(GL_SCISSOR_TEST); - else glDisable(GL_SCISSOR_TEST); - scissorEnabled = enabled; - } -} - -void Caches::resetScissor() { - mScissorX = mScissorY = mScissorWidth = mScissorHeight = 0; -} - -/////////////////////////////////////////////////////////////////////////////// // Tiling /////////////////////////////////////////////////////////////////////////////// @@ -688,10 +385,10 @@ void Caches::unregisterFunctors(uint32_t functorCount) { TextureVertex* Caches::getRegionMesh() { // Create the mesh, 2 triangles and 4 vertices per rectangle in the region if (!mRegionMesh) { - mRegionMesh = new TextureVertex[gMaxNumberOfQuads * 4]; + mRegionMesh.reset(new TextureVertex[kMaxNumberOfQuads * 4]); } - return mRegionMesh; + return mRegionMesh.get(); } /////////////////////////////////////////////////////////////////////////////// @@ -699,7 +396,7 @@ TextureVertex* Caches::getRegionMesh() { /////////////////////////////////////////////////////////////////////////////// void Caches::initTempProperties() { - propertyLightDiameter = -1.0f; + propertyLightRadius = -1.0f; propertyLightPosY = -1.0f; propertyLightPosZ = -1.0f; propertyAmbientRatio = -1.0f; @@ -713,9 +410,9 @@ void Caches::setTempProperty(const char* name, const char* value) { propertyAmbientRatio = fmin(fmax(atof(value), 0.0), 10.0); ALOGD("ambientRatio = %.2f", propertyAmbientRatio); return; - } else if (!strcmp(name, "lightDiameter")) { - propertyLightDiameter = fmin(fmax(atof(value), 0.0), 3000.0); - ALOGD("lightDiameter = %.2f", propertyLightDiameter); + } else if (!strcmp(name, "lightRadius")) { + propertyLightRadius = fmin(fmax(atof(value), 0.0), 3000.0); + ALOGD("lightRadius = %.2f", propertyLightRadius); return; } else if (!strcmp(name, "lightPosY")) { propertyLightPosY = fmin(fmax(atof(value), 0.0), 3000.0); diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h index 2e179af..8aea8ff 100644 --- a/libs/hwui/Caches.h +++ b/libs/hwui/Caches.h @@ -21,86 +21,43 @@ #define LOG_TAG "OpenGLRenderer" #endif -#include <vector> - -#include <GLES3/gl3.h> - -#include <utils/KeyedVector.h> -#include <utils/Singleton.h> -#include <utils/Vector.h> - -#include <cutils/compiler.h> - -#include "thread/TaskProcessor.h" -#include "thread/TaskManager.h" #include "AssetAtlas.h" +#include "Dither.h" #include "Extensions.h" -#include "FontRenderer.h" -#include "GammaFontRenderer.h" -#include "TextureCache.h" -#include "LayerCache.h" -#include "RenderBufferCache.h" +#include "FboCache.h" #include "GradientCache.h" +#include "LayerCache.h" #include "PatchCache.h" #include "ProgramCache.h" #include "PathCache.h" +#include "RenderBufferCache.h" +#include "renderstate/PixelBufferState.h" +#include "renderstate/TextureState.h" +#include "ResourceCache.h" #include "TessellationCache.h" #include "TextDropShadowCache.h" -#include "FboCache.h" -#include "ResourceCache.h" -#include "Stencil.h" -#include "Dither.h" +#include "TextureCache.h" +#include "thread/TaskProcessor.h" +#include "thread/TaskManager.h" -namespace android { -namespace uirenderer { +#include <vector> +#include <memory> -/////////////////////////////////////////////////////////////////////////////// -// Globals -/////////////////////////////////////////////////////////////////////////////// +#include <GLES3/gl3.h> -// GL ES 2.0 defines that at least 16 texture units must be supported -#define REQUIRED_TEXTURE_UNITS_COUNT 3 - -// Maximum number of quads that pre-allocated meshes can draw -static const uint32_t gMaxNumberOfQuads = 2048; - -// Generates simple and textured vertices -#define FV(x, y, u, v) { x, y, u, v } - -// This array is never used directly but used as a memcpy source in the -// OpenGLRenderer constructor -static const TextureVertex gMeshVertices[] = { - FV(0.0f, 0.0f, 0.0f, 0.0f), - FV(1.0f, 0.0f, 1.0f, 0.0f), - FV(0.0f, 1.0f, 0.0f, 1.0f), - FV(1.0f, 1.0f, 1.0f, 1.0f) -}; -static const GLsizei gMeshStride = sizeof(TextureVertex); -static const GLsizei gVertexStride = sizeof(Vertex); -static const GLsizei gAlphaVertexStride = sizeof(AlphaVertex); -static const GLsizei gMeshTextureOffset = 2 * sizeof(float); -static const GLsizei gVertexAlphaOffset = 2 * sizeof(float); -static const GLsizei gVertexAAWidthOffset = 2 * sizeof(float); -static const GLsizei gVertexAALengthOffset = 3 * sizeof(float); -static const GLsizei gMeshCount = 4; - -// Must define as many texture units as specified by REQUIRED_TEXTURE_UNITS_COUNT -static const GLenum gTextureUnits[] = { - GL_TEXTURE0, - GL_TEXTURE1, - GL_TEXTURE2 -}; +#include <utils/KeyedVector.h> +#include <utils/Singleton.h> +#include <utils/Vector.h> -/////////////////////////////////////////////////////////////////////////////// -// Debug -/////////////////////////////////////////////////////////////////////////////// +#include <cutils/compiler.h> -struct CacheLogger { - CacheLogger() { - INIT_LOGD("Creating OpenGL renderer caches"); - } -}; // struct CacheLogger +#include <SkPath.h> + +namespace android { +namespace uirenderer { + +class GammaFontRenderer; /////////////////////////////////////////////////////////////////////////////// // Caches @@ -109,12 +66,25 @@ struct CacheLogger { class RenderNode; class RenderState; -class ANDROID_API Caches: public Singleton<Caches> { - Caches(); +class ANDROID_API Caches { +public: + static Caches& createInstance(RenderState& renderState) { + LOG_ALWAYS_FATAL_IF(sInstance, "double create of Caches attempted"); + sInstance = new Caches(renderState); + return *sInstance; + } - friend class Singleton<Caches>; + static Caches& getInstance() { + LOG_ALWAYS_FATAL_IF(!sInstance, "instance not yet created"); + return *sInstance; + } - CacheLogger mLogger; + static bool hasInstance() { + return sInstance != 0; + } +private: + Caches(RenderState& renderState); + static Caches* sInstance; public: enum FlushMode { @@ -133,8 +103,6 @@ public: */ bool initProperties(); - void setRenderState(RenderState* renderState) { mRenderState = renderState; } - /** * Flush the cache. * @@ -173,116 +141,6 @@ public: */ void deleteLayerDeferred(Layer* layer); - /** - * Binds the VBO used to render simple textured quads. - */ - bool bindMeshBuffer(); - - /** - * Binds the specified VBO if needed. - */ - bool bindMeshBuffer(const GLuint buffer); - - /** - * Unbinds the VBO used to render simple textured quads. - */ - bool unbindMeshBuffer(); - - /** - * Binds a global indices buffer that can draw up to - * gMaxNumberOfQuads quads. - */ - bool bindQuadIndicesBuffer(); - bool bindShadowIndicesBuffer(); - bool unbindIndicesBuffer(); - - /** - * Binds the specified buffer as the current GL unpack pixel buffer. - */ - bool bindPixelBuffer(const GLuint buffer); - - /** - * Resets the current unpack pixel buffer to 0 (default value.) - */ - bool unbindPixelBuffer(); - - /** - * Binds an attrib to the specified float vertex pointer. - * Assumes a stride of gMeshStride and a size of 2. - */ - void bindPositionVertexPointer(bool force, const GLvoid* vertices, GLsizei stride = gMeshStride); - - /** - * Binds an attrib to the specified float vertex pointer. - * Assumes a stride of gMeshStride and a size of 2. - */ - void bindTexCoordsVertexPointer(bool force, const GLvoid* vertices, GLsizei stride = gMeshStride); - - /** - * Resets the vertex pointers. - */ - void resetVertexPointers(); - void resetTexCoordsVertexPointer(); - - void enableTexCoordsVertexArray(); - void disableTexCoordsVertexArray(); - - /** - * Activate the specified texture unit. The texture unit must - * be specified using an integer number (0 for GL_TEXTURE0 etc.) - */ - void activeTexture(GLuint textureUnit); - - /** - * Invalidate the cached value of the active texture unit. - */ - void resetActiveTexture(); - - /** - * Binds the specified texture as a GL_TEXTURE_2D texture. - * All texture bindings must be performed with this method or - * bindTexture(GLenum, GLuint). - */ - void bindTexture(GLuint texture); - - /** - * Binds the specified texture with the specified render target. - * All texture bindings must be performed with this method or - * bindTexture(GLuint). - */ - void bindTexture(GLenum target, GLuint texture); - - /** - * Deletes the specified texture and clears it from the cache - * of bound textures. - * All textures must be deleted using this method. - */ - void deleteTexture(GLuint texture); - - /** - * Signals that the cache of bound textures should be cleared. - * Other users of the context may have altered which textures are bound. - */ - void resetBoundTextures(); - - /** - * Clear the cache of bound textures. - */ - void unbindTexture(GLuint texture); - - /** - * Sets the scissor for the current surface. - */ - bool setScissor(GLint x, GLint y, GLint width, GLint height); - - /** - * Resets the scissor state. - */ - void resetScissor(); - - bool enableScissor(); - bool disableScissor(); - void setScissorEnabled(bool enabled); void startTiling(GLuint x, GLuint y, GLuint width, GLuint height, bool discard); void endTiling(); @@ -304,18 +162,9 @@ public: void registerFunctors(uint32_t functorCount); void unregisterFunctors(uint32_t functorCount); - bool blend; - GLenum lastSrcMode; - GLenum lastDstMode; - Program* currentProgram; - bool scissorEnabled; - bool drawDeferDisabled; bool drawReorderDisabled; - // VBO to draw with - GLuint meshBuffer; - // Misc GLint maxTextureSize; @@ -329,14 +178,18 @@ public: kStencilShowRegion }; StencilClipDebug debugStencilClip; - +private: + // Declared before gradientCache and programCache which need this to initialize. + // TODO: cleanup / move elsewhere + Extensions mExtensions; +public: TextureCache textureCache; LayerCache layerCache; RenderBufferCache renderBufferCache; GradientCache gradientCache; - ProgramCache programCache; - PathCache pathCache; PatchCache patchCache; + PathCache pathCache; + ProgramCache programCache; TessellationCache tessellationCache; TextDropShadowCache dropShadowCache; FboCache fboCache; @@ -346,7 +199,6 @@ public: TaskManager tasks; Dither dither; - Stencil stencil; bool gpuPixelBuffersEnabled; @@ -355,20 +207,25 @@ public: PFNGLPUSHGROUPMARKEREXTPROC startMark; PFNGLPOPGROUPMARKEREXTPROC endMark; - PFNGLLABELOBJECTEXTPROC setLabel; - PFNGLGETOBJECTLABELEXTPROC getLabel; - // TEMPORARY properties void initTempProperties(); void setTempProperty(const char* name, const char* value); - float propertyLightDiameter; + float propertyLightRadius; float propertyLightPosY; float propertyLightPosZ; float propertyAmbientRatio; int propertyAmbientShadowStrength; int propertySpotShadowStrength; + void setProgram(const ProgramDescription& description); + void setProgram(Program* program); + + Extensions& extensions() { return mExtensions; } + Program& program() { return *mProgram; } + PixelBufferState& pixelBufferState() { return *mPixelBufferState; } + TextureState& textureState() { return *mTextureState; } + private: enum OverdrawColorSet { kColorSet_Default = 0, @@ -380,45 +237,14 @@ private: void initConstraints(); void initStaticProperties(); - bool bindIndicesBufferInternal(const GLuint buffer); - static void eventMarkNull(GLsizei length, const GLchar* marker) { } static void startMarkNull(GLsizei length, const GLchar* marker) { } static void endMarkNull() { } - static void setLabelNull(GLenum type, uint object, GLsizei length, - const char* label) { } - static void getLabelNull(GLenum type, uint object, GLsizei bufferSize, - GLsizei* length, char* label) { - if (length) *length = 0; - if (label) *label = '\0'; - } - - GLuint mCurrentBuffer; - GLuint mCurrentIndicesBuffer; - GLuint mCurrentPixelBuffer; - const void* mCurrentPositionPointer; - GLsizei mCurrentPositionStride; - const void* mCurrentTexCoordsPointer; - GLsizei mCurrentTexCoordsStride; - - bool mTexCoordsArrayEnabled; - - GLuint mTextureUnit; - - GLint mScissorX; - GLint mScissorY; - GLint mScissorWidth; - GLint mScissorHeight; - - Extensions& mExtensions; + RenderState* mRenderState; // Used to render layers - TextureVertex* mRegionMesh; - - // Global index buffer - GLuint mMeshIndices; - GLuint mShadowStripsIndices; + std::unique_ptr<TextureVertex[]> mRegionMesh; mutable Mutex mGarbageLock; Vector<Layer*> mLayerGarbage; @@ -428,12 +254,13 @@ private: uint32_t mFunctorsCount; - // Caches texture bindings for the GL_TEXTURE_2D target - GLuint mBoundTextures[REQUIRED_TEXTURE_UNITS_COUNT]; - OverdrawColorSet mOverdrawDebugColorSet; - RenderState* mRenderState; + // TODO: move below to RenderState + PixelBufferState* mPixelBufferState = nullptr; + TextureState* mTextureState = nullptr; + Program* mProgram = nullptr; // note: object owned by ProgramCache + }; // class Caches }; // namespace uirenderer diff --git a/libs/hwui/Canvas.h b/libs/hwui/Canvas.h new file mode 100644 index 0000000..aa24673 --- /dev/null +++ b/libs/hwui/Canvas.h @@ -0,0 +1,164 @@ +/* + * 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 ANDROID_GRAPHICS_CANVAS_H +#define ANDROID_GRAPHICS_CANVAS_H + +#include <cutils/compiler.h> + +#include <SkBitmap.h> +#include <SkCanvas.h> +#include <SkMatrix.h> + +namespace android { + +class ANDROID_API Canvas { +public: + virtual ~Canvas() {}; + + static Canvas* create_canvas(const SkBitmap& bitmap); + + /** + * Create a new Canvas object which delegates to an SkCanvas. + * + * @param skiaCanvas Must not be NULL. All drawing calls will be + * 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. + */ + static Canvas* create_canvas(SkCanvas* skiaCanvas); + + /** + * Provides a Skia SkCanvas interface that acts as a proxy to this Canvas. + * It is useful for testing and clients (e.g. Picture/Movie) that expect to + * draw their contents into an SkCanvas. + * + * Further, the returned SkCanvas should NOT be unref'd and is valid until + * this canvas is destroyed or a new bitmap is set. + */ + virtual SkCanvas* asSkCanvas() = 0; + + virtual void setBitmap(const SkBitmap& bitmap) = 0; + + virtual bool isOpaque() = 0; + virtual int width() = 0; + virtual int height() = 0; + +// ---------------------------------------------------------------------------- +// Canvas state operations +// ---------------------------------------------------------------------------- + // Save (layer) + virtual int getSaveCount() const = 0; + virtual int save(SkCanvas::SaveFlags flags) = 0; + virtual void restore() = 0; + virtual void restoreToCount(int saveCount) = 0; + + virtual int saveLayer(float left, float top, float right, float bottom, + const SkPaint* paint, SkCanvas::SaveFlags flags) = 0; + virtual int saveLayerAlpha(float left, float top, float right, float bottom, + int alpha, SkCanvas::SaveFlags flags) = 0; + + // Matrix + virtual void getMatrix(SkMatrix* outMatrix) const = 0; + virtual void setMatrix(const SkMatrix& matrix) = 0; + + virtual void concat(const SkMatrix& matrix) = 0; + virtual void rotate(float degrees) = 0; + virtual void scale(float sx, float sy) = 0; + virtual void skew(float sx, float sy) = 0; + virtual void translate(float dx, float dy) = 0; + + // clip + virtual bool getClipBounds(SkRect* outRect) const = 0; + virtual bool quickRejectRect(float left, float top, float right, float bottom) const = 0; + virtual bool quickRejectPath(const SkPath& path) const = 0; + + virtual bool clipRect(float left, float top, float right, float bottom, + SkRegion::Op op = SkRegion::kIntersect_Op) = 0; + virtual bool clipPath(const SkPath* path, SkRegion::Op op) = 0; + virtual bool clipRegion(const SkRegion* region, SkRegion::Op op) = 0; + + // filters + virtual SkDrawFilter* getDrawFilter() = 0; + virtual void setDrawFilter(SkDrawFilter* drawFilter) = 0; + +// ---------------------------------------------------------------------------- +// Canvas draw operations +// ---------------------------------------------------------------------------- + virtual void drawColor(int color, SkXfermode::Mode mode) = 0; + virtual void drawPaint(const SkPaint& paint) = 0; + + // Geometry + virtual void drawPoint(float x, float y, const SkPaint& paint) = 0; + virtual void drawPoints(const float* points, int count, const SkPaint& paint) = 0; + virtual void drawLine(float startX, float startY, float stopX, float stopY, + const SkPaint& paint) = 0; + virtual void drawLines(const float* points, int count, const SkPaint& paint) = 0; + virtual void drawRect(float left, float top, float right, float bottom, + const SkPaint& paint) = 0; + virtual void drawRoundRect(float left, float top, float right, float bottom, + float rx, float ry, const SkPaint& paint) = 0; + virtual void drawCircle(float x, float y, float radius, const SkPaint& paint) = 0; + virtual void drawOval(float left, float top, float right, float bottom, + const SkPaint& paint) = 0; + virtual void drawArc(float left, float top, float right, float bottom, + float startAngle, float sweepAngle, bool useCenter, const SkPaint& paint) = 0; + virtual void drawPath(const SkPath& path, const SkPaint& paint) = 0; + 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) = 0; + + // Bitmap-based + virtual void drawBitmap(const SkBitmap& bitmap, float left, float top, + const SkPaint* paint) = 0; + virtual void drawBitmap(const SkBitmap& bitmap, const SkMatrix& matrix, + const SkPaint* paint) = 0; + 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) = 0; + virtual void drawBitmapMesh(const SkBitmap& bitmap, int meshWidth, int meshHeight, + const float* vertices, const int* colors, const SkPaint* paint) = 0; + + // Text + /** + * drawText: count is of glyphs + * totalAdvance is ignored in software renderering, used by hardware renderer for + * text decorations (underlines, strikethroughs). + */ + virtual void drawText(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) = 0; + /** drawPosText: count is of UTF16 characters, posCount is floats (2 * glyphs) */ + virtual void drawPosText(const uint16_t* text, const float* positions, int count, + int posCount, const SkPaint& paint) = 0; + /** drawTextOnPath: count is of glyphs */ + virtual void drawTextOnPath(const uint16_t* glyphs, int count, const SkPath& path, + float hOffset, float vOffset, const SkPaint& paint) = 0; + + /** + * Specifies if the positions passed to ::drawText are absolute or relative + * to the (x,y) value provided. + * + * If true the (x,y) values are ignored. Otherwise, those (x,y) values need + * to be added to each glyph's position to get its absolute position. + */ + virtual bool drawTextAbsolutePos() const = 0; +}; + +}; // namespace android +#endif // ANDROID_GRAPHICS_CANVAS_H diff --git a/libs/hwui/StatefulBaseRenderer.cpp b/libs/hwui/CanvasState.cpp index 88d6f68..e88e9f6 100644 --- a/libs/hwui/StatefulBaseRenderer.cpp +++ b/libs/hwui/CanvasState.cpp @@ -14,41 +14,45 @@ * limitations under the License. */ -#define LOG_TAG "OpenGLRenderer" - #include <SkCanvas.h> -#include "StatefulBaseRenderer.h" - +#include "CanvasState.h" #include "utils/MathUtils.h" namespace android { namespace uirenderer { -StatefulBaseRenderer::StatefulBaseRenderer() + +CanvasState::CanvasState(CanvasStateClient& renderer) : mDirtyClip(false) , mWidth(-1) , mHeight(-1) , mSaveCount(1) , mFirstSnapshot(new Snapshot) + , mCanvas(renderer) , mSnapshot(mFirstSnapshot) { + } -void StatefulBaseRenderer::initializeSaveStack(float clipLeft, float clipTop, +CanvasState::~CanvasState() { + +} + +void CanvasState::initializeSaveStack(float clipLeft, float clipTop, float clipRight, float clipBottom, const Vector3& lightCenter) { mSnapshot = new Snapshot(mFirstSnapshot, SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag); mSnapshot->setClip(clipLeft, clipTop, clipRight, clipBottom); - mSnapshot->fbo = getTargetFbo(); + mSnapshot->fbo = mCanvas.getTargetFbo(); mSnapshot->setRelativeLightCenter(lightCenter); mSaveCount = 1; } -void StatefulBaseRenderer::setViewport(int width, int height) { +void CanvasState::setViewport(int width, int height) { mWidth = width; mHeight = height; mFirstSnapshot->initializeViewport(width, height); - onViewportInitialized(); + mCanvas.onViewportInitialized(); // create a temporary 1st snapshot, so old snapshots are released, // and viewport can be queried safely. @@ -63,24 +67,24 @@ void StatefulBaseRenderer::setViewport(int width, int height) { /////////////////////////////////////////////////////////////////////////////// /** - * Non-virtual implementation of save, guaranteed to save without side-effects + * Guaranteed to save without side-effects * - * The approach here and in restoreSnapshot(), allows subclasses to directly manipulate the save + * This approach, here and in restoreSnapshot(), allows subclasses to directly manipulate the save * stack, and ensures restoreToCount() doesn't call back into subclass overrides. */ -int StatefulBaseRenderer::saveSnapshot(int flags) { +int CanvasState::saveSnapshot(int flags) { mSnapshot = new Snapshot(mSnapshot, flags); return mSaveCount++; } -int StatefulBaseRenderer::save(int flags) { +int CanvasState::save(int flags) { return saveSnapshot(flags); } /** - * Non-virtual implementation of restore, guaranteed to restore without side-effects. + * Guaranteed to restore without side-effects. */ -void StatefulBaseRenderer::restoreSnapshot() { +void CanvasState::restoreSnapshot() { sp<Snapshot> toRemove = mSnapshot; sp<Snapshot> toRestore = mSnapshot->previous; @@ -88,16 +92,16 @@ void StatefulBaseRenderer::restoreSnapshot() { mSnapshot = toRestore; // subclass handles restore implementation - onSnapshotRestored(*toRemove, *toRestore); + mCanvas.onSnapshotRestored(*toRemove, *toRestore); } -void StatefulBaseRenderer::restore() { +void CanvasState::restore() { if (mSaveCount > 1) { restoreSnapshot(); } } -void StatefulBaseRenderer::restoreToCount(int saveCount) { +void CanvasState::restoreToCount(int saveCount) { if (saveCount < 1) saveCount = 1; while (mSaveCount > saveCount) { @@ -109,40 +113,40 @@ void StatefulBaseRenderer::restoreToCount(int saveCount) { // Matrix /////////////////////////////////////////////////////////////////////////////// -void StatefulBaseRenderer::getMatrix(SkMatrix* matrix) const { +void CanvasState::getMatrix(SkMatrix* matrix) const { mSnapshot->transform->copyTo(*matrix); } -void StatefulBaseRenderer::translate(float dx, float dy, float dz) { +void CanvasState::translate(float dx, float dy, float dz) { mSnapshot->transform->translate(dx, dy, dz); } -void StatefulBaseRenderer::rotate(float degrees) { +void CanvasState::rotate(float degrees) { mSnapshot->transform->rotate(degrees, 0.0f, 0.0f, 1.0f); } -void StatefulBaseRenderer::scale(float sx, float sy) { +void CanvasState::scale(float sx, float sy) { mSnapshot->transform->scale(sx, sy, 1.0f); } -void StatefulBaseRenderer::skew(float sx, float sy) { +void CanvasState::skew(float sx, float sy) { mSnapshot->transform->skew(sx, sy); } -void StatefulBaseRenderer::setMatrix(const SkMatrix& matrix) { +void CanvasState::setMatrix(const SkMatrix& matrix) { mSnapshot->transform->load(matrix); } -void StatefulBaseRenderer::setMatrix(const Matrix4& matrix) { +void CanvasState::setMatrix(const Matrix4& matrix) { mSnapshot->transform->load(matrix); } -void StatefulBaseRenderer::concatMatrix(const SkMatrix& matrix) { +void CanvasState::concatMatrix(const SkMatrix& matrix) { mat4 transform(matrix); mSnapshot->transform->multiply(transform); } -void StatefulBaseRenderer::concatMatrix(const Matrix4& matrix) { +void CanvasState::concatMatrix(const Matrix4& matrix) { mSnapshot->transform->multiply(matrix); } @@ -150,51 +154,22 @@ void StatefulBaseRenderer::concatMatrix(const Matrix4& matrix) { // Clip /////////////////////////////////////////////////////////////////////////////// -bool StatefulBaseRenderer::clipRect(float left, float top, float right, float bottom, SkRegion::Op op) { - if (CC_LIKELY(currentTransform()->rectToRect())) { - mDirtyClip |= mSnapshot->clip(left, top, right, bottom, op); - return !mSnapshot->clipRect->isEmpty(); - } - - SkPath path; - path.addRect(left, top, right, bottom); - - return StatefulBaseRenderer::clipPath(&path, op); +bool CanvasState::clipRect(float left, float top, float right, float bottom, SkRegion::Op op) { + mDirtyClip |= mSnapshot->clip(left, top, right, bottom, op); + return !mSnapshot->clipIsEmpty(); } -bool StatefulBaseRenderer::clipPath(const SkPath* path, SkRegion::Op op) { - SkMatrix transform; - currentTransform()->copyTo(transform); - - SkPath transformed; - path->transform(transform, &transformed); - - SkRegion clip; - if (!mSnapshot->previous->clipRegion->isEmpty()) { - clip.setRegion(*mSnapshot->previous->clipRegion); - } else { - if (mSnapshot->previous == firstSnapshot()) { - clip.setRect(0, 0, getWidth(), getHeight()); - } else { - Rect* bounds = mSnapshot->previous->clipRect; - clip.setRect(bounds->left, bounds->top, bounds->right, bounds->bottom); - } - } - - SkRegion region; - region.setPath(transformed, clip); - - // region is the transformed input path, masked by the previous clip - mDirtyClip |= mSnapshot->clipRegionTransformed(region, op); - return !mSnapshot->clipRect->isEmpty(); +bool CanvasState::clipPath(const SkPath* path, SkRegion::Op op) { + mDirtyClip |= mSnapshot->clipPath(*path, op); + return !mSnapshot->clipIsEmpty(); } -bool StatefulBaseRenderer::clipRegion(const SkRegion* region, SkRegion::Op op) { +bool CanvasState::clipRegion(const SkRegion* region, SkRegion::Op op) { mDirtyClip |= mSnapshot->clipRegionTransformed(*region, op); - return !mSnapshot->clipRect->isEmpty(); + return !mSnapshot->clipIsEmpty(); } -void StatefulBaseRenderer::setClippingOutline(LinearAllocator& allocator, const Outline* outline) { +void CanvasState::setClippingOutline(LinearAllocator& allocator, const Outline* outline) { Rect bounds; float radius; if (!outline->getAsRoundRect(&bounds, &radius)) return; // only RR supported @@ -209,7 +184,7 @@ void StatefulBaseRenderer::setClippingOutline(LinearAllocator& allocator, const } } -void StatefulBaseRenderer::setClippingRoundRect(LinearAllocator& allocator, +void CanvasState::setClippingRoundRect(LinearAllocator& allocator, const Rect& rect, float radius, bool highPriority) { mSnapshot->setClippingRoundRect(allocator, rect, radius, highPriority); } @@ -229,7 +204,7 @@ void StatefulBaseRenderer::setClippingRoundRect(LinearAllocator& allocator, * @param snapOut if set, the geometry will be treated as having an AA ramp. * See Rect::snapGeometryToPixelBoundaries() */ -bool StatefulBaseRenderer::calculateQuickRejectForScissor(float left, float top, +bool CanvasState::calculateQuickRejectForScissor(float left, float top, float right, float bottom, bool* clipRequired, bool* roundRectClipRequired, bool snapOut) const { @@ -241,7 +216,7 @@ bool StatefulBaseRenderer::calculateQuickRejectForScissor(float left, float top, currentTransform()->mapRect(r); r.snapGeometryToPixelBoundaries(snapOut); - Rect clipRect(*currentClipRect()); + Rect clipRect(currentClipRect()); clipRect.snapToPixelBoundaries(); if (!clipRect.intersects(r)) return true; @@ -253,24 +228,13 @@ bool StatefulBaseRenderer::calculateQuickRejectForScissor(float left, float top, // round rect clip is required if RR clip exists, and geometry intersects its corners if (roundRectClipRequired) { - *roundRectClipRequired = mSnapshot->roundRectClipState != NULL + *roundRectClipRequired = mSnapshot->roundRectClipState != nullptr && mSnapshot->roundRectClipState->areaRequiresRoundRectClip(r); } return false; } -/** - * Returns false if drawing won't be clipped out. - * - * Makes the decision conservatively, by rounding out the mapped rect before comparing with the - * clipRect. To be used when perfect, pixel accuracy is not possible (esp. with tessellation) but - * rejection is still desired. - * - * This function, unlike quickRejectSetupScissor, should be used where precise geometry information - * isn't known (esp. when geometry adjusts based on scale). Generally, this will be first pass - * rejection where precise rejection isn't important, or precise information isn't available. - */ -bool StatefulBaseRenderer::quickRejectConservative(float left, float top, +bool CanvasState::quickRejectConservative(float left, float top, float right, float bottom) const { if (mSnapshot->isIgnored() || bottom <= top || right <= left) { return true; @@ -280,7 +244,7 @@ bool StatefulBaseRenderer::quickRejectConservative(float left, float top, currentTransform()->mapRect(r); r.roundOut(); // rounded out to be conservative - Rect clipRect(*currentClipRect()); + Rect clipRect(currentClipRect()); clipRect.snapToPixelBoundaries(); if (!clipRect.intersects(r)) return true; @@ -288,5 +252,5 @@ bool StatefulBaseRenderer::quickRejectConservative(float left, float top, return false; } -}; // namespace uirenderer -}; // namespace android +} // namespace uirenderer +} // namespace android diff --git a/libs/hwui/CanvasState.h b/libs/hwui/CanvasState.h new file mode 100644 index 0000000..8e4a4d3 --- /dev/null +++ b/libs/hwui/CanvasState.h @@ -0,0 +1,193 @@ +/* + * 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 ANDROID_HWUI_CANVAS_STATE_H +#define ANDROID_HWUI_CANVAS_STATE_H + +#include <SkMatrix.h> +#include <SkPath.h> +#include <SkRegion.h> + +#include "Snapshot.h" + +namespace android { +namespace uirenderer { + +/** + * Abstract base class for any class containing CanvasState. + * Defines three mandatory callbacks. + */ +class CanvasStateClient { +public: + CanvasStateClient() { } + virtual ~CanvasStateClient() { } + + /** + * Callback allowing embedder to take actions in the middle of a + * setViewport() call. + */ + virtual void onViewportInitialized() = 0; + + /** + * Callback allowing embedder to take actions in the middle of a + * restore() call. May be called several times sequentially. + */ + virtual void onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) = 0; + + /** + * Allows subclasses to control what value is stored in snapshot's + * fbo field in * initializeSaveStack. + */ + virtual GLuint getTargetFbo() const = 0; + +}; // class CanvasStateClient + +/** + * Implements Canvas state methods on behalf of Renderers. + * + * Manages the Snapshot stack, implementing matrix, save/restore, and clipping methods in the + * 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 + * 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 + * through state operations to CanvasState, so that not only will querying operations work + * (getClip/Matrix), but so that quickRejection can also be used. + */ + +class ANDROID_API CanvasState { +public: + CanvasState(CanvasStateClient& renderer); + ~CanvasState(); + + /** + * Initializes the first snapshot, computing the projection matrix, + * and stores the dimensions of the render target. + */ + void initializeSaveStack(float clipLeft, float clipTop, float clipRight, float clipBottom, + const Vector3& lightCenter); + + void setViewport(int width, int height); + + bool hasRectToRectTransform() const { + return CC_LIKELY(currentTransform()->rectToRect()); + } + + // Save (layer) + int getSaveCount() const { return mSaveCount; } + int save(int flags); + void restore(); + void restoreToCount(int saveCount); + + // Save/Restore without side-effects + int saveSnapshot(int flags); + void restoreSnapshot(); + + // Matrix + void getMatrix(SkMatrix* outMatrix) const; + 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 SkMatrix& matrix); + void setMatrix(const Matrix4& matrix); // internal only convenience method + void concatMatrix(const SkMatrix& matrix); + void concatMatrix(const Matrix4& matrix); // internal only convenience method + + // Clip + const Rect& getLocalClipBounds() const { return mSnapshot->getLocalClip(); } + const Rect& getRenderTargetClipBounds() const { return mSnapshot->getRenderTargetClip(); } + + bool quickRejectConservative(float left, float top, float right, float bottom) const; + + bool clipRect(float left, float top, float right, float bottom, SkRegion::Op op); + bool clipPath(const SkPath* path, SkRegion::Op op); + bool clipRegion(const SkRegion* region, SkRegion::Op op); + + /** + * Sets a "clipping outline", which is independent from the regular clip. + * Currently only supports rectangles or rounded rectangles; passing in a + * more complicated outline fails silently. Replaces any previous clipping + * outline. + */ + void setClippingOutline(LinearAllocator& allocator, const Outline* outline); + void setClippingRoundRect(LinearAllocator& allocator, + const Rect& rect, float radius, bool highPriority = true); + + /** + * Returns true if drawing in the rectangle (left, top, right, bottom) + * will be clipped out. Is conservative: might return false when subpixel- + * perfect tests would return true. + */ + 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& currentClipRect() const { return currentSnapshot()->getClipRect(); } + 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() { return mWidth; } + int getHeight() { return mHeight; } + + inline const Snapshot* currentSnapshot() const { + return mSnapshot != nullptr ? mSnapshot.get() : mFirstSnapshot.get(); + } + inline Snapshot* writableSnapshot() { return mSnapshot.get(); } + inline const Snapshot* firstSnapshot() const { return mFirstSnapshot.get(); } + +private: + /// No default constructor - must supply a CanvasStateClient (mCanvas). + CanvasState(); + + /// indicates that the clip has been changed since the last time it was consumed + bool mDirtyClip; + + /// Dimensions of the drawing surface + int mWidth, mHeight; + + /// Number of saved states + int mSaveCount; + + /// Base state + sp<Snapshot> mFirstSnapshot; + + /// Host providing callbacks + CanvasStateClient& mCanvas; + + /// Current state + sp<Snapshot> mSnapshot; + +}; // class CanvasState + +}; // namespace uirenderer +}; // namespace android + +#endif // ANDROID_HWUI_CANVAS_STATE_H diff --git a/libs/hwui/ClipArea.cpp b/libs/hwui/ClipArea.cpp new file mode 100644 index 0000000..eb520b4 --- /dev/null +++ b/libs/hwui/ClipArea.cpp @@ -0,0 +1,373 @@ +/* + * 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 "ClipArea.h" + +#include <SkPath.h> +#include <limits> + +#include "Rect.h" + +namespace android { +namespace uirenderer { + +static bool intersect(Rect& r, const Rect& r2) { + bool hasIntersection = r.intersect(r2); + if (!hasIntersection) { + r.setEmpty(); + } + return hasIntersection; +} + +static void handlePoint(Rect& transformedBounds, const Matrix4& transform, float x, float y) { + Vertex v; + v.x = x; + v.y = y; + transform.mapPoint(v.x, v.y); + transformedBounds.expandToCoverVertex(v.x, v.y); +} + +Rect transformAndCalculateBounds(const Rect& r, const Matrix4& transform) { + const float kMinFloat = std::numeric_limits<float>::lowest(); + const float kMaxFloat = std::numeric_limits<float>::max(); + Rect transformedBounds = { kMaxFloat, kMaxFloat, kMinFloat, kMinFloat }; + handlePoint(transformedBounds, transform, r.left, r.top); + handlePoint(transformedBounds, transform, r.right, r.top); + handlePoint(transformedBounds, transform, r.left, r.bottom); + handlePoint(transformedBounds, transform, r.right, r.bottom); + return transformedBounds; +} + +/* + * TransformedRectangle + */ + +TransformedRectangle::TransformedRectangle() { +} + +TransformedRectangle::TransformedRectangle(const Rect& bounds, + const Matrix4& transform) + : mBounds(bounds) + , mTransform(transform) { +} + +bool TransformedRectangle::canSimplyIntersectWith( + const TransformedRectangle& other) const { + + return mTransform == other.mTransform; +} + +bool TransformedRectangle::intersectWith(const TransformedRectangle& other) { + Rect translatedBounds(other.mBounds); + return intersect(mBounds, translatedBounds); +} + +bool TransformedRectangle::isEmpty() const { + return mBounds.isEmpty(); +} + +/* + * RectangleList + */ + +RectangleList::RectangleList() + : mTransformedRectanglesCount(0) { +} + +bool RectangleList::isEmpty() const { + if (mTransformedRectanglesCount < 1) { + return true; + } + + for (int i = 0; i < mTransformedRectanglesCount; i++) { + if (mTransformedRectangles[i].isEmpty()) { + return true; + } + } + return false; +} + +int RectangleList::getTransformedRectanglesCount() const { + return mTransformedRectanglesCount; +} + +const TransformedRectangle& RectangleList::getTransformedRectangle(int i) const { + return mTransformedRectangles[i]; +} + +void RectangleList::setEmpty() { + mTransformedRectanglesCount = 0; +} + +void RectangleList::set(const Rect& bounds, const Matrix4& transform) { + mTransformedRectanglesCount = 1; + mTransformedRectangles[0] = TransformedRectangle(bounds, transform); +} + +bool RectangleList::intersectWith(const Rect& bounds, + const Matrix4& transform) { + TransformedRectangle newRectangle(bounds, transform); + + // Try to find a rectangle with a compatible transformation + int index = 0; + for (; index < mTransformedRectanglesCount; index++) { + TransformedRectangle& tr(mTransformedRectangles[index]); + if (tr.canSimplyIntersectWith(newRectangle)) { + tr.intersectWith(newRectangle); + return true; + } + } + + // Add it to the list if there is room + if (index < kMaxTransformedRectangles) { + mTransformedRectangles[index] = newRectangle; + mTransformedRectanglesCount += 1; + return true; + } + + // This rectangle list is full + return false; +} + +Rect RectangleList::calculateBounds() const { + Rect bounds; + for (int index = 0; index < mTransformedRectanglesCount; index++) { + const TransformedRectangle& tr(mTransformedRectangles[index]); + if (index == 0) { + bounds = tr.transformedBounds(); + } else { + bounds.intersect(tr.transformedBounds()); + } + } + return bounds; +} + +static SkPath pathFromTransformedRectangle(const Rect& bounds, + const Matrix4& transform) { + SkPath rectPath; + SkPath rectPathTransformed; + rectPath.addRect(bounds.left, bounds.top, bounds.right, bounds.bottom); + SkMatrix skTransform; + transform.copyTo(skTransform); + rectPath.transform(skTransform, &rectPathTransformed); + return rectPathTransformed; +} + +SkRegion RectangleList::convertToRegion(const SkRegion& clip) const { + SkRegion rectangleListAsRegion; + for (int index = 0; index < mTransformedRectanglesCount; index++) { + const TransformedRectangle& tr(mTransformedRectangles[index]); + SkPath rectPathTransformed = pathFromTransformedRectangle( + tr.getBounds(), tr.getTransform()); + if (index == 0) { + rectangleListAsRegion.setPath(rectPathTransformed, clip); + } else { + SkRegion rectRegion; + rectRegion.setPath(rectPathTransformed, clip); + rectangleListAsRegion.op(rectRegion, SkRegion::kIntersect_Op); + } + } + return rectangleListAsRegion; +} + +/* + * ClipArea + */ + +ClipArea::ClipArea() + : mMode(kModeRectangle) { +} + +/* + * Interface + */ + +void ClipArea::setViewportDimensions(int width, int height) { + mViewportBounds.set(0, 0, width, height); + mClipRect = mViewportBounds; +} + +void ClipArea::setEmpty() { + mMode = kModeRectangle; + mClipRect.setEmpty(); + mClipRegion.setEmpty(); + mRectangleList.setEmpty(); +} + +void ClipArea::setClip(float left, float top, float right, float bottom) { + mMode = kModeRectangle; + mClipRect.set(left, top, right, bottom); + mClipRegion.setEmpty(); +} + +bool ClipArea::clipRectWithTransform(float left, float top, float right, + float bottom, const mat4* transform, SkRegion::Op op) { + Rect r(left, top, right, bottom); + return clipRectWithTransform(r, transform, op); +} + +bool ClipArea::clipRectWithTransform(const Rect& r, const mat4* transform, + SkRegion::Op op) { + switch (mMode) { + case kModeRectangle: + return rectangleModeClipRectWithTransform(r, transform, op); + case kModeRectangleList: + return rectangleListModeClipRectWithTransform(r, transform, op); + case kModeRegion: + return regionModeClipRectWithTransform(r, transform, op); + } + return false; +} + +bool ClipArea::clipRegion(const SkRegion& region, SkRegion::Op op) { + enterRegionMode(); + mClipRegion.op(region, op); + onClipRegionUpdated(); + return true; +} + +bool ClipArea::clipPathWithTransform(const SkPath& path, const mat4* transform, + SkRegion::Op op) { + SkMatrix skTransform; + transform->copyTo(skTransform); + SkPath transformed; + path.transform(skTransform, &transformed); + SkRegion region; + regionFromPath(transformed, region); + return clipRegion(region, op); +} + +/* + * Rectangle mode + */ + +void ClipArea::enterRectangleMode() { + // Entering rectangle mode discards any + // existing clipping information from the other modes. + // The only way this occurs is by a clip setting operation. + mMode = kModeRectangle; +} + +bool ClipArea::rectangleModeClipRectWithTransform(const Rect& r, + const mat4* transform, SkRegion::Op op) { + + // TODO: we should be able to handle kReplace_Op efficiently without + // going through RegionMode and later falling back into RectangleMode. + + if (op != SkRegion::kIntersect_Op) { + enterRegionMode(); + return regionModeClipRectWithTransform(r, transform, op); + } + + if (transform->rectToRect()) { + Rect transformed(r); + transform->mapRect(transformed); + bool hasIntersection = mClipRect.intersect(transformed); + if (!hasIntersection) { + mClipRect.setEmpty(); + } + return true; + } + + enterRectangleListMode(); + return rectangleListModeClipRectWithTransform(r, transform, op); +} + +bool ClipArea::rectangleModeClipRectWithTransform(float left, float top, + float right, float bottom, const mat4* transform, SkRegion::Op op) { + Rect r(left, top, right, bottom); + bool result = rectangleModeClipRectWithTransform(r, transform, op); + mClipRect = mRectangleList.calculateBounds(); + return result; +} + +/* + * RectangleList mode implementation + */ + +void ClipArea::enterRectangleListMode() { + // Is is only legal to enter rectangle list mode from + // rectangle mode, since rectangle list mode cannot represent + // all clip areas that can be represented by a region. + ALOG_ASSERT(mMode == kModeRectangle); + mMode = kModeRectangleList; + mRectangleList.set(mClipRect, Matrix4::identity()); +} + +bool ClipArea::rectangleListModeClipRectWithTransform(const Rect& r, + const mat4* transform, SkRegion::Op op) { + if (op != SkRegion::kIntersect_Op + || !mRectangleList.intersectWith(r, *transform)) { + enterRegionMode(); + return regionModeClipRectWithTransform(r, transform, op); + } + return true; +} + +bool ClipArea::rectangleListModeClipRectWithTransform(float left, float top, + float right, float bottom, const mat4* transform, SkRegion::Op op) { + Rect r(left, top, right, bottom); + return rectangleListModeClipRectWithTransform(r, transform, op); +} + +/* + * Region mode implementation + */ + +void ClipArea::enterRegionMode() { + Mode oldMode = mMode; + mMode = kModeRegion; + if (oldMode != kModeRegion) { + if (oldMode == kModeRectangle) { + mClipRegion.setRect(mClipRect.left, mClipRect.top, + mClipRect.right, mClipRect.bottom); + } else { + mClipRegion = mRectangleList.convertToRegion(createViewportRegion()); + onClipRegionUpdated(); + } + } +} + +bool ClipArea::regionModeClipRectWithTransform(const Rect& r, + const mat4* transform, SkRegion::Op op) { + SkPath transformedRect = pathFromTransformedRectangle(r, *transform); + SkRegion transformedRectRegion; + regionFromPath(transformedRect, transformedRectRegion); + mClipRegion.op(transformedRectRegion, op); + onClipRegionUpdated(); + return true; +} + +bool ClipArea::regionModeClipRectWithTransform(float left, float top, + float right, float bottom, const mat4* transform, SkRegion::Op op) { + return regionModeClipRectWithTransform(Rect(left, top, right, bottom), + transform, op); +} + +void ClipArea::onClipRegionUpdated() { + if (!mClipRegion.isEmpty()) { + mClipRect.set(mClipRegion.getBounds()); + + if (mClipRegion.isRect()) { + mClipRegion.setEmpty(); + enterRectangleMode(); + } + } else { + mClipRect.setEmpty(); + } +} + +} /* namespace uirenderer */ +} /* namespace android */ diff --git a/libs/hwui/ClipArea.h b/libs/hwui/ClipArea.h new file mode 100644 index 0000000..e284af0 --- /dev/null +++ b/libs/hwui/ClipArea.h @@ -0,0 +1,175 @@ +/* + * 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. + */ +#ifndef CLIPAREA_H +#define CLIPAREA_H + +#include <SkRegion.h> + +#include "Matrix.h" +#include "Rect.h" +#include "utils/Pair.h" + +namespace android { +namespace uirenderer { + +Rect transformAndCalculateBounds(const Rect& r, const Matrix4& transform); + +class TransformedRectangle { +public: + TransformedRectangle(); + TransformedRectangle(const Rect& bounds, const Matrix4& transform); + + bool canSimplyIntersectWith(const TransformedRectangle& other) const; + bool intersectWith(const TransformedRectangle& other); + + bool isEmpty() const; + + const Rect& getBounds() const { + return mBounds; + } + + Rect transformedBounds() const { + Rect transformedBounds(transformAndCalculateBounds(mBounds, mTransform)); + return transformedBounds; + } + + const Matrix4& getTransform() const { + return mTransform; + } + +private: + Rect mBounds; + Matrix4 mTransform; +}; + +class RectangleList { +public: + RectangleList(); + + bool isEmpty() const; + int getTransformedRectanglesCount() const; + const TransformedRectangle& getTransformedRectangle(int i) const; + + void setEmpty(); + void set(const Rect& bounds, const Matrix4& transform); + bool intersectWith(const Rect& bounds, const Matrix4& transform); + + SkRegion convertToRegion(const SkRegion& clip) const; + Rect calculateBounds() const; + +private: + enum { + kMaxTransformedRectangles = 5 + }; + + int mTransformedRectanglesCount; + TransformedRectangle mTransformedRectangles[kMaxTransformedRectangles]; +}; + +class ClipArea { +public: + ClipArea(); + + void setViewportDimensions(int width, int height); + + bool isEmpty() const { + return mClipRect.isEmpty(); + } + + void setEmpty(); + void setClip(float left, float top, float right, float bottom); + bool clipRectWithTransform(float left, float top, float right, float bottom, + const mat4* transform, SkRegion::Op op = SkRegion::kIntersect_Op); + bool clipRectWithTransform(const Rect& r, const mat4* transform, + SkRegion::Op op = SkRegion::kIntersect_Op); + bool clipRegion(const SkRegion& region, SkRegion::Op op = SkRegion::kIntersect_Op); + bool clipPathWithTransform(const SkPath& path, const mat4* transform, + SkRegion::Op op); + + const Rect& getClipRect() const { + return mClipRect; + } + + const SkRegion& getClipRegion() const { + return mClipRegion; + } + + const RectangleList& getRectangleList() const { + return mRectangleList; + } + + bool isRegion() const { + return kModeRegion == mMode; + } + + bool isSimple() const { + return mMode == kModeRectangle; + } + + bool isRectangleList() const { + return mMode == kModeRectangleList; + } + +private: + void enterRectangleMode(); + bool rectangleModeClipRectWithTransform(const Rect& r, const mat4* transform, SkRegion::Op op); + bool rectangleModeClipRectWithTransform(float left, float top, float right, + float bottom, const mat4* transform, SkRegion::Op op); + + void enterRectangleListMode(); + bool rectangleListModeClipRectWithTransform(float left, float top, + float right, float bottom, const mat4* transform, SkRegion::Op op); + bool rectangleListModeClipRectWithTransform(const Rect& r, + const mat4* transform, SkRegion::Op op); + + void enterRegionModeFromRectangleMode(); + void enterRegionModeFromRectangleListMode(); + void enterRegionMode(); + bool regionModeClipRectWithTransform(const Rect& r, const mat4* transform, + SkRegion::Op op); + bool regionModeClipRectWithTransform(float left, float top, float right, + float bottom, const mat4* transform, SkRegion::Op op); + + void ensureClipRegion(); + void onClipRegionUpdated(); + bool clipRegionOp(float left, float top, float right, float bottom, + SkRegion::Op op); + + SkRegion createViewportRegion() { + return SkRegion(mViewportBounds.toSkIRect()); + } + + void regionFromPath(const SkPath& path, SkRegion& pathAsRegion) { + pathAsRegion.setPath(path, createViewportRegion()); + } + + enum Mode { + kModeRectangle, + kModeRegion, + kModeRectangleList + }; + + Mode mMode; + Rect mViewportBounds; + Rect mClipRect; + SkRegion mClipRegion; + RectangleList mRectangleList; +}; + +} /* namespace uirenderer */ +} /* namespace android */ + +#endif /* CLIPAREA_H_ */ diff --git a/libs/hwui/DamageAccumulator.cpp b/libs/hwui/DamageAccumulator.cpp index 420e331..9bd3bdc 100644 --- a/libs/hwui/DamageAccumulator.cpp +++ b/libs/hwui/DamageAccumulator.cpp @@ -79,7 +79,7 @@ void DamageAccumulator::computeCurrentTransform(Matrix4* outMatrix) const { void DamageAccumulator::pushCommon() { if (!mHead->next) { DirtyStack* nextFrame = (DirtyStack*) mAllocator.alloc(sizeof(DirtyStack)); - nextFrame->next = 0; + nextFrame->next = nullptr; nextFrame->prev = mHead; mHead->next = nextFrame; } @@ -147,7 +147,7 @@ static DirtyStack* findParentRenderNode(DirtyStack* frame) { return frame; } } - return NULL; + return nullptr; } static DirtyStack* findProjectionReceiver(DirtyStack* frame) { @@ -160,7 +160,7 @@ static DirtyStack* findProjectionReceiver(DirtyStack* frame) { } } } - return NULL; + return nullptr; } static void applyTransforms(DirtyStack* frame, DirtyStack* end) { @@ -222,7 +222,7 @@ void DamageAccumulator::finish(SkRect* totalDirty) { LOG_ALWAYS_FATAL_IF(mHead->prev != mHead, "Cannot finish, mismatched push/pop calls! %p vs. %p", mHead->prev, mHead); // Root node never has a transform, so this is the fully mapped dirty rect *totalDirty = mHead->pendingDirty; - totalDirty->roundOut(); + totalDirty->roundOut(totalDirty); mHead->pendingDirty.setEmpty(); } diff --git a/libs/hwui/DeferredDisplayList.cpp b/libs/hwui/DeferredDisplayList.cpp index a998594..c78971a 100644 --- a/libs/hwui/DeferredDisplayList.cpp +++ b/libs/hwui/DeferredDisplayList.cpp @@ -52,7 +52,7 @@ namespace uirenderer { class Batch { public: - virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty, int index) = 0; + 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; } @@ -91,12 +91,10 @@ public: return false; } - virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty, int index) { + 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()); - status_t status = DrawGlInfo::kStatusDone; - DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance(); for (unsigned int i = 0; i < mOps.size(); i++) { DrawOp* op = mOps[i].op; const DeferredDisplayState* state = mOps[i].state; @@ -105,8 +103,7 @@ public: #if DEBUG_DISPLAY_LIST_OPS_AS_EVENTS renderer.eventMark(op->name()); #endif - logBuffer.writeCommand(0, op->name()); - status |= op->applyDraw(renderer, dirty); + op->applyDraw(renderer, dirty); #if DEBUG_MERGE_BEHAVIOR const Rect& bounds = state->mBounds; @@ -118,12 +115,11 @@ public: batchColor); #endif } - return status; } - virtual bool purelyDrawBatch() { return true; } + virtual bool purelyDrawBatch() override { return true; } - virtual bool coversBounds(const Rect& bounds) { + 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)); @@ -235,30 +231,11 @@ public: return false; } - /* Draw Modifiers compatibility check - * - * Shadows are ignored, as only text uses them, and in that case they are drawn - * per-DrawTextOp, before the unified text draw. Because of this, it's always safe to merge - * text UNLESS a later draw's shadow should overlays a previous draw's text. This is covered - * above with the intersection check. - * - * OverrideLayerAlpha is also ignored, as it's only used for drawing layers, which are never - * merged. - * - * These ignore cases prevent us from simply memcmp'ing the drawModifiers - */ - const DrawModifiers& lhsMod = lhs->mDrawModifiers; - const DrawModifiers& rhsMod = rhs->mDrawModifiers; - - // Draw filter testing expects bit fields to be clear if filter not set. - if (lhsMod.mHasDrawFilter != rhsMod.mHasDrawFilter) return false; - if (lhsMod.mPaintFilterClearBits != rhsMod.mPaintFilterClearBits) return false; - if (lhsMod.mPaintFilterSetBits != rhsMod.mPaintFilterSetBits) return false; - return true; } - virtual void add(DrawOp* op, const DeferredDisplayState* state, bool opaqueOverBounds) { + virtual void add(DrawOp* op, const DeferredDisplayState* state, + bool opaqueOverBounds) override { DrawBatch::add(op, state, opaqueOverBounds); const int newClipSideFlags = state->mClipSideFlags; @@ -269,33 +246,29 @@ public: if (newClipSideFlags & kClipSide_Bottom) mClipRect.bottom = state->mClip.bottom; } - virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty, int index) { + 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) { - return DrawBatch::replay(renderer, dirty, -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 : NULL); + renderer.setupMergedMultiDraw(mClipSideFlags ? &mClipRect : nullptr); DrawOp* op = mOps[0].op; - DisplayListLogBuffer& buffer = DisplayListLogBuffer::getInstance(); - buffer.writeCommand(0, "multiDraw"); - buffer.writeCommand(1, op->name()); - #if DEBUG_DISPLAY_LIST_OPS_AS_EVENTS renderer.eventMark("multiDraw"); renderer.eventMark(op->name()); #endif - status_t status = op->multiDraw(renderer, dirty, mOps, mBounds); + op->multiDraw(renderer, dirty, mOps, mBounds); #if DEBUG_MERGE_BEHAVIOR renderer.drawScreenSpaceColorRect(mBounds.left, mBounds.top, mBounds.right, mBounds.bottom, DEBUG_COLOR_MERGEDBATCH); #endif - return status; } private: @@ -313,7 +286,7 @@ public: // creates a single operation batch StateOpBatch(const StateOp* op, const DeferredDisplayState* state) : mOp(op), mState(state) {} - virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty, int index) { + virtual void replay(OpenGLRenderer& renderer, Rect& dirty, int index) override { DEFER_LOGD("replaying state op batch %p", this); renderer.restoreDisplayState(*mState); @@ -322,7 +295,6 @@ public: // renderer.restoreToCount directly int saveCount = -1; mOp->applyState(renderer, saveCount); - return DrawGlInfo::kStatusDone; } private: @@ -333,19 +305,17 @@ private: class RestoreToCountBatch : public Batch { public: RestoreToCountBatch(const StateOp* op, const DeferredDisplayState* state, int restoreCount) : - mOp(op), mState(state), mRestoreCount(restoreCount) {} + mState(state), mRestoreCount(restoreCount) {} - virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty, int index) { + 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); - return DrawGlInfo::kStatusDone; } private: // we use the state storage for the RestoreToCountOp, but don't replay the op itself - const StateOp* mOp; const DeferredDisplayState* mState; /* @@ -359,9 +329,8 @@ private: #if DEBUG_MERGE_BEHAVIOR class BarrierDebugBatch : public Batch { - virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty, int index) { + virtual void replay(OpenGLRenderer& renderer, Rect& dirty, int index) { renderer.drawScreenSpaceColorRect(0, 0, 10000, 10000, DEBUG_COLOR_BARRIER); - return DrawGlInfo::kStatusDrew; } }; #endif @@ -372,7 +341,7 @@ class BarrierDebugBatch : public Batch { void DeferredDisplayList::resetBatchingState() { for (int i = 0; i < kOpBatch_Count; i++) { - mBatchLookup[i] = NULL; + mBatchLookup[i] = nullptr; mMergingBatches[i].clear(); } #if DEBUG_MERGE_BEHAVIOR @@ -542,7 +511,7 @@ void DeferredDisplayList::addDrawOp(OpenGLRenderer& renderer, DrawOp* op) { } // find the latest batch of the new op's type, and try to merge the new op into it - DrawBatch* targetBatch = NULL; + DrawBatch* targetBatch = nullptr; // insertion point of a new batch, will hopefully be immediately after similar batch // (eventually, should be similar shader) @@ -565,7 +534,7 @@ void DeferredDisplayList::addDrawOp(OpenGLRenderer& renderer, DrawOp* op) { // Try to merge with any existing batch with same mergeId. if (mMergingBatches[deferInfo.batchId].get(deferInfo.mergeId, targetBatch)) { if (!((MergingDrawBatch*) targetBatch)->canMergeWith(op, state)) { - targetBatch = NULL; + targetBatch = nullptr; } } } else { @@ -591,7 +560,7 @@ void DeferredDisplayList::addDrawOp(OpenGLRenderer& renderer, DrawOp* op) { // 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 = NULL; + targetBatch = nullptr; #if DEBUG_DEFER DEFER_LOGD("op couldn't join batch %p, was intersected by batch %d", targetBatch, i); @@ -648,26 +617,22 @@ void DeferredDisplayList::storeRestoreToCountBarrier(OpenGLRenderer& renderer, S // Replay / flush ///////////////////////////////////////////////////////////////////////////////// -static status_t replayBatchList(const Vector<Batch*>& batchList, +static void replayBatchList(const Vector<Batch*>& batchList, OpenGLRenderer& renderer, Rect& dirty) { - status_t status = DrawGlInfo::kStatusDone; for (unsigned int i = 0; i < batchList.size(); i++) { if (batchList[i]) { - status |= batchList[i]->replay(renderer, dirty, i); + batchList[i]->replay(renderer, dirty, i); } } DEFER_LOGD("--flushed, drew %d batches", batchList.size()); - return status; } -status_t DeferredDisplayList::flush(OpenGLRenderer& renderer, Rect& dirty) { +void DeferredDisplayList::flush(OpenGLRenderer& renderer, Rect& dirty) { ATRACE_NAME("flush drawing commands"); Caches::getInstance().fontRenderer->endPrecaching(); - status_t status = DrawGlInfo::kStatusDone; - - if (isEmpty()) return status; // nothing to flush + if (isEmpty()) return; // nothing to flush renderer.restoreToCount(1); DEFER_LOGD("--flushing"); @@ -686,23 +651,21 @@ status_t DeferredDisplayList::flush(OpenGLRenderer& renderer, Rect& dirty) { } // 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 - status |= replayBatchList(mBatches, renderer, dirty); + replayBatchList(mBatches, renderer, dirty); renderer.restoreToCount(1); renderer.setDrawModifiers(restoreDrawModifiers); DEFER_LOGD("--flush complete, returning %x", status); clear(); - return status; } 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()) { - DrawBatch* b = (DrawBatch*) mBatches[i]; delete mBatches[i]; - mBatches.replaceAt(NULL, i); + mBatches.replaceAt(nullptr, i); } } mEarliestUnclearedIndex = maxIndex + 1; diff --git a/libs/hwui/DeferredDisplayList.h b/libs/hwui/DeferredDisplayList.h index 8a015b2..f535afb 100644 --- a/libs/hwui/DeferredDisplayList.h +++ b/libs/hwui/DeferredDisplayList.h @@ -69,7 +69,7 @@ public: class OpStatePair { public: OpStatePair() - : op(NULL), state(NULL) {} + : op(nullptr), state(nullptr) {} OpStatePair(DrawOp* newOp, const DeferredDisplayState* newState) : op(newOp), state(newState) {} OpStatePair(const OpStatePair& other) @@ -79,7 +79,7 @@ public: }; class DeferredDisplayList { - friend class DeferStateStruct; // used to give access to allocator + friend struct DeferStateStruct; // used to give access to allocator public: DeferredDisplayList(const Rect& bounds, bool avoidOverdraw = true) : mBounds(bounds), mAvoidOverdraw(avoidOverdraw) { @@ -106,7 +106,7 @@ public: * 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 */ - status_t flush(OpenGLRenderer& renderer, Rect& dirty); + void flush(OpenGLRenderer& renderer, Rect& dirty); void addClip(OpenGLRenderer& renderer, ClipOp* op); void addSaveLayer(OpenGLRenderer& renderer, SaveLayerOp* op, int newSaveCount); @@ -127,7 +127,7 @@ private: } void tryRecycleState(DeferredDisplayState* state) { - mAllocator.rewindIfLastAlloc(state, sizeof(DeferredDisplayState)); + mAllocator.rewindIfLastAlloc(state); } /** diff --git a/libs/hwui/DeferredLayerUpdater.cpp b/libs/hwui/DeferredLayerUpdater.cpp index d02455c..6b8c780 100644 --- a/libs/hwui/DeferredLayerUpdater.cpp +++ b/libs/hwui/DeferredLayerUpdater.cpp @@ -25,8 +25,8 @@ namespace android { namespace uirenderer { DeferredLayerUpdater::DeferredLayerUpdater(renderthread::RenderThread& thread, Layer* layer) - : mSurfaceTexture(0) - , mTransform(0) + : mSurfaceTexture(nullptr) + , mTransform(nullptr) , mNeedsGLContextAttach(false) , mUpdateTexImage(false) , mLayer(layer) @@ -42,14 +42,14 @@ DeferredLayerUpdater::DeferredLayerUpdater(renderthread::RenderThread& thread, L DeferredLayerUpdater::~DeferredLayerUpdater() { SkSafeUnref(mColorFilter); - setTransform(0); + setTransform(nullptr); mLayer->postDecStrong(); - mLayer = 0; + mLayer = nullptr; } void DeferredLayerUpdater::setPaint(const SkPaint* paint) { OpenGLRenderer::getAlphaAndModeDirect(paint, &mAlpha, &mMode); - SkColorFilter* colorFilter = (paint) ? paint->getColorFilter() : NULL; + SkColorFilter* colorFilter = (paint) ? paint->getColorFilter() : nullptr; SkRefCnt_SafeAssign(mColorFilter, colorFilter); } @@ -62,7 +62,7 @@ bool DeferredLayerUpdater::apply() { if (mSurfaceTexture.get()) { if (mNeedsGLContextAttach) { mNeedsGLContextAttach = false; - mSurfaceTexture->attachToContext(mLayer->getTexture()); + mSurfaceTexture->attachToContext(mLayer->getTextureId()); } if (mUpdateTexImage) { mUpdateTexImage = false; @@ -70,7 +70,7 @@ bool DeferredLayerUpdater::apply() { } if (mTransform) { mLayer->getTransform().load(*mTransform); - setTransform(0); + setTransform(nullptr); } } return success; @@ -95,10 +95,10 @@ void DeferredLayerUpdater::doUpdateTexImage() { bool forceFilter = false; sp<GraphicBuffer> buffer = mSurfaceTexture->getCurrentBuffer(); - if (buffer != NULL) { + if (buffer != nullptr) { // force filtration if buffer size != layer size - forceFilter = mWidth != buffer->getWidth() - || mHeight != buffer->getHeight(); + forceFilter = mWidth != static_cast<int>(buffer->getWidth()) + || mHeight != static_cast<int>(buffer->getHeight()); } #if DEBUG_RENDERER @@ -109,6 +109,9 @@ void DeferredLayerUpdater::doUpdateTexImage() { mSurfaceTexture->getTransformMatrix(transform); GLenum renderTarget = mSurfaceTexture->getCurrentTextureTarget(); + 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); } @@ -122,7 +125,7 @@ void DeferredLayerUpdater::detachSurfaceTexture() { // TODO: Elevate to fatal exception ALOGE("Failed to detach SurfaceTexture from context %d", err); } - mSurfaceTexture = 0; + mSurfaceTexture = nullptr; mLayer->clearTexture(); } } diff --git a/libs/hwui/DeferredLayerUpdater.h b/libs/hwui/DeferredLayerUpdater.h index 84411ed..82f2741 100644 --- a/libs/hwui/DeferredLayerUpdater.h +++ b/libs/hwui/DeferredLayerUpdater.h @@ -24,7 +24,6 @@ #include "Layer.h" #include "Rect.h" -#include "RenderNode.h" #include "renderthread/RenderThread.h" namespace android { @@ -39,7 +38,7 @@ public: ANDROID_API DeferredLayerUpdater(renderthread::RenderThread& thread, Layer* layer); ANDROID_API ~DeferredLayerUpdater(); - ANDROID_API bool setSize(uint32_t width, uint32_t height) { + ANDROID_API bool setSize(int width, int height) { if (mWidth != width || mHeight != height) { mWidth = width; mHeight = height; @@ -60,6 +59,10 @@ public: if (texture.get() != mSurfaceTexture.get()) { mNeedsGLContextAttach = needsAttach; mSurfaceTexture = texture; + + GLenum target = texture->getCurrentTextureTarget(); + LOG_ALWAYS_FATAL_IF(target != GL_TEXTURE_2D && target != GL_TEXTURE_EXTERNAL_OES, + "set unsupported GLConsumer with target %x", target); } } @@ -69,7 +72,7 @@ public: ANDROID_API void setTransform(const SkMatrix* matrix) { delete mTransform; - mTransform = matrix ? new SkMatrix(*matrix) : 0; + mTransform = matrix ? new SkMatrix(*matrix) : nullptr; } ANDROID_API void setPaint(const SkPaint* paint); @@ -84,8 +87,8 @@ public: private: // Generic properties - uint32_t mWidth; - uint32_t mHeight; + int mWidth; + int mHeight; bool mBlend; SkColorFilter* mColorFilter; int mAlpha; diff --git a/libs/hwui/DisplayList.cpp b/libs/hwui/DisplayList.cpp index 8953166..4540bec 100644 --- a/libs/hwui/DisplayList.cpp +++ b/libs/hwui/DisplayList.cpp @@ -24,7 +24,6 @@ #include "Debug.h" #include "DisplayList.h" #include "DisplayListOp.h" -#include "DisplayListLogBuffer.h" namespace android { namespace uirenderer { @@ -46,41 +45,25 @@ void DisplayListData::cleanupResources() { resourceCache.decrementRefcountLocked(bitmapResources.itemAt(i)); } - for (size_t i = 0; i < ownedBitmapResources.size(); i++) { - const SkBitmap* bitmap = ownedBitmapResources.itemAt(i); - resourceCache.decrementRefcountLocked(bitmap); - resourceCache.destructorLocked(bitmap); - } - for (size_t i = 0; i < patchResources.size(); i++) { resourceCache.decrementRefcountLocked(patchResources.itemAt(i)); } - for (size_t i = 0; i < sourcePaths.size(); i++) { - resourceCache.decrementRefcountLocked(sourcePaths.itemAt(i)); - } - resourceCache.unlock(); - for (size_t i = 0; i < paints.size(); i++) { - delete paints.itemAt(i); - } - - for (size_t i = 0; i < regions.size(); i++) { - delete regions.itemAt(i); - } - - for (size_t i = 0; i < paths.size(); i++) { - delete paths.itemAt(i); + for (size_t i = 0; i < pathResources.size(); i++) { + const SkPath* path = pathResources.itemAt(i); + if (path->unique() && Caches::hasInstance()) { + Caches::getInstance().pathCache.removeDeferred(path); + } + delete path; } bitmapResources.clear(); - ownedBitmapResources.clear(); patchResources.clear(); - sourcePaths.clear(); + pathResources.clear(); paints.clear(); regions.clear(); - paths.clear(); } size_t DisplayListData::addChild(DrawRenderNodeOp* op) { diff --git a/libs/hwui/DisplayList.h b/libs/hwui/DisplayList.h index 7a43a2a..7fbda1f 100644 --- a/libs/hwui/DisplayList.h +++ b/libs/hwui/DisplayList.h @@ -38,8 +38,9 @@ #include <androidfw/ResourceTypes.h> #include "Debug.h" -#include "Matrix.h" +#include "CanvasProperty.h" #include "DeferredDisplayList.h" +#include "Matrix.h" #include "RenderProperties.h" class SkBitmap; @@ -52,7 +53,7 @@ namespace uirenderer { class DeferredDisplayList; class DisplayListOp; -class DisplayListRenderer; +class DisplayListCanvas; class OpenGLRenderer; class Rect; class Layer; @@ -66,7 +67,7 @@ class DrawRenderNodeOp; /** * Holds data used in the playback a tree of DisplayLists. */ -class PlaybackStateStruct { +struct PlaybackStateStruct { protected: PlaybackStateStruct(OpenGLRenderer& renderer, int replayFlags, LinearAllocator* allocator) : mRenderer(renderer) @@ -87,8 +88,7 @@ public: } }; -class DeferStateStruct : public PlaybackStateStruct { -public: +struct DeferStateStruct : public PlaybackStateStruct { DeferStateStruct(DeferredDisplayList& deferredList, OpenGLRenderer& renderer, int replayFlags) : PlaybackStateStruct(renderer, replayFlags, &(deferredList.mAllocator)), mDeferredList(deferredList) {} @@ -96,14 +96,12 @@ public: DeferredDisplayList& mDeferredList; }; -class ReplayStateStruct : public PlaybackStateStruct { -public: +struct ReplayStateStruct : public PlaybackStateStruct { ReplayStateStruct(OpenGLRenderer& renderer, Rect& dirty, int replayFlags) : PlaybackStateStruct(renderer, replayFlags, &mReplayAllocator), - mDirty(dirty), mDrawGlStatus(DrawGlInfo::kStatusDone) {} + mDirty(dirty) {} Rect& mDirty; - status_t mDrawGlStatus; LinearAllocator mReplayAllocator; }; @@ -111,7 +109,7 @@ public: * Data structure that holds the list of commands used in display list stream */ class DisplayListData { - friend class DisplayListRenderer; + friend class DisplayListCanvas; public: struct Chunk { // range of included ops in DLD::displayListOps @@ -136,13 +134,11 @@ public: int projectionReceiveIndex; Vector<const SkBitmap*> bitmapResources; - Vector<const SkBitmap*> ownedBitmapResources; + Vector<const SkPath*> pathResources; Vector<const Res_png_9patch*> patchResources; - Vector<const SkPaint*> paints; - Vector<const SkPath*> paths; - SortedVector<const SkPath*> sourcePaths; - Vector<const SkRegion*> regions; + std::vector<std::unique_ptr<const SkPaint>> paints; + std::vector<std::unique_ptr<const SkRegion>> regions; Vector<Functor*> functors; const Vector<Chunk>& getChunks() const { diff --git a/libs/hwui/DisplayListCanvas.cpp b/libs/hwui/DisplayListCanvas.cpp new file mode 100644 index 0000000..6b86030 --- /dev/null +++ b/libs/hwui/DisplayListCanvas.cpp @@ -0,0 +1,570 @@ +/* + * 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 "ResourceCache.h" +#include "DeferredDisplayList.h" +#include "DeferredLayerUpdater.h" +#include "DisplayListOp.h" +#include "RenderNode.h" +#include "utils/PaintUtils.h" + +#include <SkCamera.h> +#include <SkCanvas.h> + +#include <private/hwui/DrawGlInfo.h> + +namespace android { +namespace uirenderer { + +DisplayListCanvas::DisplayListCanvas() + : mState(*this) + , mResourceCache(ResourceCache::getInstance()) + , mDisplayListData(nullptr) + , mTranslateX(0.0f) + , mTranslateY(0.0f) + , mHasDeferredTranslate(false) + , mDeferredBarrierType(kBarrier_None) + , mHighContrastText(false) + , mRestoreSaveCount(-1) { +} + +DisplayListCanvas::~DisplayListCanvas() { + LOG_ALWAYS_FATAL_IF(mDisplayListData, + "Destroyed a DisplayListCanvas during a record!"); +} + +/////////////////////////////////////////////////////////////////////////////// +// Operations +/////////////////////////////////////////////////////////////////////////////// + +DisplayListData* DisplayListCanvas::finishRecording() { + mPaintMap.clear(); + mRegionMap.clear(); + mPathMap.clear(); + DisplayListData* data = mDisplayListData; + mDisplayListData = nullptr; + mSkiaCanvasProxy.reset(nullptr); + return data; +} + +void DisplayListCanvas::prepareDirty(float left, float top, + float right, float bottom) { + + LOG_ALWAYS_FATAL_IF(mDisplayListData, + "prepareDirty called a second time during a recording!"); + mDisplayListData = new DisplayListData(); + + mState.initializeSaveStack(0, 0, mState.getWidth(), mState.getHeight(), Vector3()); + + mDeferredBarrierType = kBarrier_InOrder; + mState.setDirtyClip(false); + mRestoreSaveCount = -1; +} + +bool DisplayListCanvas::finish() { + flushRestoreToCount(); + flushTranslate(); + return false; +} + +void DisplayListCanvas::interrupt() { +} + +void DisplayListCanvas::resume() { +} + +void DisplayListCanvas::callDrawGLFunction(Functor *functor, Rect& dirty) { + // Ignore dirty during recording, it matters only when we replay + addDrawOp(new (alloc()) DrawFunctorOp(functor)); + mDisplayListData->functors.add(functor); +} + +SkCanvas* DisplayListCanvas::asSkCanvas() { + LOG_ALWAYS_FATAL_IF(!mDisplayListData, + "attempting to get an SkCanvas when we are not recording!"); + if (!mSkiaCanvasProxy) { + mSkiaCanvasProxy.reset(new SkiaCanvasProxy(this)); + } + return mSkiaCanvasProxy.get(); +} + +int DisplayListCanvas::save(SkCanvas::SaveFlags 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, SkCanvas::SaveFlags flags) { + // force matrix/clip isolation for layer + flags |= SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag; + + 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) { + mHasDeferredTranslate = true; + mTranslateX += dx; + mTranslateY += dy; + flushRestoreToCount(); + mState.translate(dx, dy, 0.0f); +} + +void DisplayListCanvas::rotate(float degrees) { + addStateOp(new (alloc()) RotateOp(degrees)); + mState.rotate(degrees); +} + +void DisplayListCanvas::scale(float sx, float sy) { + 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, Rect& dirty, int32_t flags) { + LOG_ALWAYS_FATAL_IF(!renderNode, "missing rendernode"); + + // dirty is an out parameter and should not be recorded, + // it matters only when replaying the display list + DrawRenderNodeOp* op = new (alloc()) DrawRenderNodeOp(renderNode, flags, *mState.currentTransform()); + addRenderNodeOp(op); +} + +void DisplayListCanvas::drawLayer(DeferredLayerUpdater* layerHandle, float x, float y) { + // We ref the DeferredLayerUpdater due to its thread-safe ref-counting + // semantics. + mDisplayListData->ref(layerHandle); + addDrawOp(new (alloc()) DrawLayerOp(layerHandle->backingLayer(), x, y)); +} + +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(SkCanvas::kMatrix_SaveFlag); + 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))) { + // 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(SkCanvas::kMatrix_SaveFlag); + 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(SkCanvas::kMatrix_SaveFlag); + 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(SkCanvas::kMatrix_SaveFlag); + 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::drawPatch(const SkBitmap* bitmap, const Res_png_9patch* patch, + float left, float top, float right, float bottom, const SkPaint* paint) { + bitmap = refBitmap(bitmap); + patch = refPatch(patch); + paint = refPaint(paint); + + addDrawOp(new (alloc()) DrawPatchOp(bitmap, patch, left, top, right, bottom, 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) { + mDisplayListData->ref(left); + mDisplayListData->ref(top); + mDisplayListData->ref(right); + mDisplayListData->ref(bottom); + mDisplayListData->ref(rx); + mDisplayListData->ref(ry); + mDisplayListData->ref(paint); + 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) { + mDisplayListData->ref(x); + mDisplayListData->ref(y); + mDisplayListData->ref(radius); + mDisplayListData->ref(paint); + 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::drawTextOnPath(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(refText((const char*) glyphs, bytesCount), + bytesCount, count, refPath(&path), + hOffset, vOffset, refPaint(&paint)); + addDrawOp(op); +} + +void DisplayListCanvas::drawPosText(const uint16_t* text, const float* positions, + int count, int posCount, const SkPaint& paint) { + if (!text || count <= 0) return; + + int bytesCount = 2 * count; + positions = refBuffer<float>(positions, count * 2); + + DrawOp* op = new (alloc()) DrawPosTextOp(refText((const char*) text, bytesCount), + bytesCount, count, positions, refPaint(&paint)); + addDrawOp(op); +} + +static void simplifyPaint(int color, SkPaint* paint) { + paint->setColor(color); + paint->setShader(nullptr); + paint->setColorFilter(nullptr); + paint->setLooper(nullptr); + paint->setStrokeWidth(4 + 0.04 * paint->getTextSize()); + paint->setStrokeJoin(SkPaint::kRound_Join); + paint->setLooper(nullptr); +} + +void DisplayListCanvas::drawText(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; + const char* text = refText((const char*) glyphs, bytesCount); + positions = refBuffer<float>(positions, count * 2); + Rect bounds(boundsLeft, boundsTop, boundsRight, boundsBottom); + + if (CC_UNLIKELY(mHighContrastText)) { + // high contrast draw path + int color = paint.getColor(); + int channelSum = SkColorGetR(color) + SkColorGetG(color) + SkColorGetB(color); + bool darken = channelSum < (128 * 3); + + // outline + SkPaint* outlinePaint = copyPaint(&paint); + simplifyPaint(darken ? SK_ColorWHITE : SK_ColorBLACK, outlinePaint); + outlinePaint->setStyle(SkPaint::kStrokeAndFill_Style); + addDrawOp(new (alloc()) DrawTextOp(text, bytesCount, count, + x, y, positions, outlinePaint, totalAdvance, bounds)); // bounds? + + // inner + SkPaint* innerPaint = copyPaint(&paint); + simplifyPaint(darken ? SK_ColorBLACK : SK_ColorWHITE, innerPaint); + innerPaint->setStyle(SkPaint::kFill_Style); + addDrawOp(new (alloc()) DrawTextOp(text, bytesCount, count, + x, y, positions, innerPaint, totalAdvance, bounds)); + } else { + // standard draw path + DrawOp* op = new (alloc()) DrawTextOp(text, bytesCount, count, + x, y, positions, refPaint(&paint), totalAdvance, bounds); + addDrawOp(op); + } +} + +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 = mDisplayListData->displayListOps.add(op); + if (mDeferredBarrierType != kBarrier_None) { + // op is first in new chunk + mDisplayListData->chunks.push(); + DisplayListData::Chunk& newChunk = mDisplayListData->chunks.editTop(); + newChunk.beginOpIndex = insertIndex; + newChunk.endOpIndex = insertIndex + 1; + newChunk.reorderChildren = (mDeferredBarrierType == kBarrier_OutOfOrder); + + int nextChildIndex = mDisplayListData->children().size(); + newChunk.beginChildIndex = newChunk.endChildIndex = nextChildIndex; + mDeferredBarrierType = kBarrier_None; + } else { + // standard case - append to existing chunk + mDisplayListData->chunks.editTop().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); + } + + mDisplayListData->hasDrawOps = true; + return flushAndAddOp(op); +} + +size_t DisplayListCanvas::addRenderNodeOp(DrawRenderNodeOp* op) { + int opIndex = addDrawOp(op); + int childIndex = mDisplayListData->addChild(op); + + // update the chunk's child indices + DisplayListData::Chunk& chunk = mDisplayListData->chunks.editTop(); + chunk.endChildIndex = childIndex + 1; + + if (op->renderNode()->stagingProperties().isProjectionReceiver()) { + // use staging property, since recording on UI thread + mDisplayListData->projectionReceiveIndex = opIndex; + } + return opIndex; +} + +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/DisplayListCanvas.h b/libs/hwui/DisplayListCanvas.h new file mode 100644 index 0000000..2b0b6b2 --- /dev/null +++ b/libs/hwui/DisplayListCanvas.h @@ -0,0 +1,388 @@ +/* + * 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 <SkDrawFilter.h> +#include <SkMatrix.h> +#include <SkPaint.h> +#include <SkPath.h> +#include <SkRegion.h> +#include <SkTLazy.h> +#include <cutils/compiler.h> + +#include "Canvas.h" +#include "CanvasState.h" +#include "DisplayList.h" +#include "SkiaCanvasProxy.h" +#include "RenderNode.h" +#include "ResourceCache.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 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(); + virtual ~DisplayListCanvas(); + + void insertReorderBarrier(bool enableReorder); + + DisplayListData* finishRecording(); + +// ---------------------------------------------------------------------------- +// HWUI Frame state operations +// ---------------------------------------------------------------------------- + + void prepareDirty(float left, float top, float right, float bottom); + void prepare() { prepareDirty(0.0f, 0.0f, width(), height()); } + bool finish(); + void interrupt(); + void resume(); + +// ---------------------------------------------------------------------------- +// HWUI Canvas state operations +// ---------------------------------------------------------------------------- + + void setViewport(int width, int height) { mState.setViewport(width, height); } + + const Rect& getRenderTargetClipBounds() const { return mState.getRenderTargetClipBounds(); } + + bool isCurrentTransformSimple() { + return mState.currentTransform()->isSimple(); + } + +// ---------------------------------------------------------------------------- +// HWUI Canvas draw operations +// ---------------------------------------------------------------------------- + + // Bitmap-based + void drawBitmap(const SkBitmap* bitmap, const SkPaint* paint); + // TODO: move drawPatch() to Canvas.h + void drawPatch(const SkBitmap* bitmap, const Res_png_9patch* patch, + float left, float top, float right, float bottom, const SkPaint* paint); + + // Shapes + void drawRects(const float* rects, int count, const SkPaint* paint); + void drawRoundRect(CanvasPropertyPrimitive* left, CanvasPropertyPrimitive* top, + CanvasPropertyPrimitive* right, CanvasPropertyPrimitive* bottom, + CanvasPropertyPrimitive* rx, CanvasPropertyPrimitive* ry, + CanvasPropertyPaint* paint); + void drawCircle(CanvasPropertyPrimitive* x, CanvasPropertyPrimitive* y, + CanvasPropertyPrimitive* radius, CanvasPropertyPaint* paint); + + +// ---------------------------------------------------------------------------- +// HWUI Canvas draw operations - special +// ---------------------------------------------------------------------------- + void drawLayer(DeferredLayerUpdater* layerHandle, float x, float y); + void drawRenderNode(RenderNode* renderNode, Rect& dirty, int32_t replayFlags); + + // TODO: rename for consistency + void callDrawGLFunction(Functor* functor, Rect& dirty); + + void setHighContrastText(bool highContrastText) { + mHighContrastText = highContrastText; + } + +// ---------------------------------------------------------------------------- +// 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(); } + +// ---------------------------------------------------------------------------- +// android/graphics/Canvas state operations +// ---------------------------------------------------------------------------- + // Save (layer) + virtual int getSaveCount() const override { return mState.getSaveCount(); } + virtual int save(SkCanvas::SaveFlags 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, + SkCanvas::SaveFlags flags) override; + virtual int saveLayerAlpha(float left, float top, float right, float bottom, + int alpha, SkCanvas::SaveFlags 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 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; + + // Text + virtual void drawText(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 drawPosText(const uint16_t* text, const float* positions, int count, + int posCount, const SkPaint& paint) override; + virtual void drawTextOnPath(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 flushRestoreToCount(); + void flushTranslate(); + void flushReorderBarrier(); + + LinearAllocator& alloc() { return mDisplayListData->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); + + + template<class T> + inline const T* refBuffer(const T* srcBuffer, int32_t count) { + if (!srcBuffer) return nullptr; + + T* dstBuffer = (T*) mDisplayListData->allocator.alloc(count * sizeof(T)); + memcpy(dstBuffer, srcBuffer, count * sizeof(T)); + return dstBuffer; + } + + inline char* refText(const char* text, size_t byteLength) { + return (char*) refBuffer<uint8_t>((uint8_t*)text, byteLength); + } + + 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); + mDisplayListData->pathResources.add(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); + mDisplayListData->paints.push_back(std::move(copy)); + + // replaceValueFor() performs an add if the entry doesn't exist + mPaintMap.replaceValueFor(key, cachedPaint); + } + + return cachedPaint; + } + + inline SkPaint* copyPaint(const SkPaint* paint) { + if (!paint) return nullptr; + + SkPaint* returnPaint = new SkPaint(*paint); + std::unique_ptr<const SkPaint> copy(returnPaint); + mDisplayListData->paints.push_back(std::move(copy)); + + return returnPaint; + } + + 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(); + mDisplayListData->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. + const SkBitmap* cachedBitmap = mResourceCache.insert(bitmap); + mDisplayListData->bitmapResources.add(cachedBitmap); + return cachedBitmap; + } + + inline const Res_png_9patch* refPatch(const Res_png_9patch* patch) { + mDisplayListData->patchResources.add(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; + DisplayListData* mDisplayListData; + + 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/DisplayListLogBuffer.cpp b/libs/hwui/DisplayListLogBuffer.cpp deleted file mode 100644 index 45aacca..0000000 --- a/libs/hwui/DisplayListLogBuffer.cpp +++ /dev/null @@ -1,112 +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 "DisplayListLogBuffer.h" - -// BUFFER_SIZE size must be one more than a multiple of COMMAND_SIZE to ensure -// that mStart always points at the next command, not just the next item -#define NUM_COMMANDS 50 -#define BUFFER_SIZE ((NUM_COMMANDS) + 1) - -/** - * DisplayListLogBuffer is a utility class which logs the most recent display - * list operations in a circular buffer. The log is process-wide, because we - * only care about the most recent operations, not the operations on a per-window - * basis for a given activity. The purpose of the log is to provide more debugging - * information in a bug report, by telling us not just where a process hung (which - * generally is just reported as a stack trace at the Java level) or crashed, but - * also what happened immediately before that hang or crash. This may help track down - * problems in the native rendering code or driver interaction related to the display - * list operations that led up to the hang or crash. - * - * The log is implemented as a circular buffer for both space and performance - * reasons - we only care about the last several operations to give us context - * leading up to the problem, and we don't want to constantly copy data around or do - * additional mallocs to keep the most recent operations logged. Only numbers are - * logged to make the operation fast. If and when the log is output, we process this - * data into meaningful strings. - * - * There is an assumption about the format of the command (currently 2 ints: the - * opcode and the nesting level). If the type of information logged changes (for example, - * we may want to save a timestamp), then the size of the buffer and the way the - * information is recorded in writeCommand() should change to suit. - */ - -namespace android { - -#ifdef USE_OPENGL_RENDERER -using namespace uirenderer; -ANDROID_SINGLETON_STATIC_INSTANCE(DisplayListLogBuffer); -#endif - -namespace uirenderer { - - -DisplayListLogBuffer::DisplayListLogBuffer() { - mBufferFirst = (OpLog*) malloc(BUFFER_SIZE * sizeof(OpLog)); - mStart = mBufferFirst; - mBufferLast = mBufferFirst + BUFFER_SIZE - 1; - mEnd = mStart; -} - -DisplayListLogBuffer::~DisplayListLogBuffer() { - free(mBufferFirst); -} - -/** - * Called from DisplayListRenderer to output the current buffer into the - * specified FILE. This only happens in a dumpsys/bugreport operation. - */ -void DisplayListLogBuffer::outputCommands(FILE *file) -{ - OpLog* tmpBufferPtr = mStart; - while (true) { - if (tmpBufferPtr == mEnd) { - break; - } - - fprintf(file, "%*s%s\n", 2 * tmpBufferPtr->level, "", tmpBufferPtr->label); - - OpLog* nextOp = tmpBufferPtr++; - if (tmpBufferPtr > mBufferLast) { - tmpBufferPtr = mBufferFirst; - } - } -} - -/** - * Store the given level and label in the buffer and increment/wrap the mEnd - * and mStart values as appropriate. Label should point to static memory. - */ -void DisplayListLogBuffer::writeCommand(int level, const char* label) { - mEnd->level = level; - mEnd->label = label; - - if (mEnd == mBufferLast) { - mEnd = mBufferFirst; - } else { - mEnd++; - } - if (mEnd == mStart) { - mStart++; - if (mStart > mBufferLast) { - mStart = mBufferFirst; - } - } -} - -}; // namespace uirenderer -}; // namespace android diff --git a/libs/hwui/DisplayListLogBuffer.h b/libs/hwui/DisplayListLogBuffer.h deleted file mode 100644 index c884789..0000000 --- a/libs/hwui/DisplayListLogBuffer.h +++ /dev/null @@ -1,57 +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_DISPLAY_LIST_LOG_BUFFER_H -#define ANDROID_HWUI_DISPLAY_LIST_LOG_BUFFER_H - -#include <utils/Singleton.h> - -#include <stdio.h> - -namespace android { -namespace uirenderer { - -class DisplayListLogBuffer: public Singleton<DisplayListLogBuffer> { - DisplayListLogBuffer(); - ~DisplayListLogBuffer(); - - friend class Singleton<DisplayListLogBuffer>; - -public: - void writeCommand(int level, const char* label); - void outputCommands(FILE *file); - - bool isEmpty() { - return (mStart == mEnd); - } - - struct OpLog { - int level; - const char* label; - }; - -private: - OpLog* mBufferFirst; // where the memory starts - OpLog* mStart; // where the current command stream starts - OpLog* mEnd; // where the current commands end - OpLog* mBufferLast; // where the buffer memory ends - -}; - -}; // namespace uirenderer -}; // namespace android - -#endif // ANDROID_HWUI_DISPLAY_LIST_LOG_BUFFER_H diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h index 8a5e21d..b5801fc 100644 --- a/libs/hwui/DisplayListOp.h +++ b/libs/hwui/DisplayListOp.h @@ -17,9 +17,17 @@ #ifndef ANDROID_HWUI_DISPLAY_OPERATION_H #define ANDROID_HWUI_DISPLAY_OPERATION_H -#ifndef LOG_TAG - #define LOG_TAG "OpenGLRenderer" -#endif +#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 <SkColor.h> #include <SkPath.h> @@ -28,19 +36,6 @@ #include <private/hwui/DrawGlInfo.h> -#include "OpenGLRenderer.h" -#include "AssetAtlas.h" -#include "DeferredDisplayList.h" -#include "DisplayListRenderer.h" -#include "RenderState.h" -#include "UvMapper.h" -#include "utils/LinearAllocator.h" - -#define CRASH() do { \ - *(int *)(uintptr_t) 0xbbadbeef = 0; \ - ((void(*)())0)(); /* More reliable, but doesn't say BBADBEEF */ \ -} while(false) - // 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__ ) @@ -63,9 +58,9 @@ 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() { CRASH(); } - static void operator delete(void* ptr) { CRASH(); } - /** static void* operator new(size_t size); PURPOSELY OMITTED **/ + 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) { return allocator.alloc(size); } @@ -90,12 +85,8 @@ public: class StateOp : public DisplayListOp { public: - StateOp() {}; - - virtual ~StateOp() {} - virtual void defer(DeferStateStruct& deferStruct, int saveCount, int level, - bool useQuickReject) { + bool useQuickReject) override { // default behavior only affects immediate, deferrable state, issue directly to renderer applyState(deferStruct.mRenderer, saveCount); } @@ -105,7 +96,7 @@ public: * list to flush */ virtual void replay(ReplayStateStruct& replayStruct, int saveCount, int level, - bool useQuickReject) { + bool useQuickReject) override { applyState(replayStruct.mRenderer, saveCount); } @@ -119,7 +110,7 @@ public: : mPaint(paint), mQuickRejected(false) {} virtual void defer(DeferStateStruct& deferStruct, int saveCount, int level, - bool useQuickReject) { + bool useQuickReject) override { if (mQuickRejected && CC_LIKELY(useQuickReject)) { return; } @@ -128,15 +119,15 @@ public: } virtual void replay(ReplayStateStruct& replayStruct, int saveCount, int level, - bool useQuickReject) { + bool useQuickReject) override { if (mQuickRejected && CC_LIKELY(useQuickReject)) { return; } - replayStruct.mDrawGlStatus |= applyDraw(replayStruct.mRenderer, replayStruct.mDirty); + applyDraw(replayStruct.mRenderer, replayStruct.mDirty); } - virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) = 0; + virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) = 0; /** * Draw multiple instances of an operation, must be overidden for operations that merge @@ -145,14 +136,12 @@ public: * and pure translation transformations. Other guarantees of similarity should be enforced by * reducing which operations are tagged as mergeable. */ - virtual status_t multiDraw(OpenGLRenderer& renderer, Rect& dirty, + virtual void multiDraw(OpenGLRenderer& renderer, Rect& dirty, const Vector<OpStatePair>& ops, const Rect& bounds) { - status_t status = DrawGlInfo::kStatusDone; for (unsigned int i = 0; i < ops.size(); i++) { renderer.restoreDisplayState(*(ops[i].state), true); - status |= ops[i].op->applyDraw(renderer, dirty); + ops[i].op->applyDraw(renderer, dirty); } - return status; } /** @@ -199,10 +188,6 @@ public: } protected: - const SkPaint* getPaint(OpenGLRenderer& renderer) { - return renderer.filterPaint(mPaint); - } - // 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) { @@ -219,7 +204,7 @@ protected: if (mPaint->getShader() && !mPaint->getShader()->isOpaque()) { return false; } - if (Renderer::isBlendedColorFilter(mPaint->getColorFilter())) { + if (PaintUtils::isBlendedColorFilter(mPaint->getColorFilter())) { return false; } } @@ -232,7 +217,7 @@ protected: } - const SkPaint* mPaint; // should be accessed via getPaint() when applying + const SkPaint* mPaint; bool mQuickRejected; }; @@ -260,7 +245,7 @@ public: // default empty constructor for bounds, to be overridden in child constructor body DrawBoundedOp(const SkPaint* paint): DrawOp(paint) { } - virtual bool getLocalBounds(Rect& localBounds) { + virtual bool getLocalBounds(Rect& localBounds) override { localBounds.set(mLocalBounds); OpenGLRenderer::TextShadow textShadow; if (OpenGLRenderer::getTextShadow(mPaint, &textShadow)) { @@ -287,20 +272,20 @@ public: : mFlags(flags) {} virtual void defer(DeferStateStruct& deferStruct, int saveCount, int level, - bool useQuickReject) { + bool useQuickReject) override { int newSaveCount = deferStruct.mRenderer.save(mFlags); deferStruct.mDeferredList.addSave(deferStruct.mRenderer, this, newSaveCount); } - virtual void applyState(OpenGLRenderer& renderer, int saveCount) const { + virtual void applyState(OpenGLRenderer& renderer, int saveCount) const override { renderer.save(mFlags); } - virtual void output(int level, uint32_t logFlags) const { + virtual void output(int level, uint32_t logFlags) const override { OP_LOG("Save flags %x", mFlags); } - virtual const char* name() { return "Save"; } + virtual const char* name() override { return "Save"; } int getFlags() const { return mFlags; } private: @@ -313,21 +298,21 @@ public: : mCount(count) {} virtual void defer(DeferStateStruct& deferStruct, int saveCount, int level, - bool useQuickReject) { + bool useQuickReject) override { deferStruct.mDeferredList.addRestoreToCount(deferStruct.mRenderer, this, saveCount + mCount); deferStruct.mRenderer.restoreToCount(saveCount + mCount); } - virtual void applyState(OpenGLRenderer& renderer, int saveCount) const { + virtual void applyState(OpenGLRenderer& renderer, int saveCount) const override { renderer.restoreToCount(saveCount + mCount); } - virtual void output(int level, uint32_t logFlags) const { + virtual void output(int level, uint32_t logFlags) const override { OP_LOG("Restore to count %d", mCount); } - virtual const char* name() { return "RestoreToCount"; } + virtual const char* name() override { return "RestoreToCount"; } private: int mCount; @@ -339,7 +324,7 @@ public: : mArea(left, top, right, bottom) , mPaint(&mCachedPaint) , mFlags(flags) - , mConvexMask(NULL) { + , mConvexMask(nullptr) { mCachedPaint.setAlpha(alpha); } @@ -347,11 +332,11 @@ public: : mArea(left, top, right, bottom) , mPaint(paint) , mFlags(flags) - , mConvexMask(NULL) + , mConvexMask(nullptr) {} virtual void defer(DeferStateStruct& deferStruct, int saveCount, int level, - bool useQuickReject) { + 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); @@ -362,17 +347,19 @@ public: mPaint, mFlags); } - virtual void applyState(OpenGLRenderer& renderer, int saveCount) const { + 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 { + 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() { return isSaveLayerAlpha() ? "SaveLayerAlpha" : "SaveLayer"; } + virtual const char* name() override { + return isSaveLayerAlpha() ? "SaveLayerAlpha" : "SaveLayer"; + } int getFlags() { return mFlags; } @@ -403,15 +390,15 @@ public: TranslateOp(float dx, float dy) : mDx(dx), mDy(dy) {} - virtual void applyState(OpenGLRenderer& renderer, int saveCount) const { + virtual void applyState(OpenGLRenderer& renderer, int saveCount) const override { renderer.translate(mDx, mDy); } - virtual void output(int level, uint32_t logFlags) const { + virtual void output(int level, uint32_t logFlags) const override { OP_LOG("Translate by %f %f", mDx, mDy); } - virtual const char* name() { return "Translate"; } + virtual const char* name() override { return "Translate"; } private: float mDx; @@ -423,15 +410,15 @@ public: RotateOp(float degrees) : mDegrees(degrees) {} - virtual void applyState(OpenGLRenderer& renderer, int saveCount) const { + virtual void applyState(OpenGLRenderer& renderer, int saveCount) const override { renderer.rotate(mDegrees); } - virtual void output(int level, uint32_t logFlags) const { + virtual void output(int level, uint32_t logFlags) const override { OP_LOG("Rotate by %f degrees", mDegrees); } - virtual const char* name() { return "Rotate"; } + virtual const char* name() override { return "Rotate"; } private: float mDegrees; @@ -442,15 +429,15 @@ public: ScaleOp(float sx, float sy) : mSx(sx), mSy(sy) {} - virtual void applyState(OpenGLRenderer& renderer, int saveCount) const { + virtual void applyState(OpenGLRenderer& renderer, int saveCount) const override { renderer.scale(mSx, mSy); } - virtual void output(int level, uint32_t logFlags) const { + virtual void output(int level, uint32_t logFlags) const override { OP_LOG("Scale by %f %f", mSx, mSy); } - virtual const char* name() { return "Scale"; } + virtual const char* name() override { return "Scale"; } private: float mSx; @@ -462,15 +449,15 @@ public: SkewOp(float sx, float sy) : mSx(sx), mSy(sy) {} - virtual void applyState(OpenGLRenderer& renderer, int saveCount) const { + virtual void applyState(OpenGLRenderer& renderer, int saveCount) const override { renderer.skew(mSx, mSy); } - virtual void output(int level, uint32_t logFlags) const { + virtual void output(int level, uint32_t logFlags) const override { OP_LOG("Skew by %f %f", mSx, mSy); } - virtual const char* name() { return "Skew"; } + virtual const char* name() override { return "Skew"; } private: float mSx; @@ -482,11 +469,11 @@ public: SetMatrixOp(const SkMatrix& matrix) : mMatrix(matrix) {} - virtual void applyState(OpenGLRenderer& renderer, int saveCount) const { + virtual void applyState(OpenGLRenderer& renderer, int saveCount) const override { renderer.setMatrix(mMatrix); } - virtual void output(int level, uint32_t logFlags) const { + virtual void output(int level, uint32_t logFlags) const override { if (mMatrix.isIdentity()) { OP_LOGS("SetMatrix (reset)"); } else { @@ -494,7 +481,7 @@ public: } } - virtual const char* name() { return "SetMatrix"; } + virtual const char* name() override { return "SetMatrix"; } private: const SkMatrix mMatrix; @@ -505,15 +492,15 @@ public: ConcatMatrixOp(const SkMatrix& matrix) : mMatrix(matrix) {} - virtual void applyState(OpenGLRenderer& renderer, int saveCount) const { + virtual void applyState(OpenGLRenderer& renderer, int saveCount) const override { renderer.concatMatrix(mMatrix); } - virtual void output(int level, uint32_t logFlags) const { + virtual void output(int level, uint32_t logFlags) const override { OP_LOG("ConcatMatrix " SK_MATRIX_STRING, SK_MATRIX_ARGS(&mMatrix)); } - virtual const char* name() { return "ConcatMatrix"; } + virtual const char* name() override { return "ConcatMatrix"; } private: const SkMatrix mMatrix; @@ -524,7 +511,7 @@ public: ClipOp(SkRegion::Op op) : mOp(op) {} virtual void defer(DeferStateStruct& deferStruct, int saveCount, int level, - bool useQuickReject) { + bool useQuickReject) override { // NOTE: must defer op BEFORE applying state, since it may read clip deferStruct.mDeferredList.addClip(deferStruct.mRenderer, this); @@ -547,18 +534,18 @@ 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 { + 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 { + virtual void output(int level, uint32_t logFlags) const override { OP_LOG("ClipRect " RECT_STRING, RECT_ARGS(mArea)); } - virtual const char* name() { return "ClipRect"; } + virtual const char* name() override { return "ClipRect"; } protected: - virtual bool isRect() { return true; } + virtual bool isRect() override { return true; } private: Rect mArea; @@ -569,17 +556,17 @@ public: ClipPathOp(const SkPath* path, SkRegion::Op op) : ClipOp(op), mPath(path) {} - virtual void applyState(OpenGLRenderer& renderer, int saveCount) const { + virtual void applyState(OpenGLRenderer& renderer, int saveCount) const override { renderer.clipPath(mPath, mOp); } - virtual void output(int level, uint32_t logFlags) const { + 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() { return "ClipPath"; } + virtual const char* name() override { return "ClipPath"; } private: const SkPath* mPath; @@ -590,55 +577,22 @@ public: ClipRegionOp(const SkRegion* region, SkRegion::Op op) : ClipOp(op), mRegion(region) {} - virtual void applyState(OpenGLRenderer& renderer, int saveCount) const { + virtual void applyState(OpenGLRenderer& renderer, int saveCount) const override { renderer.clipRegion(mRegion, mOp); } - virtual void output(int level, uint32_t logFlags) const { + 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() { return "ClipRegion"; } + virtual const char* name() override { return "ClipRegion"; } private: const SkRegion* mRegion; }; -class ResetPaintFilterOp : public StateOp { -public: - virtual void applyState(OpenGLRenderer& renderer, int saveCount) const { - renderer.resetPaintFilter(); - } - - virtual void output(int level, uint32_t logFlags) const { - OP_LOGS("ResetPaintFilter"); - } - - virtual const char* name() { return "ResetPaintFilter"; } -}; - -class SetupPaintFilterOp : public StateOp { -public: - SetupPaintFilterOp(int clearBits, int setBits) - : mClearBits(clearBits), mSetBits(setBits) {} - - virtual void applyState(OpenGLRenderer& renderer, int saveCount) const { - renderer.setupPaintFilter(mClearBits, mSetBits); - } - - virtual void output(int level, uint32_t logFlags) const { - OP_LOG("SetupPaintFilter, clear %#x, set %#x", mClearBits, mSetBits); - } - - virtual const char* name() { return "SetupPaintFilter"; } - -private: - int mClearBits; - int mSetBits; -}; - /////////////////////////////////////////////////////////////////////////////// // DRAW OPERATIONS - these are operations that can draw to the canvas's device /////////////////////////////////////////////////////////////////////////////// @@ -648,11 +602,11 @@ public: DrawBitmapOp(const SkBitmap* bitmap, const SkPaint* paint) : DrawBoundedOp(0, 0, bitmap->width(), bitmap->height(), paint) , mBitmap(bitmap) - , mEntryValid(false), mEntry(NULL) { + , mEntryValid(false), mEntry(nullptr) { } - virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) { - return renderer.drawBitmap(mBitmap, getPaint(renderer)); + virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override { + renderer.drawBitmap(mBitmap, mPaint); } AssetAtlas::Entry* getAtlasEntry(OpenGLRenderer& renderer) { @@ -672,8 +626,8 @@ public: * for each bitmap in the batch. This method is also responsible for dirtying * the current layer, if any. */ - virtual status_t multiDraw(OpenGLRenderer& renderer, Rect& dirty, - const Vector<OpStatePair>& ops, const Rect& bounds) { + virtual void multiDraw(OpenGLRenderer& renderer, Rect& dirty, + const Vector<OpStatePair>& ops, const Rect& bounds) override { const DeferredDisplayState& firstState = *(ops[0].state); renderer.restoreDisplayState(firstState, true); // restore all but the clip @@ -709,19 +663,20 @@ public: } } - return renderer.drawBitmaps(mBitmap, mEntry, ops.size(), &vertices[0], + renderer.drawBitmaps(mBitmap, mEntry, ops.size(), &vertices[0], pureTranslate, bounds, mPaint); } - virtual void output(int level, uint32_t logFlags) const { - OP_LOG("Draw bitmap %p at %f %f%s", mBitmap, mLocalBounds.left, mLocalBounds.top, + 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() { return "DrawBitmap"; } + virtual const char* name() override { return "DrawBitmap"; } virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo, - const DeferredDisplayState& state) { + const DeferredDisplayState& state) override { deferInfo.batchId = DeferredDisplayList::kOpBatch_Bitmap; deferInfo.mergeId = getAtlasEntry(renderer) ? (mergeid_t) mEntry->getMergeId() : (mergeid_t) mBitmap; @@ -757,21 +712,19 @@ public: : DrawBoundedOp(dstLeft, dstTop, dstRight, dstBottom, paint), mBitmap(bitmap), mSrc(srcLeft, srcTop, srcRight, srcBottom) {} - virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) { - return renderer.drawBitmap(mBitmap, mSrc.left, mSrc.top, mSrc.right, mSrc.bottom, - mLocalBounds.left, mLocalBounds.top, mLocalBounds.right, mLocalBounds.bottom, - getPaint(renderer)); + virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override { + renderer.drawBitmap(mBitmap, mSrc, mLocalBounds, mPaint); } - virtual void output(int level, uint32_t logFlags) const { + 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() { return "DrawBitmapRect"; } + virtual const char* name() override { return "DrawBitmapRect"; } virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo, - const DeferredDisplayState& state) { + const DeferredDisplayState& state) override { deferInfo.batchId = DeferredDisplayList::kOpBatch_Bitmap; } @@ -780,27 +733,6 @@ private: Rect mSrc; }; -class DrawBitmapDataOp : public DrawBitmapOp { -public: - DrawBitmapDataOp(const SkBitmap* bitmap, const SkPaint* paint) - : DrawBitmapOp(bitmap, paint) {} - - virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) { - return renderer.drawBitmapData(mBitmap, getPaint(renderer)); - } - - virtual void output(int level, uint32_t logFlags) const { - OP_LOG("Draw bitmap %p", mBitmap); - } - - virtual const char* name() { return "DrawBitmapData"; } - - virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo, - const DeferredDisplayState& state) { - deferInfo.batchId = DeferredDisplayList::kOpBatch_Bitmap; - } -}; - class DrawBitmapMeshOp : public DrawBoundedOp { public: DrawBitmapMeshOp(const SkBitmap* bitmap, int meshWidth, int meshHeight, @@ -809,19 +741,19 @@ public: mBitmap(bitmap), mMeshWidth(meshWidth), mMeshHeight(meshHeight), mVertices(vertices), mColors(colors) {} - virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) { - return renderer.drawBitmapMesh(mBitmap, mMeshWidth, mMeshHeight, - mVertices, mColors, getPaint(renderer)); + 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 { + 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() { return "DrawBitmapMesh"; } + virtual const char* name() override { return "DrawBitmapMesh"; } virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo, - const DeferredDisplayState& state) { + const DeferredDisplayState& state) override { deferInfo.batchId = DeferredDisplayList::kOpBatch_Bitmap; } @@ -838,8 +770,8 @@ 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(NULL), - mEntryValid(false), mEntry(NULL) { + mBitmap(bitmap), mPatch(patch), mGenerationId(0), mMesh(nullptr), + mEntryValid(false), mEntry(nullptr) { }; AssetAtlas::Entry* getAtlasEntry(OpenGLRenderer& renderer) { @@ -865,8 +797,8 @@ public: * and transforming the vertices of each 9-patch in the batch. This method * is also responsible for dirtying the current layer, if any. */ - virtual status_t multiDraw(OpenGLRenderer& renderer, Rect& dirty, - const Vector<OpStatePair>& ops, const Rect& bounds) { + virtual void multiDraw(OpenGLRenderer& renderer, Rect& dirty, + const Vector<OpStatePair>& ops, const Rect& bounds) override { const DeferredDisplayState& firstState = *(ops[0].state); renderer.restoreDisplayState(firstState, true); // restore all but the clip @@ -905,7 +837,7 @@ public: patchOp->mLocalBounds.top + 0.5f); // Copy & transform all the vertices for the current operation - TextureVertex* opVertices = opMesh->vertices; + TextureVertex* opVertices = opMesh->vertices.get(); for (uint32_t j = 0; j < vertexCount; j++, opVertices++) { TextureVertex::set(vertex++, opVertices->x + tx, opVertices->y + ty, @@ -935,27 +867,27 @@ public: indexCount += opMesh->indexCount; } - return renderer.drawPatches(mBitmap, getAtlasEntry(renderer), - &vertices[0], indexCount, getPaint(renderer)); + renderer.drawPatches(mBitmap, getAtlasEntry(renderer), + &vertices[0], indexCount, mPaint); } - virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) { + 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 - return renderer.drawPatch(mBitmap, getMesh(renderer), getAtlasEntry(renderer), + renderer.drawPatch(mBitmap, getMesh(renderer), getAtlasEntry(renderer), mLocalBounds.left, mLocalBounds.top, mLocalBounds.right, mLocalBounds.bottom, - getPaint(renderer)); + mPaint); } - virtual void output(int level, uint32_t logFlags) const { + 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() { return "DrawPatch"; } + virtual const char* name() override { return "DrawPatch"; } virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo, - const DeferredDisplayState& state) { + 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() && @@ -977,17 +909,17 @@ private: class DrawColorOp : public DrawOp { public: DrawColorOp(int color, SkXfermode::Mode mode) - : DrawOp(NULL), mColor(color), mMode(mode) {}; + : DrawOp(nullptr), mColor(color), mMode(mode) {}; - virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) { - return renderer.drawColor(mColor, mMode); + virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override { + renderer.drawColor(mColor, mMode); } - virtual void output(int level, uint32_t logFlags) const { + virtual void output(int level, uint32_t logFlags) const override { OP_LOG("Draw color %#x, mode %d", mColor, mMode); } - virtual const char* name() { return "DrawColor"; } + virtual const char* name() override { return "DrawColor"; } private: int mColor; @@ -1001,7 +933,7 @@ public: DrawStrokableOp(const Rect& localBounds, const SkPaint* paint) : DrawBoundedOp(localBounds, paint) {}; - virtual bool getLocalBounds(Rect& localBounds) { + virtual bool getLocalBounds(Rect& localBounds) override { localBounds.set(mLocalBounds); if (mPaint && mPaint->getStyle() != SkPaint::kFill_Style) { localBounds.outset(strokeWidthOutset()); @@ -1010,7 +942,7 @@ public: } virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo, - const DeferredDisplayState& state) { + const DeferredDisplayState& state) override { if (mPaint->getPathEffect()) { deferInfo.batchId = DeferredDisplayList::kOpBatch_AlphaMaskTexture; } else { @@ -1026,23 +958,23 @@ public: DrawRectOp(float left, float top, float right, float bottom, const SkPaint* paint) : DrawStrokableOp(left, top, right, bottom, paint) {} - virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) { - return renderer.drawRect(mLocalBounds.left, mLocalBounds.top, - mLocalBounds.right, mLocalBounds.bottom, getPaint(renderer)); + 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 { + 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) { + const DeferredDisplayState& state) override { DrawStrokableOp::onDefer(renderer, deferInfo, state); deferInfo.opaqueOverBounds = isOpaqueOverBounds(state) && mPaint->getStyle() == SkPaint::kFill_Style; } - virtual const char* name() { return "DrawRect"; } + virtual const char* name() override { return "DrawRect"; } }; class DrawRectsOp : public DrawBoundedOp { @@ -1051,18 +983,18 @@ public: : DrawBoundedOp(rects, count, paint), mRects(rects), mCount(count) {} - virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) { - return renderer.drawRects(mRects, mCount, getPaint(renderer)); + virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override { + renderer.drawRects(mRects, mCount, mPaint); } - virtual void output(int level, uint32_t logFlags) const { + virtual void output(int level, uint32_t logFlags) const override { OP_LOG("Draw Rects count %d", mCount); } - virtual const char* name() { return "DrawRects"; } + virtual const char* name() override { return "DrawRects"; } virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo, - const DeferredDisplayState& state) { + const DeferredDisplayState& state) override { deferInfo.batchId = DeferredDisplayList::kOpBatch_Vertices; } @@ -1077,17 +1009,17 @@ public: float rx, float ry, const SkPaint* paint) : DrawStrokableOp(left, top, right, bottom, paint), mRx(rx), mRy(ry) {} - virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) { - return renderer.drawRoundRect(mLocalBounds.left, mLocalBounds.top, - mLocalBounds.right, mLocalBounds.bottom, mRx, mRy, getPaint(renderer)); + 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 { + 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) { + const DeferredDisplayState& state) override { DrawStrokableOp::onDefer(renderer, deferInfo, state); if (!mPaint->getPathEffect()) { renderer.getCaches().tessellationCache.precacheRoundRect(state.mMatrix, *mPaint, @@ -1095,7 +1027,7 @@ public: } } - virtual const char* name() { return "DrawRoundRect"; } + virtual const char* name() override { return "DrawRoundRect"; } private: float mRx; @@ -1109,17 +1041,17 @@ public: : DrawOp(paint), mLeft(left), mTop(top), mRight(right), mBottom(bottom), mRx(rx), mRy(ry) {} - virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) { - return renderer.drawRoundRect(*mLeft, *mTop, *mRight, *mBottom, - *mRx, *mRy, getPaint(renderer)); + 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 { + 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() { return "DrawRoundRectProps"; } + virtual const char* name() override { return "DrawRoundRectProps"; } private: float* mLeft; @@ -1136,15 +1068,15 @@ public: : DrawStrokableOp(x - radius, y - radius, x + radius, y + radius, paint), mX(x), mY(y), mRadius(radius) {} - virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) { - return renderer.drawCircle(mX, mY, mRadius, getPaint(renderer)); + virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override { + renderer.drawCircle(mX, mY, mRadius, mPaint); } - virtual void output(int level, uint32_t logFlags) const { + 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() { return "DrawCircle"; } + virtual const char* name() override { return "DrawCircle"; } private: float mX; @@ -1157,15 +1089,15 @@ public: DrawCirclePropsOp(float* x, float* y, float* radius, const SkPaint* paint) : DrawOp(paint), mX(x), mY(y), mRadius(radius) {} - virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) { - return renderer.drawCircle(*mX, *mY, *mRadius, getPaint(renderer)); + virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override { + renderer.drawCircle(*mX, *mY, *mRadius, mPaint); } - virtual void output(int level, uint32_t logFlags) const { + 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() { return "DrawCircleProps"; } + virtual const char* name() override { return "DrawCircleProps"; } private: float* mX; @@ -1178,16 +1110,16 @@ public: DrawOvalOp(float left, float top, float right, float bottom, const SkPaint* paint) : DrawStrokableOp(left, top, right, bottom, paint) {} - virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) { - return renderer.drawOval(mLocalBounds.left, mLocalBounds.top, - mLocalBounds.right, mLocalBounds.bottom, getPaint(renderer)); + 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 { + virtual void output(int level, uint32_t logFlags) const override { OP_LOG("Draw Oval " RECT_STRING, RECT_ARGS(mLocalBounds)); } - virtual const char* name() { return "DrawOval"; } + virtual const char* name() override { return "DrawOval"; } }; class DrawArcOp : public DrawStrokableOp { @@ -1197,18 +1129,18 @@ public: : DrawStrokableOp(left, top, right, bottom, paint), mStartAngle(startAngle), mSweepAngle(sweepAngle), mUseCenter(useCenter) {} - virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) { - return renderer.drawArc(mLocalBounds.left, mLocalBounds.top, + virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override { + renderer.drawArc(mLocalBounds.left, mLocalBounds.top, mLocalBounds.right, mLocalBounds.bottom, - mStartAngle, mSweepAngle, mUseCenter, getPaint(renderer)); + mStartAngle, mSweepAngle, mUseCenter, mPaint); } - virtual void output(int level, uint32_t logFlags) const { + 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() { return "DrawArc"; } + virtual const char* name() override { return "DrawArc"; } private: float mStartAngle; @@ -1228,23 +1160,22 @@ public: mLocalBounds.set(left, top, left + width, top + height); } - virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) { - return renderer.drawPath(mPath, getPaint(renderer)); + virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override { + renderer.drawPath(mPath, mPaint); } virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo, - const DeferredDisplayState& state) { - const SkPaint* paint = getPaint(renderer); - renderer.getCaches().pathCache.precache(mPath, paint); + const DeferredDisplayState& state) override { + renderer.getCaches().pathCache.precache(mPath, mPaint); deferInfo.batchId = DeferredDisplayList::kOpBatch_AlphaMaskTexture; } - virtual void output(int level, uint32_t logFlags) const { + 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() { return "DrawPath"; } + virtual const char* name() override { return "DrawPath"; } private: const SkPath* mPath; @@ -1258,18 +1189,18 @@ public: mLocalBounds.outset(strokeWidthOutset()); } - virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) { - return renderer.drawLines(mPoints, mCount, getPaint(renderer)); + virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override { + renderer.drawLines(mPoints, mCount, mPaint); } - virtual void output(int level, uint32_t logFlags) const { + virtual void output(int level, uint32_t logFlags) const override { OP_LOG("Draw Lines count %d", mCount); } - virtual const char* name() { return "DrawLines"; } + virtual const char* name() override { return "DrawLines"; } virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo, - const DeferredDisplayState& state) { + const DeferredDisplayState& state) override { deferInfo.batchId = mPaint->isAntiAlias() ? DeferredDisplayList::kOpBatch_AlphaVertices : DeferredDisplayList::kOpBatch_Vertices; @@ -1285,15 +1216,15 @@ public: DrawPointsOp(const float* points, int count, const SkPaint* paint) : DrawLinesOp(points, count, paint) {} - virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) { - return renderer.drawPoints(mPoints, mCount, getPaint(renderer)); + virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override { + renderer.drawPoints(mPoints, mCount, mPaint); } - virtual void output(int level, uint32_t logFlags) const { + virtual void output(int level, uint32_t logFlags) const override { OP_LOG("Draw Points count %d", mCount); } - virtual const char* name() { return "DrawPoints"; } + virtual const char* name() override { return "DrawPoints"; } }; class DrawSomeTextOp : public DrawOp { @@ -1301,19 +1232,18 @@ public: DrawSomeTextOp(const char* 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 { + virtual void output(int level, uint32_t logFlags) const override { OP_LOG("Draw some text, %d bytes", mBytesCount); } - virtual bool hasTextShadow() const { + virtual bool hasTextShadow() const override { return OpenGLRenderer::hasTextShadow(mPaint); } virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo, - const DeferredDisplayState& state) { - const SkPaint* paint = getPaint(renderer); - FontRenderer& fontRenderer = renderer.getCaches().fontRenderer->getFontRenderer(paint); - fontRenderer.precache(paint, mText, mCount, SkMatrix::I()); + const DeferredDisplayState& state) override { + FontRenderer& fontRenderer = renderer.getCaches().fontRenderer->getFontRenderer(mPaint); + fontRenderer.precache(mPaint, mText, mCount, SkMatrix::I()); deferInfo.batchId = mPaint->getColor() == SK_ColorBLACK ? DeferredDisplayList::kOpBatch_Text : @@ -1335,12 +1265,12 @@ public: /* TODO: inherit from DrawBounded and init mLocalBounds */ } - virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) { - return renderer.drawTextOnPath(mText, mBytesCount, mCount, mPath, - mHOffset, mVOffset, getPaint(renderer)); + virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override { + renderer.drawTextOnPath(mText, mBytesCount, mCount, mPath, + mHOffset, mVOffset, mPaint); } - virtual const char* name() { return "DrawTextOnPath"; } + virtual const char* name() override { return "DrawTextOnPath"; } private: const SkPath* mPath; @@ -1356,11 +1286,11 @@ public: /* TODO: inherit from DrawBounded and init mLocalBounds */ } - virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) { - return renderer.drawPosText(mText, mBytesCount, mCount, mPositions, getPaint(renderer)); + virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override { + renderer.drawPosText(mText, mBytesCount, mCount, mPositions, mPaint); } - virtual const char* name() { return "DrawPosText"; } + virtual const char* name() override { return "DrawPosText"; } private: const float* mPositions; @@ -1376,13 +1306,12 @@ public: } virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo, - const DeferredDisplayState& state) { - const SkPaint* paint = getPaint(renderer); - FontRenderer& fontRenderer = renderer.getCaches().fontRenderer->getFontRenderer(paint); + const DeferredDisplayState& state) override { + FontRenderer& fontRenderer = renderer.getCaches().fontRenderer->getFontRenderer(mPaint); SkMatrix transform; renderer.findBestFontTransform(state.mMatrix, &transform); if (mPrecacheTransform != transform) { - fontRenderer.precache(paint, mText, mCount, transform); + fontRenderer.precache(mPaint, mText, mCount, transform); mPrecacheTransform = transform; } deferInfo.batchId = mPaint->getColor() == SK_ColorBLACK ? @@ -1400,36 +1329,34 @@ public: && OpenGLRenderer::getXfermodeDirect(mPaint) == SkXfermode::kSrcOver_Mode; } - virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) { + virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override { Rect bounds; getLocalBounds(bounds); - return renderer.drawText(mText, mBytesCount, mCount, mX, mY, - mPositions, getPaint(renderer), mTotalAdvance, bounds); + renderer.drawText(mText, mBytesCount, mCount, mX, mY, + mPositions, mPaint, mTotalAdvance, bounds); } - virtual status_t multiDraw(OpenGLRenderer& renderer, Rect& dirty, - const Vector<OpStatePair>& ops, const Rect& bounds) { - status_t status = DrawGlInfo::kStatusDone; + virtual void multiDraw(OpenGLRenderer& renderer, Rect& dirty, + const 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) ? kDrawOpMode_Flush : kDrawOpMode_Defer; + 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() - status |= renderer.drawText(op.mText, op.mBytesCount, op.mCount, op.mX, op.mY, - op.mPositions, op.getPaint(renderer), op.mTotalAdvance, op.mLocalBounds, + renderer.drawText(op.mText, op.mBytesCount, op.mCount, op.mX, op.mY, + op.mPositions, op.mPaint, op.mTotalAdvance, op.mLocalBounds, drawOpMode); } - return status; } - virtual void output(int level, uint32_t logFlags) const { + 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() { return "DrawText"; } + virtual const char* name() override { return "DrawText"; } private: const char* mText; @@ -1449,20 +1376,19 @@ private: class DrawFunctorOp : public DrawOp { public: DrawFunctorOp(Functor* functor) - : DrawOp(NULL), mFunctor(functor) {} + : DrawOp(nullptr), mFunctor(functor) {} - virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) { + virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override { renderer.startMark("GL functor"); - status_t ret = renderer.callDrawGLFunction(mFunctor, dirty); + renderer.callDrawGLFunction(mFunctor, dirty); renderer.endMark(); - return ret; } - virtual void output(int level, uint32_t logFlags) const { + virtual void output(int level, uint32_t logFlags) const override { OP_LOG("Draw Functor %p", mFunctor); } - virtual const char* name() { return "DrawFunctor"; } + virtual const char* name() override { return "DrawFunctor"; } private: Functor* mFunctor; @@ -1473,36 +1399,38 @@ class DrawRenderNodeOp : public DrawBoundedOp { friend class DisplayListData; // grant DisplayListData access to info of child public: DrawRenderNodeOp(RenderNode* renderNode, int flags, const mat4& transformFromParent) - : DrawBoundedOp(0, 0, renderNode->getWidth(), renderNode->getHeight(), 0), - mRenderNode(renderNode), mFlags(flags), mTransformFromParent(transformFromParent) {} + : DrawBoundedOp(0, 0, renderNode->getWidth(), renderNode->getHeight(), nullptr) + , mRenderNode(renderNode) + , mFlags(flags) + , mTransformFromParent(transformFromParent) + , mSkipInOrderDraw(false) {} virtual void defer(DeferStateStruct& deferStruct, int saveCount, int level, - bool useQuickReject) { + bool useQuickReject) override { if (mRenderNode->isRenderable() && !mSkipInOrderDraw) { mRenderNode->defer(deferStruct, level + 1); } } virtual void replay(ReplayStateStruct& replayStruct, int saveCount, int level, - bool useQuickReject) { + bool useQuickReject) override { if (mRenderNode->isRenderable() && !mSkipInOrderDraw) { mRenderNode->replay(replayStruct, level + 1); } } - virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) { + virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override { LOG_ALWAYS_FATAL("should not be called, because replay() is overridden"); - return 0; } - virtual void output(int level, uint32_t logFlags) const { + virtual void output(int level, uint32_t logFlags) const override { OP_LOG("Draw RenderNode %p %s, flags %#x", mRenderNode, mRenderNode->getName(), mFlags); if (mRenderNode && (logFlags & kOpLogFlag_Recurse)) { mRenderNode->output(level + 1); } } - virtual const char* name() { return "DrawRenderNode"; } + virtual const char* name() override { return "DrawRenderNode"; } RenderNode* renderNode() { return mRenderNode; } @@ -1537,7 +1465,7 @@ class DrawShadowOp : public DrawOp { public: DrawShadowOp(const mat4& transformXY, const mat4& transformZ, float casterAlpha, const SkPath* casterOutline) - : DrawOp(NULL) + : DrawOp(nullptr) , mTransformXY(transformXY) , mTransformZ(transformZ) , mCasterAlpha(casterAlpha) @@ -1545,13 +1473,13 @@ public: } virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo, - const DeferredDisplayState& state) { + const DeferredDisplayState& state) override { renderer.getCaches().tessellationCache.precacheShadows(&state.mMatrix, renderer.getLocalClipBounds(), isCasterOpaque(), mCasterOutline, &mTransformXY, &mTransformZ, renderer.getLightCenter(), renderer.getLightRadius()); } - virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) { + virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override { TessellationCache::vertexBuffer_pair_t buffers; Matrix4 drawTransform(*(renderer.currentTransform())); renderer.getCaches().tessellationCache.getShadowBuffers(&drawTransform, @@ -1559,14 +1487,14 @@ public: &mTransformXY, &mTransformZ, renderer.getLightCenter(), renderer.getLightRadius(), buffers); - return renderer.drawShadow(mCasterAlpha, buffers.first, buffers.second); + renderer.drawShadow(mCasterAlpha, buffers.first, buffers.second); } - virtual void output(int level, uint32_t logFlags) const { + virtual void output(int level, uint32_t logFlags) const override { OP_LOGS("DrawShadow"); } - virtual const char* name() { return "DrawShadow"; } + virtual const char* name() override { return "DrawShadow"; } private: bool isCasterOpaque() { return mCasterAlpha >= 1.0f; } @@ -1580,17 +1508,17 @@ private: class DrawLayerOp : public DrawOp { public: DrawLayerOp(Layer* layer, float x, float y) - : DrawOp(NULL), mLayer(layer), mX(x), mY(y) {} + : DrawOp(nullptr), mLayer(layer), mX(x), mY(y) {} - virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) { - return renderer.drawLayer(mLayer, mX, mY); + virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override { + renderer.drawLayer(mLayer, mX, mY); } - virtual void output(int level, uint32_t logFlags) const { + virtual void output(int level, uint32_t logFlags) const override { OP_LOG("Draw Layer %p at %f %f", mLayer, mX, mY); } - virtual const char* name() { return "DrawLayer"; } + virtual const char* name() override { return "DrawLayer"; } private: Layer* mLayer; diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp deleted file mode 100644 index c2cb76e..0000000 --- a/libs/hwui/DisplayListRenderer.cpp +++ /dev/null @@ -1,532 +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. - */ - -#define LOG_TAG "OpenGLRenderer" - -#include <SkCamera.h> -#include <SkCanvas.h> - -#include <private/hwui/DrawGlInfo.h> - -#include "ResourceCache.h" -#include "DeferredDisplayList.h" -#include "DeferredLayerUpdater.h" -#include "DisplayListLogBuffer.h" -#include "DisplayListOp.h" -#include "DisplayListRenderer.h" -#include "RenderNode.h" - -namespace android { -namespace uirenderer { - -DisplayListRenderer::DisplayListRenderer() - : mResourceCache(ResourceCache::getInstance()) - , mDisplayListData(NULL) - , mTranslateX(0.0f) - , mTranslateY(0.0f) - , mDeferredBarrierType(kBarrier_None) - , mHighContrastText(false) - , mRestoreSaveCount(-1) { -} - -DisplayListRenderer::~DisplayListRenderer() { - LOG_ALWAYS_FATAL_IF(mDisplayListData, - "Destroyed a DisplayListRenderer during a record!"); -} - -/////////////////////////////////////////////////////////////////////////////// -// Operations -/////////////////////////////////////////////////////////////////////////////// - -DisplayListData* DisplayListRenderer::finishRecording() { - mPaintMap.clear(); - mRegionMap.clear(); - mPathMap.clear(); - DisplayListData* data = mDisplayListData; - mDisplayListData = 0; - return data; -} - -status_t DisplayListRenderer::prepareDirty(float left, float top, - float right, float bottom, bool opaque) { - - LOG_ALWAYS_FATAL_IF(mDisplayListData, - "prepareDirty called a second time during a recording!"); - mDisplayListData = new DisplayListData(); - - initializeSaveStack(0, 0, getWidth(), getHeight(), Vector3()); - - mDeferredBarrierType = kBarrier_InOrder; - mDirtyClip = opaque; - mRestoreSaveCount = -1; - - return DrawGlInfo::kStatusDone; // No invalidate needed at record-time -} - -void DisplayListRenderer::finish() { - flushRestoreToCount(); - flushTranslate(); -} - -void DisplayListRenderer::interrupt() { -} - -void DisplayListRenderer::resume() { -} - -status_t DisplayListRenderer::callDrawGLFunction(Functor *functor, Rect& dirty) { - // Ignore dirty during recording, it matters only when we replay - addDrawOp(new (alloc()) DrawFunctorOp(functor)); - mDisplayListData->functors.add(functor); - return DrawGlInfo::kStatusDone; // No invalidate needed at record-time -} - -int DisplayListRenderer::save(int flags) { - addStateOp(new (alloc()) SaveOp(flags)); - return StatefulBaseRenderer::save(flags); -} - -void DisplayListRenderer::restore() { - if (mRestoreSaveCount < 0) { - restoreToCount(getSaveCount() - 1); - return; - } - - mRestoreSaveCount--; - flushTranslate(); - StatefulBaseRenderer::restore(); -} - -void DisplayListRenderer::restoreToCount(int saveCount) { - mRestoreSaveCount = saveCount; - flushTranslate(); - StatefulBaseRenderer::restoreToCount(saveCount); -} - -int DisplayListRenderer::saveLayer(float left, float top, float right, float bottom, - const SkPaint* paint, int flags) { - // force matrix/clip isolation for layer - flags |= SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag; - - paint = refPaint(paint); - addStateOp(new (alloc()) SaveLayerOp(left, top, right, bottom, paint, flags)); - return StatefulBaseRenderer::save(flags); -} - -void DisplayListRenderer::translate(float dx, float dy, float dz) { - // ignore dz, not used at defer time - mHasDeferredTranslate = true; - mTranslateX += dx; - mTranslateY += dy; - flushRestoreToCount(); - StatefulBaseRenderer::translate(dx, dy, dz); -} - -void DisplayListRenderer::rotate(float degrees) { - addStateOp(new (alloc()) RotateOp(degrees)); - StatefulBaseRenderer::rotate(degrees); -} - -void DisplayListRenderer::scale(float sx, float sy) { - addStateOp(new (alloc()) ScaleOp(sx, sy)); - StatefulBaseRenderer::scale(sx, sy); -} - -void DisplayListRenderer::skew(float sx, float sy) { - addStateOp(new (alloc()) SkewOp(sx, sy)); - StatefulBaseRenderer::skew(sx, sy); -} - -void DisplayListRenderer::setMatrix(const SkMatrix& matrix) { - addStateOp(new (alloc()) SetMatrixOp(matrix)); - StatefulBaseRenderer::setMatrix(matrix); -} - -void DisplayListRenderer::concatMatrix(const SkMatrix& matrix) { - addStateOp(new (alloc()) ConcatMatrixOp(matrix)); - StatefulBaseRenderer::concatMatrix(matrix); -} - -bool DisplayListRenderer::clipRect(float left, float top, float right, float bottom, - SkRegion::Op op) { - addStateOp(new (alloc()) ClipRectOp(left, top, right, bottom, op)); - return StatefulBaseRenderer::clipRect(left, top, right, bottom, op); -} - -bool DisplayListRenderer::clipPath(const SkPath* path, SkRegion::Op op) { - path = refPath(path); - addStateOp(new (alloc()) ClipPathOp(path, op)); - return StatefulBaseRenderer::clipPath(path, op); -} - -bool DisplayListRenderer::clipRegion(const SkRegion* region, SkRegion::Op op) { - region = refRegion(region); - addStateOp(new (alloc()) ClipRegionOp(region, op)); - return StatefulBaseRenderer::clipRegion(region, op); -} - -status_t DisplayListRenderer::drawRenderNode(RenderNode* renderNode, Rect& dirty, int32_t flags) { - LOG_ALWAYS_FATAL_IF(!renderNode, "missing rendernode"); - - // dirty is an out parameter and should not be recorded, - // it matters only when replaying the display list - DrawRenderNodeOp* op = new (alloc()) DrawRenderNodeOp(renderNode, flags, *currentTransform()); - addRenderNodeOp(op); - - return DrawGlInfo::kStatusDone; -} - -status_t DisplayListRenderer::drawLayer(DeferredLayerUpdater* layerHandle, float x, float y) { - // We ref the DeferredLayerUpdater due to its thread-safe ref-counting - // semantics. - mDisplayListData->ref(layerHandle); - addDrawOp(new (alloc()) DrawLayerOp(layerHandle->backingLayer(), x, y)); - return DrawGlInfo::kStatusDone; -} - -status_t DisplayListRenderer::drawBitmap(const SkBitmap* bitmap, const SkPaint* paint) { - bitmap = refBitmap(bitmap); - paint = refPaint(paint); - - addDrawOp(new (alloc()) DrawBitmapOp(bitmap, paint)); - return DrawGlInfo::kStatusDone; -} - -status_t DisplayListRenderer::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(SkCanvas::kMatrix_SaveFlag); - translate(dstLeft, dstTop); - drawBitmap(bitmap, paint); - restore(); - } else { - bitmap = refBitmap(bitmap); - paint = refPaint(paint); - - addDrawOp(new (alloc()) DrawBitmapRectOp(bitmap, - srcLeft, srcTop, srcRight, srcBottom, - dstLeft, dstTop, dstRight, dstBottom, paint)); - } - return DrawGlInfo::kStatusDone; -} - -status_t DisplayListRenderer::drawBitmapData(const SkBitmap* bitmap, const SkPaint* paint) { - bitmap = refBitmapData(bitmap); - paint = refPaint(paint); - - addDrawOp(new (alloc()) DrawBitmapDataOp(bitmap, paint)); - return DrawGlInfo::kStatusDone; -} - -status_t DisplayListRenderer::drawBitmapMesh(const SkBitmap* bitmap, int meshWidth, int meshHeight, - const float* vertices, const int* colors, const SkPaint* paint) { - int vertexCount = (meshWidth + 1) * (meshHeight + 1); - bitmap = refBitmap(bitmap); - 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(bitmap, meshWidth, meshHeight, - vertices, colors, paint)); - return DrawGlInfo::kStatusDone; -} - -status_t DisplayListRenderer::drawPatch(const SkBitmap* bitmap, const Res_png_9patch* patch, - float left, float top, float right, float bottom, const SkPaint* paint) { - bitmap = refBitmap(bitmap); - patch = refPatch(patch); - paint = refPaint(paint); - - addDrawOp(new (alloc()) DrawPatchOp(bitmap, patch, left, top, right, bottom, paint)); - return DrawGlInfo::kStatusDone; -} - -status_t DisplayListRenderer::drawColor(int color, SkXfermode::Mode mode) { - addDrawOp(new (alloc()) DrawColorOp(color, mode)); - return DrawGlInfo::kStatusDone; -} - -status_t DisplayListRenderer::drawRect(float left, float top, float right, float bottom, - const SkPaint* paint) { - paint = refPaint(paint); - addDrawOp(new (alloc()) DrawRectOp(left, top, right, bottom, paint)); - return DrawGlInfo::kStatusDone; -} - -status_t DisplayListRenderer::drawRoundRect(float left, float top, float right, float bottom, - float rx, float ry, const SkPaint* paint) { - paint = refPaint(paint); - addDrawOp(new (alloc()) DrawRoundRectOp(left, top, right, bottom, rx, ry, paint)); - return DrawGlInfo::kStatusDone; -} - -status_t DisplayListRenderer::drawRoundRect( - CanvasPropertyPrimitive* left, CanvasPropertyPrimitive* top, - CanvasPropertyPrimitive* right, CanvasPropertyPrimitive* bottom, - CanvasPropertyPrimitive* rx, CanvasPropertyPrimitive* ry, - CanvasPropertyPaint* paint) { - mDisplayListData->ref(left); - mDisplayListData->ref(top); - mDisplayListData->ref(right); - mDisplayListData->ref(bottom); - mDisplayListData->ref(rx); - mDisplayListData->ref(ry); - mDisplayListData->ref(paint); - addDrawOp(new (alloc()) DrawRoundRectPropsOp(&left->value, &top->value, - &right->value, &bottom->value, &rx->value, &ry->value, &paint->value)); - return DrawGlInfo::kStatusDone; -} - -status_t DisplayListRenderer::drawCircle(float x, float y, float radius, const SkPaint* paint) { - paint = refPaint(paint); - addDrawOp(new (alloc()) DrawCircleOp(x, y, radius, paint)); - return DrawGlInfo::kStatusDone; -} - -status_t DisplayListRenderer::drawCircle(CanvasPropertyPrimitive* x, CanvasPropertyPrimitive* y, - CanvasPropertyPrimitive* radius, CanvasPropertyPaint* paint) { - mDisplayListData->ref(x); - mDisplayListData->ref(y); - mDisplayListData->ref(radius); - mDisplayListData->ref(paint); - addDrawOp(new (alloc()) DrawCirclePropsOp(&x->value, &y->value, - &radius->value, &paint->value)); - return DrawGlInfo::kStatusDone; -} - -status_t DisplayListRenderer::drawOval(float left, float top, float right, float bottom, - const SkPaint* paint) { - paint = refPaint(paint); - addDrawOp(new (alloc()) DrawOvalOp(left, top, right, bottom, paint)); - return DrawGlInfo::kStatusDone; -} - -status_t DisplayListRenderer::drawArc(float left, float top, float right, float bottom, - float startAngle, float sweepAngle, bool useCenter, const SkPaint* paint) { - if (fabs(sweepAngle) >= 360.0f) { - return drawOval(left, top, right, bottom, paint); - } - - paint = refPaint(paint); - addDrawOp(new (alloc()) DrawArcOp(left, top, right, bottom, - startAngle, sweepAngle, useCenter, paint)); - return DrawGlInfo::kStatusDone; -} - -status_t DisplayListRenderer::drawPath(const SkPath* path, const SkPaint* paint) { - path = refPath(path); - paint = refPaint(paint); - - addDrawOp(new (alloc()) DrawPathOp(path, paint)); - return DrawGlInfo::kStatusDone; -} - -status_t DisplayListRenderer::drawLines(const float* points, int count, const SkPaint* paint) { - points = refBuffer<float>(points, count); - paint = refPaint(paint); - - addDrawOp(new (alloc()) DrawLinesOp(points, count, paint)); - return DrawGlInfo::kStatusDone; -} - -status_t DisplayListRenderer::drawPoints(const float* points, int count, const SkPaint* paint) { - points = refBuffer<float>(points, count); - paint = refPaint(paint); - - addDrawOp(new (alloc()) DrawPointsOp(points, count, paint)); - return DrawGlInfo::kStatusDone; -} - -status_t DisplayListRenderer::drawTextOnPath(const char* text, int bytesCount, int count, - const SkPath* path, float hOffset, float vOffset, const SkPaint* paint) { - if (!text || count <= 0) return DrawGlInfo::kStatusDone; - - text = refText(text, bytesCount); - path = refPath(path); - paint = refPaint(paint); - - DrawOp* op = new (alloc()) DrawTextOnPathOp(text, bytesCount, count, path, - hOffset, vOffset, paint); - addDrawOp(op); - return DrawGlInfo::kStatusDone; -} - -status_t DisplayListRenderer::drawPosText(const char* text, int bytesCount, int count, - const float* positions, const SkPaint* paint) { - if (!text || count <= 0) return DrawGlInfo::kStatusDone; - - text = refText(text, bytesCount); - positions = refBuffer<float>(positions, count * 2); - paint = refPaint(paint); - - DrawOp* op = new (alloc()) DrawPosTextOp(text, bytesCount, count, positions, paint); - addDrawOp(op); - return DrawGlInfo::kStatusDone; -} - -static void simplifyPaint(int color, SkPaint* paint) { - paint->setColor(color); - paint->setShader(NULL); - paint->setColorFilter(NULL); - paint->setLooper(NULL); - paint->setStrokeWidth(4 + 0.04 * paint->getTextSize()); - paint->setStrokeJoin(SkPaint::kRound_Join); - paint->setLooper(NULL); -} - -status_t DisplayListRenderer::drawText(const char* text, int bytesCount, int count, - float x, float y, const float* positions, const SkPaint* paint, - float totalAdvance, const Rect& bounds, DrawOpMode drawOpMode) { - - if (!text || count <= 0 || paintWillNotDrawText(*paint)) return DrawGlInfo::kStatusDone; - - text = refText(text, bytesCount); - positions = refBuffer<float>(positions, count * 2); - - if (CC_UNLIKELY(mHighContrastText)) { - // high contrast draw path - int color = paint->getColor(); - int channelSum = SkColorGetR(color) + SkColorGetG(color) + SkColorGetB(color); - bool darken = channelSum < (128 * 3); - - // outline - SkPaint* outlinePaint = copyPaint(paint); - simplifyPaint(darken ? SK_ColorWHITE : SK_ColorBLACK, outlinePaint); - outlinePaint->setStyle(SkPaint::kStrokeAndFill_Style); - addDrawOp(new (alloc()) DrawTextOp(text, bytesCount, count, - x, y, positions, outlinePaint, totalAdvance, bounds)); // bounds? - - // inner - SkPaint* innerPaint = copyPaint(paint); - simplifyPaint(darken ? SK_ColorBLACK : SK_ColorWHITE, innerPaint); - innerPaint->setStyle(SkPaint::kFill_Style); - addDrawOp(new (alloc()) DrawTextOp(text, bytesCount, count, - x, y, positions, innerPaint, totalAdvance, bounds)); - } else { - // standard draw path - paint = refPaint(paint); - - DrawOp* op = new (alloc()) DrawTextOp(text, bytesCount, count, - x, y, positions, paint, totalAdvance, bounds); - addDrawOp(op); - } - return DrawGlInfo::kStatusDone; -} - -status_t DisplayListRenderer::drawRects(const float* rects, int count, const SkPaint* paint) { - if (count <= 0) return DrawGlInfo::kStatusDone; - - rects = refBuffer<float>(rects, count); - paint = refPaint(paint); - addDrawOp(new (alloc()) DrawRectsOp(rects, count, paint)); - return DrawGlInfo::kStatusDone; -} - -void DisplayListRenderer::resetPaintFilter() { - addStateOp(new (alloc()) ResetPaintFilterOp()); -} - -void DisplayListRenderer::setupPaintFilter(int clearBits, int setBits) { - addStateOp(new (alloc()) SetupPaintFilterOp(clearBits, setBits)); -} - -void DisplayListRenderer::insertReorderBarrier(bool enableReorder) { - flushRestoreToCount(); - flushTranslate(); - mDeferredBarrierType = enableReorder ? kBarrier_OutOfOrder : kBarrier_InOrder; -} - -void DisplayListRenderer::flushRestoreToCount() { - if (mRestoreSaveCount >= 0) { - addOpAndUpdateChunk(new (alloc()) RestoreToCountOp(mRestoreSaveCount)); - mRestoreSaveCount = -1; - } -} - -void DisplayListRenderer::flushTranslate() { - if (mHasDeferredTranslate) { - if (mTranslateX != 0.0f || mTranslateY != 0.0f) { - addOpAndUpdateChunk(new (alloc()) TranslateOp(mTranslateX, mTranslateY)); - mTranslateX = mTranslateY = 0.0f; - } - mHasDeferredTranslate = false; - } -} - -size_t DisplayListRenderer::addOpAndUpdateChunk(DisplayListOp* op) { - int insertIndex = mDisplayListData->displayListOps.add(op); - if (mDeferredBarrierType != kBarrier_None) { - // op is first in new chunk - mDisplayListData->chunks.push(); - DisplayListData::Chunk& newChunk = mDisplayListData->chunks.editTop(); - newChunk.beginOpIndex = insertIndex; - newChunk.endOpIndex = insertIndex + 1; - newChunk.reorderChildren = (mDeferredBarrierType == kBarrier_OutOfOrder); - - int nextChildIndex = mDisplayListData->children().size(); - newChunk.beginChildIndex = newChunk.endChildIndex = nextChildIndex; - mDeferredBarrierType = kBarrier_None; - } else { - // standard case - append to existing chunk - mDisplayListData->chunks.editTop().endOpIndex = insertIndex + 1; - } - return insertIndex; -} - -size_t DisplayListRenderer::flushAndAddOp(DisplayListOp* op) { - flushRestoreToCount(); - flushTranslate(); - return addOpAndUpdateChunk(op); -} - -size_t DisplayListRenderer::addStateOp(StateOp* op) { - return flushAndAddOp(op); -} - -size_t DisplayListRenderer::addDrawOp(DrawOp* op) { - Rect localBounds; - if (op->getLocalBounds(localBounds)) { - bool rejected = quickRejectConservative(localBounds.left, localBounds.top, - localBounds.right, localBounds.bottom); - op->setQuickRejected(rejected); - } - - mDisplayListData->hasDrawOps = true; - return flushAndAddOp(op); -} - -size_t DisplayListRenderer::addRenderNodeOp(DrawRenderNodeOp* op) { - int opIndex = addDrawOp(op); - int childIndex = mDisplayListData->addChild(op); - - // update the chunk's child indices - DisplayListData::Chunk& chunk = mDisplayListData->chunks.editTop(); - chunk.endChildIndex = childIndex + 1; - - if (op->renderNode()->stagingProperties().isProjectionReceiver()) { - // use staging property, since recording on UI thread - mDisplayListData->projectionReceiveIndex = opIndex; - } - return opIndex; -} - -}; // namespace uirenderer -}; // namespace android diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h deleted file mode 100644 index 2cc2be3..0000000 --- a/libs/hwui/DisplayListRenderer.h +++ /dev/null @@ -1,316 +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 <SkMatrix.h> -#include <SkPaint.h> -#include <SkPath.h> -#include <cutils/compiler.h> - -#include "DisplayListLogBuffer.h" -#include "RenderNode.h" -#include "ResourceCache.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 DisplayListRenderer; -class DisplayListOp; -class DrawOp; -class StateOp; - -/** - * Records drawing commands in a display list for later playback into an OpenGLRenderer. - */ -class ANDROID_API DisplayListRenderer: public StatefulBaseRenderer { -public: - DisplayListRenderer(); - virtual ~DisplayListRenderer(); - - void insertReorderBarrier(bool enableReorder); - - DisplayListData* finishRecording(); - -// ---------------------------------------------------------------------------- -// Frame state operations -// ---------------------------------------------------------------------------- - virtual status_t prepareDirty(float left, float top, float right, float bottom, bool opaque); - virtual void finish(); - virtual void interrupt(); - virtual void resume(); - -// ---------------------------------------------------------------------------- -// Canvas state operations -// ---------------------------------------------------------------------------- - // Save (layer) - virtual int save(int flags); - virtual void restore(); - virtual void restoreToCount(int saveCount); - virtual int saveLayer(float left, float top, float right, float bottom, - const SkPaint* paint, int flags); - - // Matrix - virtual void translate(float dx, float dy, float dz = 0.0f); - virtual void rotate(float degrees); - virtual void scale(float sx, float sy); - virtual void skew(float sx, float sy); - - virtual void setMatrix(const SkMatrix& matrix); - virtual void concatMatrix(const SkMatrix& matrix); - - // Clip - virtual bool clipRect(float left, float top, float right, float bottom, SkRegion::Op op); - virtual bool clipPath(const SkPath* path, SkRegion::Op op); - virtual bool clipRegion(const SkRegion* region, SkRegion::Op op); - - // Misc - should be implemented with SkPaint inspection - virtual void resetPaintFilter(); - virtual void setupPaintFilter(int clearBits, int setBits); - - bool isCurrentTransformSimple() { - return currentTransform()->isSimple(); - } - -// ---------------------------------------------------------------------------- -// Canvas draw operations -// ---------------------------------------------------------------------------- - virtual status_t drawColor(int color, SkXfermode::Mode mode); - - // Bitmap-based - virtual status_t drawBitmap(const SkBitmap* bitmap, const SkPaint* paint); - virtual status_t drawBitmap(const SkBitmap* bitmap, float srcLeft, float srcTop, - float srcRight, float srcBottom, float dstLeft, float dstTop, - float dstRight, float dstBottom, const SkPaint* paint); - virtual status_t drawBitmapData(const SkBitmap* bitmap, const SkPaint* paint); - virtual status_t drawBitmapMesh(const SkBitmap* bitmap, int meshWidth, int meshHeight, - const float* vertices, const int* colors, const SkPaint* paint); - virtual status_t drawPatch(const SkBitmap* bitmap, const Res_png_9patch* patch, - float left, float top, float right, float bottom, const SkPaint* paint); - - // Shapes - virtual status_t drawRect(float left, float top, float right, float bottom, - const SkPaint* paint); - virtual status_t drawRects(const float* rects, int count, const SkPaint* paint); - virtual status_t drawRoundRect(float left, float top, float right, float bottom, - float rx, float ry, const SkPaint* paint); - virtual status_t drawRoundRect(CanvasPropertyPrimitive* left, CanvasPropertyPrimitive* top, - CanvasPropertyPrimitive* right, CanvasPropertyPrimitive* bottom, - CanvasPropertyPrimitive* rx, CanvasPropertyPrimitive* ry, - CanvasPropertyPaint* paint); - virtual status_t drawCircle(float x, float y, float radius, const SkPaint* paint); - virtual status_t drawCircle(CanvasPropertyPrimitive* x, CanvasPropertyPrimitive* y, - CanvasPropertyPrimitive* radius, CanvasPropertyPaint* paint); - virtual status_t drawOval(float left, float top, float right, float bottom, - const SkPaint* paint); - virtual status_t drawArc(float left, float top, float right, float bottom, - float startAngle, float sweepAngle, bool useCenter, const SkPaint* paint); - virtual status_t drawPath(const SkPath* path, const SkPaint* paint); - virtual status_t drawLines(const float* points, int count, const SkPaint* paint); - virtual status_t drawPoints(const float* points, int count, const SkPaint* paint); - - // Text - virtual status_t drawText(const char* text, int bytesCount, int count, float x, float y, - const float* positions, const SkPaint* paint, float totalAdvance, const Rect& bounds, - DrawOpMode drawOpMode = kDrawOpMode_Immediate); - virtual status_t drawTextOnPath(const char* text, int bytesCount, int count, const SkPath* path, - float hOffset, float vOffset, const SkPaint* paint); - virtual status_t drawPosText(const char* text, int bytesCount, int count, - const float* positions, const SkPaint* paint); - -// ---------------------------------------------------------------------------- -// Canvas draw operations - special -// ---------------------------------------------------------------------------- - virtual status_t drawLayer(DeferredLayerUpdater* layerHandle, float x, float y); - virtual status_t drawRenderNode(RenderNode* renderNode, Rect& dirty, int32_t replayFlags); - - // TODO: rename for consistency - virtual status_t callDrawGLFunction(Functor* functor, Rect& dirty); - - void setHighContrastText(bool highContrastText) { - mHighContrastText = highContrastText; - } -private: - enum DeferredBarrierType { - kBarrier_None, - kBarrier_InOrder, - kBarrier_OutOfOrder, - }; - - void flushRestoreToCount(); - void flushTranslate(); - void flushReorderBarrier(); - - LinearAllocator& alloc() { return mDisplayListData->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); - - - template<class T> - inline const T* refBuffer(const T* srcBuffer, int32_t count) { - if (!srcBuffer) return NULL; - - T* dstBuffer = (T*) mDisplayListData->allocator.alloc(count * sizeof(T)); - memcpy(dstBuffer, srcBuffer, count * sizeof(T)); - return dstBuffer; - } - - inline char* refText(const char* text, size_t byteLength) { - return (char*) refBuffer<uint8_t>((uint8_t*)text, byteLength); - } - - inline const SkPath* refPath(const SkPath* path) { - if (!path) return NULL; - - const SkPath* pathCopy = mPathMap.valueFor(path); - if (pathCopy == NULL || pathCopy->getGenerationID() != path->getGenerationID()) { - SkPath* newPathCopy = new SkPath(*path); - newPathCopy->setSourcePath(path); - - pathCopy = newPathCopy; - // replaceValueFor() performs an add if the entry doesn't exist - mPathMap.replaceValueFor(path, pathCopy); - mDisplayListData->paths.add(pathCopy); - } - if (mDisplayListData->sourcePaths.indexOf(path) < 0) { - mResourceCache.incrementRefcount(path); - mDisplayListData->sourcePaths.add(path); - } - return pathCopy; - } - - inline const SkPaint* refPaint(const SkPaint* paint) { - if (!paint) return NULL; - - const SkPaint* paintCopy = mPaintMap.valueFor(paint); - if (paintCopy == NULL - || paintCopy->getGenerationID() != paint->getGenerationID() - // We can't compare shader pointers because that will always - // change as we do partial copying via wrapping. However, if the - // shader changes the paint generationID will have changed and - // so we don't hit this comparison anyway - || !(paint->getShader() && paintCopy->getShader() - && paint->getShader()->getGenerationID() == paintCopy->getShader()->getGenerationID())) { - paintCopy = copyPaint(paint); - // replaceValueFor() performs an add if the entry doesn't exist - mPaintMap.replaceValueFor(paint, paintCopy); - } - - return paintCopy; - } - - inline SkPaint* copyPaint(const SkPaint* paint) { - if (!paint) return NULL; - SkPaint* paintCopy = new SkPaint(*paint); - if (paint->getShader()) { - SkShader* shaderCopy = SkShader::CreateLocalMatrixShader( - paint->getShader(), paint->getShader()->getLocalMatrix()); - paintCopy->setShader(shaderCopy); - paintCopy->setGenerationID(paint->getGenerationID()); - shaderCopy->setGenerationID(paint->getShader()->getGenerationID()); - shaderCopy->unref(); - } - mDisplayListData->paints.add(paintCopy); - return paintCopy; - } - - inline const SkRegion* refRegion(const SkRegion* region) { - if (!region) { - return region; - } - - const SkRegion* regionCopy = mRegionMap.valueFor(region); - // TODO: Add generation ID to SkRegion - if (regionCopy == NULL) { - regionCopy = new SkRegion(*region); - // replaceValueFor() performs an add if the entry doesn't exist - mRegionMap.replaceValueFor(region, regionCopy); - mDisplayListData->regions.add(regionCopy); - } - - return regionCopy; - } - - 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. - mDisplayListData->bitmapResources.add(bitmap); - mResourceCache.incrementRefcount(bitmap); - return bitmap; - } - - inline const SkBitmap* refBitmapData(const SkBitmap* bitmap) { - mDisplayListData->ownedBitmapResources.add(bitmap); - mResourceCache.incrementRefcount(bitmap); - return bitmap; - } - - inline const Res_png_9patch* refPatch(const Res_png_9patch* patch) { - mDisplayListData->patchResources.add(patch); - mResourceCache.incrementRefcount(patch); - return patch; - } - - DefaultKeyedVector<const SkPaint*, const SkPaint*> mPaintMap; - DefaultKeyedVector<const SkPath*, const SkPath*> mPathMap; - DefaultKeyedVector<const SkRegion*, const SkRegion*> mRegionMap; - - ResourceCache& mResourceCache; - DisplayListData* mDisplayListData; - - float mTranslateX; - float mTranslateY; - bool mHasDeferredTranslate; - DeferredBarrierType mDeferredBarrierType; - bool mHighContrastText; - - int mRestoreSaveCount; - - friend class RenderNode; - -}; // class DisplayListRenderer - -}; // namespace uirenderer -}; // namespace android - -#endif // ANDROID_HWUI_DISPLAY_LIST_RENDERER_H diff --git a/libs/hwui/Dither.cpp b/libs/hwui/Dither.cpp index 77006a4..1ba6511 100644 --- a/libs/hwui/Dither.cpp +++ b/libs/hwui/Dither.cpp @@ -24,15 +24,16 @@ namespace uirenderer { // Lifecycle /////////////////////////////////////////////////////////////////////////////// -Dither::Dither(): mCaches(NULL), mInitialized(false), mDitherTexture(0) { +Dither::Dither(Caches& caches) + : mCaches(caches) + , mInitialized(false) + , mDitherTexture(0) { } void Dither::bindDitherTexture() { if (!mInitialized) { - bool useFloatTexture = Extensions::getInstance().hasFloatTextures(); - glGenTextures(1, &mDitherTexture); - mCaches->bindTexture(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); @@ -40,7 +41,7 @@ void Dither::bindDitherTexture() { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); - if (useFloatTexture) { + 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); @@ -71,13 +72,13 @@ void Dither::bindDitherTexture() { mInitialized = true; } else { - mCaches->bindTexture(mDitherTexture); + mCaches.textureState().bindTexture(mDitherTexture); } } void Dither::clear() { if (mInitialized) { - mCaches->deleteTexture(mDitherTexture); + mCaches.textureState().deleteTexture(mDitherTexture); mInitialized = false; } } @@ -86,15 +87,13 @@ void Dither::clear() { // Program management /////////////////////////////////////////////////////////////////////////////// -void Dither::setupProgram(Program* program, GLuint* textureUnit) { - if (!mCaches) mCaches = &Caches::getInstance(); - +void Dither::setupProgram(Program& program, GLuint* textureUnit) { GLuint textureSlot = (*textureUnit)++; - mCaches->activeTexture(textureSlot); + mCaches.textureState().activateTexture(textureSlot); bindDitherTexture(); - glUniform1i(program->getUniform("ditherSampler"), textureSlot); + glUniform1i(program.getUniform("ditherSampler"), textureSlot); } }; // namespace uirenderer diff --git a/libs/hwui/Dither.h b/libs/hwui/Dither.h index 546236b..b589b80 100644 --- a/libs/hwui/Dither.h +++ b/libs/hwui/Dither.h @@ -19,12 +19,12 @@ #include <GLES3/gl3.h> -#include "Program.h" - namespace android { namespace uirenderer { class Caches; +class Extensions; +class Program; // Must be a power of two #define DITHER_KERNEL_SIZE 4 @@ -37,15 +37,15 @@ class Caches; */ class Dither { public: - Dither(); + Dither(Caches& caches); void clear(); - void setupProgram(Program* program, GLuint* textureUnit); + void setupProgram(Program& program, GLuint* textureUnit); private: void bindDitherTexture(); - Caches* mCaches; + Caches& mCaches; bool mInitialized; GLuint mDitherTexture; }; diff --git a/libs/hwui/DrawProfiler.cpp b/libs/hwui/DrawProfiler.cpp index e590642..ecde5ff 100644 --- a/libs/hwui/DrawProfiler.cpp +++ b/libs/hwui/DrawProfiler.cpp @@ -59,7 +59,7 @@ static int dpToPx(int dp, float density) { DrawProfiler::DrawProfiler() : mType(kNone) , mDensity(0) - , mData(NULL) + , mData(nullptr) , mDataSize(0) , mCurrentFrame(-1) , mPreviousTime(0) @@ -160,7 +160,7 @@ void DrawProfiler::createData() { void DrawProfiler::destroyData() { delete mData; - mData = NULL; + mData = nullptr; } void DrawProfiler::addRect(Rect& r, float data, float* shapeOutput) { diff --git a/libs/hwui/Extensions.cpp b/libs/hwui/Extensions.cpp index 84e7e65..2a82216 100644 --- a/libs/hwui/Extensions.cpp +++ b/libs/hwui/Extensions.cpp @@ -16,18 +16,16 @@ #define LOG_TAG "OpenGLRenderer" -#include <GLES2/gl2.h> -#include <GLES2/gl2ext.h> +#include "Extensions.h" + +#include "Debug.h" +#include "Properties.h" #include <EGL/egl.h> #include <EGL/eglext.h> - +#include <GLES2/gl2ext.h> #include <utils/Log.h> -#include "Debug.h" -#include "Extensions.h" -#include "Properties.h" - namespace android { using namespace uirenderer; @@ -50,14 +48,13 @@ namespace uirenderer { // Constructors /////////////////////////////////////////////////////////////////////////////// -Extensions::Extensions(): Singleton<Extensions>() { +Extensions::Extensions() { // Query GL extensions findExtensions((const char*) glGetString(GL_EXTENSIONS), mGlExtensionList); mHasNPot = hasGlExtension("GL_OES_texture_npot"); mHasFramebufferFetch = hasGlExtension("GL_NV_shader_framebuffer_fetch"); mHasDiscardFramebuffer = hasGlExtension("GL_EXT_discard_framebuffer"); mHasDebugMarker = hasGlExtension("GL_EXT_debug_marker"); - mHasDebugLabel = hasGlExtension("GL_EXT_debug_label"); mHasTiledRendering = hasGlExtension("GL_QCOM_tiled_rendering"); mHas1BitStencil = hasGlExtension("GL_OES_stencil1"); mHas4BitStencil = hasGlExtension("GL_OES_stencil4"); @@ -66,7 +63,7 @@ Extensions::Extensions(): Singleton<Extensions>() { findExtensions(eglQueryString(eglGetCurrentDisplay(), EGL_EXTENSIONS), mEglExtensionList); char property[PROPERTY_VALUE_MAX]; - if (property_get(PROPERTY_DEBUG_NV_PROFILING, property, NULL) > 0) { + if (property_get(PROPERTY_DEBUG_NV_PROFILING, property, nullptr) > 0) { mHasNvSystemTime = !strcmp(property, "true") && hasEglExtension("EGL_NV_system_time"); } else { mHasNvSystemTime = false; @@ -93,9 +90,6 @@ Extensions::Extensions(): Singleton<Extensions>() { } } -Extensions::~Extensions() { -} - /////////////////////////////////////////////////////////////////////////////// // Methods /////////////////////////////////////////////////////////////////////////////// diff --git a/libs/hwui/Extensions.h b/libs/hwui/Extensions.h index 25d4c5e..e7d317d 100644 --- a/libs/hwui/Extensions.h +++ b/libs/hwui/Extensions.h @@ -23,6 +23,8 @@ #include <utils/SortedVector.h> #include <utils/String8.h> +#include <GLES2/gl2.h> + namespace android { namespace uirenderer { @@ -30,13 +32,14 @@ namespace uirenderer { // Classes /////////////////////////////////////////////////////////////////////////////// -class ANDROID_API Extensions: public Singleton<Extensions> { +class ANDROID_API Extensions { public: + Extensions(); + inline bool hasNPot() const { return mHasNPot; } inline bool hasFramebufferFetch() const { return mHasFramebufferFetch; } inline bool hasDiscardFramebuffer() const { return mHasDiscardFramebuffer; } inline bool hasDebugMarker() const { return mHasDebugMarker; } - inline bool hasDebugLabel() const { return mHasDebugLabel; } inline bool hasTiledRendering() const { return mHasTiledRendering; } inline bool has1BitStencil() const { return mHas1BitStencil; } inline bool has4BitStencil() const { return mHas4BitStencil; } @@ -55,13 +58,8 @@ public: void dump() const; private: - Extensions(); - ~Extensions(); - void findExtensions(const char* extensions, SortedVector<String8>& list) const; - friend class Singleton<Extensions>; - SortedVector<String8> mGlExtensionList; SortedVector<String8> mEglExtensionList; @@ -69,7 +67,6 @@ private: bool mHasFramebufferFetch; bool mHasDiscardFramebuffer; bool mHasDebugMarker; - bool mHasDebugLabel; bool mHasTiledRendering; bool mHas1BitStencil; bool mHas4BitStencil; diff --git a/libs/hwui/FboCache.cpp b/libs/hwui/FboCache.cpp index beef7be..b54d532 100644 --- a/libs/hwui/FboCache.cpp +++ b/libs/hwui/FboCache.cpp @@ -31,7 +31,7 @@ namespace uirenderer { FboCache::FboCache(): mMaxSize(DEFAULT_FBO_CACHE_SIZE) { char property[PROPERTY_VALUE_MAX]; - if (property_get(PROPERTY_FBO_CACHE_SIZE, property, NULL) > 0) { + if (property_get(PROPERTY_FBO_CACHE_SIZE, property, nullptr) > 0) { INIT_LOGD(" Setting fbo cache size to %s", property); mMaxSize = atoi(property); } else { diff --git a/libs/hwui/Fence.h b/libs/hwui/Fence.h deleted file mode 100644 index f175e98..0000000 --- a/libs/hwui/Fence.h +++ /dev/null @@ -1,113 +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_FENCE_H -#define ANDROID_HWUI_FENCE_H - -#include <EGL/egl.h> -#include <EGL/eglext.h> - -namespace android { -namespace uirenderer { - -/** - * Creating a Fence instance inserts a new sync fence in the OpenGL - * commands stream. The caller can then wait for the fence to be signaled - * by calling the wait method. - */ -class Fence { -public: - enum { - /** - * Default timeout in nano-seconds for wait() - */ - kDefaultTimeout = 1000000000 - }; - - /** - * Inserts a new sync fence in the OpenGL commands stream. - */ - Fence() { - mDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); - if (mDisplay != EGL_NO_DISPLAY) { - mFence = eglCreateSyncKHR(mDisplay, EGL_SYNC_FENCE_KHR, NULL); - } else { - mFence = EGL_NO_SYNC_KHR; - } - } - - /** - * Destroys the fence. Any caller waiting on the fence will be - * signaled immediately. - */ - ~Fence() { - if (mFence != EGL_NO_SYNC_KHR) { - eglDestroySyncKHR(mDisplay, mFence); - } - } - - /** - * Blocks the calling thread until this fence is signaled, or until - * <timeout> nanoseconds have passed. - * - * Returns true if waiting for the fence was successful, false if - * a timeout or an error occurred. - */ - bool wait(EGLTimeKHR timeout = kDefaultTimeout) { - EGLint waitStatus = eglClientWaitSyncKHR(mDisplay, mFence, - EGL_SYNC_FLUSH_COMMANDS_BIT_KHR, timeout); - if (waitStatus == EGL_FALSE) { - ALOGW("Failed to wait for the fence %#x", eglGetError()); - } - return waitStatus == EGL_CONDITION_SATISFIED_KHR; - } - -private: - EGLDisplay mDisplay; - EGLSyncKHR mFence; - -}; // class Fence - -/** - * An AutoFence creates a Fence instance and waits for the fence - * to be signaled when the AutoFence is destroyed. This is useful - * to automatically wait for a series of OpenGL commands to be - * executed. For example: - * - * void drawAndWait() { - * glDrawElements(); - * AutoFence fence; - * } - */ -class AutoFence { -public: - AutoFence(EGLTimeKHR timeout = Fence::kDefaultTimeout): mTimeout(timeout) { - } - - ~AutoFence() { - mFence.wait(mTimeout); - } - -private: - EGLTimeKHR mTimeout; - Fence mFence; - -}; // class AutoFence - -}; // namespace uirenderer -}; // namespace android - -#endif // ANDROID_HWUI_FENCE_H diff --git a/libs/hwui/FloatColor.h b/libs/hwui/FloatColor.h new file mode 100644 index 0000000..97dec88 --- /dev/null +++ b/libs/hwui/FloatColor.h @@ -0,0 +1,52 @@ +/* + * 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. + */ +#ifndef FLOATCOLOR_H +#define FLOATCOLOR_H + +#include "utils/Macros.h" + +#include <stdint.h> + +namespace android { +namespace uirenderer { + +struct FloatColor { + 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; + } + + bool isNotBlack() { + return a < 1.0f + || r > 0.0f + || g > 0.0f + || b > 0.0f; + } + + float r; + float g; + float b; + float a; +}; + +REQUIRE_COMPATIBLE_LAYOUT(FloatColor); + +} /* namespace uirenderer */ +} /* namespace android */ + +#endif /* FLOATCOLOR_H */ diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp index 3910381..c79ae77 100644 --- a/libs/hwui/FontRenderer.cpp +++ b/libs/hwui/FontRenderer.cpp @@ -14,7 +14,20 @@ * limitations under the License. */ -#define LOG_TAG "OpenGLRenderer" +#include "FontRenderer.h" + +#include "Caches.h" +#include "Debug.h" +#include "Extensions.h" +#include "Glop.h" +#include "GlopBuilder.h" +#include "OpenGLRenderer.h" +#include "PixelBuffer.h" +#include "Rect.h" +#include "renderstate/RenderState.h" +#include "utils/Blur.h" +#include "utils/MathUtils.h" +#include "utils/Timing.h" #include <SkGlyph.h> #include <SkUtils.h> @@ -27,17 +40,6 @@ #include <RenderScript.h> #endif -#include "utils/Blur.h" -#include "utils/Timing.h" - -#include "Caches.h" -#include "Debug.h" -#include "Extensions.h" -#include "FontRenderer.h" -#include "OpenGLRenderer.h" -#include "PixelBuffer.h" -#include "Rect.h" - namespace android { namespace uirenderer { @@ -47,48 +49,23 @@ namespace uirenderer { /////////////////////////////////////////////////////////////////////////////// // TextSetupFunctor /////////////////////////////////////////////////////////////////////////////// -status_t TextSetupFunctor::operator ()(int what, void* data) { - Data* typedData = reinterpret_cast<Data*>(data); - GLenum glyphFormat = typedData ? typedData->glyphFormat : GL_ALPHA; - - renderer->setupDraw(); - renderer->setupDrawTextGamma(paint); - renderer->setupDrawDirtyRegionsDisabled(); - renderer->setupDrawWithTexture(glyphFormat == GL_ALPHA); - switch (glyphFormat) { - case GL_ALPHA: { - renderer->setupDrawAlpha8Color(paint->getColor(), alpha); - break; - } - case GL_RGBA: { - float floatAlpha = alpha / 255.0f; - renderer->setupDrawColor(floatAlpha, floatAlpha, floatAlpha, floatAlpha); - break; - } - default: { -#if DEBUG_FONT_RENDERER - ALOGD("TextSetupFunctor: called with unknown glyph format %x", glyphFormat); -#endif - break; - } - } - renderer->setupDrawColorFilter(paint->getColorFilter()); - renderer->setupDrawShader(paint->getShader()); - renderer->setupDrawBlending(paint); - renderer->setupDrawProgram(); - renderer->setupDrawModelView(kModelViewMode_Translate, false, - 0.0f, 0.0f, 0.0f, 0.0f, pureTranslate); - // Calling setupDrawTexture with the name 0 will enable the - // uv attributes and increase the texture unit count - // texture binding will be performed by the font renderer as - // needed - renderer->setupDrawTexture(0); - renderer->setupDrawPureColorUniforms(); - renderer->setupDrawColorFilterUniforms(paint->getColorFilter()); - renderer->setupDrawShaderUniforms(paint->getShader(), pureTranslate); - renderer->setupDrawTextGammaUniforms(); - - return NO_ERROR; + +void TextDrawFunctor::draw(CacheTexture& texture, bool linearFiltering) { + int textureFillFlags = static_cast<int>(texture.getFormat() == GL_ALPHA + ? TextureFillFlags::kIsAlphaMaskTexture : TextureFillFlags::kNone); + if (linearFiltering) { + textureFillFlags |= TextureFillFlags::kForceFilter; + } + const Matrix4& transform = pureTranslate ? Matrix4::identity() : *(renderer->currentTransform()); + Glop glop; + GlopBuilder(renderer->mRenderState, renderer->mCaches, &glop) + .setMeshTexturedIndexedQuads(texture.mesh(), texture.meshElementCount()) + .setFillTexturePaint(texture.getTexture(), textureFillFlags, paint, renderer->currentSnapshot()->alpha) + .setTransform(renderer->currentSnapshot()->getOrthoMatrix(), transform, false) + .setModelViewOffsetRect(0, 0, Rect(0, 0, 0, 0)) + .setRoundRectClipState(renderer->currentSnapshot()->roundRectClipState) + .build(); + renderer->renderGlop(glop); } /////////////////////////////////////////////////////////////////////////////// @@ -97,47 +74,51 @@ status_t TextSetupFunctor::operator ()(int what, void* data) { static bool sLogFontRendererCreate = true; -FontRenderer::FontRenderer() : - mActiveFonts(LruCache<Font::FontDescription, Font*>::kUnlimitedCapacity) { +FontRenderer::FontRenderer() + : mGammaTable(nullptr) + , mCurrentFont(nullptr) + , mActiveFonts(LruCache<Font::FontDescription, Font*>::kUnlimitedCapacity) + , mCurrentCacheTexture(nullptr) + , mUploadTexture(false) + , mFunctor(nullptr) + , mClip(nullptr) + , mBounds(nullptr) + , mDrawn(false) + , mInitialized(false) + , mLinearFiltering(false) { if (sLogFontRendererCreate) { INIT_LOGD("Creating FontRenderer"); } - mGammaTable = NULL; - mInitialized = false; - - mCurrentCacheTexture = NULL; - - mLinearFiltering = false; - mSmallCacheWidth = DEFAULT_TEXT_SMALL_CACHE_WIDTH; mSmallCacheHeight = DEFAULT_TEXT_SMALL_CACHE_HEIGHT; mLargeCacheWidth = DEFAULT_TEXT_LARGE_CACHE_WIDTH; mLargeCacheHeight = DEFAULT_TEXT_LARGE_CACHE_HEIGHT; char property[PROPERTY_VALUE_MAX]; - if (property_get(PROPERTY_TEXT_SMALL_CACHE_WIDTH, property, NULL) > 0) { + if (property_get(PROPERTY_TEXT_SMALL_CACHE_WIDTH, property, nullptr) > 0) { mSmallCacheWidth = atoi(property); } - if (property_get(PROPERTY_TEXT_SMALL_CACHE_HEIGHT, property, NULL) > 0) { + if (property_get(PROPERTY_TEXT_SMALL_CACHE_HEIGHT, property, nullptr) > 0) { mSmallCacheHeight = atoi(property); } - if (property_get(PROPERTY_TEXT_LARGE_CACHE_WIDTH, property, NULL) > 0) { + if (property_get(PROPERTY_TEXT_LARGE_CACHE_WIDTH, property, nullptr) > 0) { mLargeCacheWidth = atoi(property); } - if (property_get(PROPERTY_TEXT_LARGE_CACHE_HEIGHT, property, NULL) > 0) { + if (property_get(PROPERTY_TEXT_LARGE_CACHE_HEIGHT, property, nullptr) > 0) { mLargeCacheHeight = atoi(property); } uint32_t maxTextureSize = (uint32_t) Caches::getInstance().maxTextureSize; - mSmallCacheWidth = mSmallCacheWidth > maxTextureSize ? maxTextureSize : mSmallCacheWidth; - mSmallCacheHeight = mSmallCacheHeight > maxTextureSize ? maxTextureSize : mSmallCacheHeight; - mLargeCacheWidth = mLargeCacheWidth > maxTextureSize ? maxTextureSize : mLargeCacheWidth; - mLargeCacheHeight = mLargeCacheHeight > maxTextureSize ? maxTextureSize : mLargeCacheHeight; + + mSmallCacheWidth = MathUtils::min(mSmallCacheWidth, maxTextureSize); + mSmallCacheHeight = MathUtils::min(mSmallCacheHeight, maxTextureSize); + mLargeCacheWidth = MathUtils::min(mLargeCacheWidth, maxTextureSize); + mLargeCacheHeight = MathUtils::min(mLargeCacheHeight, maxTextureSize); if (sLogFontRendererCreate) { INIT_LOGD(" Text cache sizes, in pixels: %i x %i, %i x %i, %i x %i, %i x %i", @@ -183,6 +164,8 @@ void FontRenderer::flushAllAndInvalidate() { for (uint32_t i = 0; i < mRGBACacheTextures.size(); i++) { mRGBACacheTextures[i]->init(); } + + mDrawn = false; } void FontRenderer::flushLargeCaches(Vector<CacheTexture*>& cacheTextures) { @@ -195,7 +178,7 @@ void FontRenderer::flushLargeCaches(Vector<CacheTexture*>& cacheTextures) { while (it.next()) { it.value()->invalidateTextureCache(cacheTexture); } - cacheTexture->releaseTexture(); + cacheTexture->releasePixelBuffer(); } } } @@ -213,7 +196,7 @@ CacheTexture* FontRenderer::cacheBitmapInTexture(Vector<CacheTexture*>& cacheTex } } // Could not fit glyph into current cache textures - return NULL; + return nullptr; } void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyph, @@ -224,7 +207,7 @@ void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyp // so we can avoid doing extra work later on if (glyph.fWidth == 0 || glyph.fHeight == 0) { cachedGlyph->mIsValid = true; - cachedGlyph->mCacheTexture = NULL; + cachedGlyph->mCacheTexture = nullptr; return; } @@ -232,7 +215,7 @@ void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyp // choose an appropriate cache texture list for this glyph format SkMask::Format format = static_cast<SkMask::Format>(glyph.fMaskFormat); - Vector<CacheTexture*>* cacheTextures = NULL; + Vector<CacheTexture*>* cacheTextures = nullptr; switch (format) { case SkMask::kA8_Format: case SkMask::kBW_Format: @@ -287,9 +270,9 @@ void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyp uint32_t cacheWidth = cacheTexture->getWidth(); if (!cacheTexture->getPixelBuffer()) { - Caches::getInstance().activeTexture(0); + Caches::getInstance().textureState().activateTexture(0); // Large-glyph texture memory is allocated only as needed - cacheTexture->allocateTexture(); + cacheTexture->allocatePixelBuffer(); } if (!cacheTexture->mesh()) { cacheTexture->allocateMesh(); @@ -358,7 +341,7 @@ void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyp break; } case SkMask::kBW_Format: { - uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0; + uint32_t cacheX = 0, cacheY = 0; uint32_t row = (startY - TEXTURE_BORDER_SIZE) * cacheWidth + startX - TEXTURE_BORDER_SIZE; static const uint8_t COLORS[2] = { 0, 255 }; @@ -397,11 +380,11 @@ void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyp CacheTexture* FontRenderer::createCacheTexture(int width, int height, GLenum format, bool allocate) { - CacheTexture* cacheTexture = new CacheTexture(width, height, format, gMaxNumberOfQuads); + CacheTexture* cacheTexture = new CacheTexture(width, height, format, kMaxNumberOfQuads); if (allocate) { - Caches::getInstance().activeTexture(0); - cacheTexture->allocateTexture(); + Caches::getInstance().textureState().activateTexture(0); + cacheTexture->allocatePixelBuffer(); cacheTexture->allocateMesh(); } @@ -446,8 +429,8 @@ void checkTextureUpdateForCache(Caches& caches, Vector<CacheTexture*>& cacheText if (cacheTexture->isDirty() && cacheTexture->getPixelBuffer()) { if (cacheTexture->getTextureId() != lastTextureId) { lastTextureId = cacheTexture->getTextureId(); - caches.activeTexture(0); - caches.bindTexture(lastTextureId); + caches.textureState().activateTexture(0); + caches.textureState().bindTexture(lastTextureId); } if (cacheTexture->upload()) { @@ -473,7 +456,7 @@ void FontRenderer::checkTextureUpdate() { checkTextureUpdateForCache(caches, mRGBACacheTextures, resetPixelStore, lastTextureId); // Unbind any PBO we might have used to update textures - caches.unbindPixelBuffer(); + caches.pixelBufferState().unbind(); // Reset to default unpack row length to avoid affecting texture // uploads in other parts of the renderer @@ -485,44 +468,23 @@ void FontRenderer::checkTextureUpdate() { } void FontRenderer::issueDrawCommand(Vector<CacheTexture*>& cacheTextures) { - Caches& caches = Caches::getInstance(); + if (!mFunctor) return; + bool first = true; - bool force = false; + bool forceRebind = false; for (uint32_t i = 0; i < cacheTextures.size(); i++) { CacheTexture* texture = cacheTextures[i]; if (texture->canDraw()) { if (first) { - if (mFunctor) { - TextSetupFunctor::Data functorData(texture->getFormat()); - (*mFunctor)(0, &functorData); - } - checkTextureUpdate(); - caches.bindQuadIndicesBuffer(); - - if (!mDrawn) { - // If returns true, a VBO was bound and we must - // rebind our vertex attrib pointers even if - // they have the same values as the current pointers - force = caches.unbindMeshBuffer(); - } - - caches.activeTexture(0); first = false; + mDrawn = true; } - caches.bindTexture(texture->getTextureId()); - texture->setLinearFiltering(mLinearFiltering, false); - - TextureVertex* mesh = texture->mesh(); - caches.bindPositionVertexPointer(force, &mesh[0].x); - caches.bindTexCoordsVertexPointer(force, &mesh[0].u); - force = false; - - glDrawElements(GL_TRIANGLES, texture->meshElementCount(), - GL_UNSIGNED_SHORT, texture->indices()); + mFunctor->draw(*texture, mLinearFiltering); texture->resetMesh(); + forceRebind = false; } } } @@ -530,8 +492,6 @@ void FontRenderer::issueDrawCommand(Vector<CacheTexture*>& cacheTextures) { void FontRenderer::issueDrawCommand() { issueDrawCommand(mACacheTextures); issueDrawCommand(mRGBACacheTextures); - - mDrawn = true; } void FontRenderer::appendMeshQuadNoClip(float x1, float y1, float u1, float v1, @@ -598,7 +558,7 @@ FontRenderer::DropShadow FontRenderer::renderDropShadow(const SkPaint* paint, co DropShadow image; image.width = 0; image.height = 0; - image.image = NULL; + image.image = nullptr; image.penX = 0; image.penY = 0; @@ -607,8 +567,8 @@ FontRenderer::DropShadow FontRenderer::renderDropShadow(const SkPaint* paint, co } mDrawn = false; - mClip = NULL; - mBounds = NULL; + mClip = nullptr; + mBounds = nullptr; Rect bounds; mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds, positions); @@ -644,10 +604,10 @@ FontRenderer::DropShadow FontRenderer::renderDropShadow(const SkPaint* paint, co // NOTE: bounds.isEmpty() can't be used here, since vertical coordinates are inverted // TODO: don't draw pure whitespace in the first place, and avoid needing this check mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY, - Font::BITMAP, dataBuffer, paddedWidth, paddedHeight, NULL, positions); + Font::BITMAP, dataBuffer, paddedWidth, paddedHeight, nullptr, positions); // Unbind any PBO we might have used - Caches::getInstance().unbindPixelBuffer(); + Caches::getInstance().pixelBufferState().unbind(); blurImage(&dataBuffer, paddedWidth, paddedHeight, radius); } @@ -661,7 +621,7 @@ FontRenderer::DropShadow FontRenderer::renderDropShadow(const SkPaint* paint, co return image; } -void FontRenderer::initRender(const Rect* clip, Rect* bounds, Functor* functor) { +void FontRenderer::initRender(const Rect* clip, Rect* bounds, TextDrawFunctor* functor) { checkInit(); mDrawn = false; @@ -671,8 +631,8 @@ void FontRenderer::initRender(const Rect* clip, Rect* bounds, Functor* functor) } void FontRenderer::finishRender() { - mBounds = NULL; - mClip = NULL; + mBounds = nullptr; + mClip = nullptr; issueDrawCommand(); } @@ -689,7 +649,7 @@ void FontRenderer::endPrecaching() { bool FontRenderer::renderPosText(const SkPaint* paint, const Rect* clip, const char *text, uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, - const float* positions, Rect* bounds, Functor* functor, bool forceFinish) { + const float* positions, Rect* bounds, TextDrawFunctor* functor, bool forceFinish) { if (!mCurrentFont) { ALOGE("No font set"); return false; @@ -707,7 +667,7 @@ bool FontRenderer::renderPosText(const SkPaint* paint, const Rect* clip, const c bool FontRenderer::renderTextOnPath(const SkPaint* paint, const Rect* clip, const char *text, uint32_t startIndex, uint32_t len, int numGlyphs, const SkPath* path, - float hOffset, float vOffset, Rect* bounds, Functor* functor) { + float hOffset, float vOffset, Rect* bounds, TextDrawFunctor* functor) { if (!mCurrentFont) { ALOGE("No font set"); return false; @@ -724,7 +684,7 @@ void FontRenderer::removeFont(const Font* font) { mActiveFonts.remove(font->getDescription()); if (mCurrentFont == font) { - mCurrentFont = NULL; + mCurrentFont = nullptr; } } @@ -734,7 +694,7 @@ void FontRenderer::blurImage(uint8_t** image, int32_t width, int32_t height, flo if (width * height * intRadius >= RS_MIN_INPUT_CUTOFF) { uint8_t* outImage = (uint8_t*) memalign(RS_CPU_ALLOCATION_ALIGNMENT, width * height); - if (mRs == 0) { + if (mRs == nullptr) { mRs = new RSC::RS(); // a null path is OK because there are no custom kernels used // hence nothing gets cached by RS @@ -746,7 +706,7 @@ void FontRenderer::blurImage(uint8_t** image, int32_t width, int32_t height, flo mRsScript = RSC::ScriptIntrinsicBlur::create(mRs, mRsElement); } } - if (mRs != 0) { + if (mRs != nullptr) { RSC::sp<const RSC::Type> t = RSC::Type::create(mRs, mRsElement, width, height, 0); RSC::sp<RSC::Allocation> ain = RSC::Allocation::createTyped(mRs, t, RS_ALLOCATION_MIPMAP_NONE, @@ -770,15 +730,12 @@ void FontRenderer::blurImage(uint8_t** image, int32_t width, int32_t height, flo } #endif - float *gaussian = new float[2 * intRadius + 1]; - Blur::generateGaussianWeights(gaussian, intRadius); - - uint8_t* scratch = new uint8_t[width * height]; - Blur::horizontal(gaussian, intRadius, *image, scratch, width, height); - Blur::vertical(gaussian, intRadius, scratch, *image, width, height); + std::unique_ptr<float[]> gaussian(new float[2 * intRadius + 1]); + Blur::generateGaussianWeights(gaussian.get(), intRadius); - delete[] gaussian; - delete[] scratch; + std::unique_ptr<uint8_t[]> scratch(new uint8_t[width * height]); + Blur::horizontal(gaussian.get(), intRadius, *image, scratch.get(), width, height); + Blur::vertical(gaussian.get(), intRadius, scratch.get(), *image, width, height); } static uint32_t calculateCacheSize(const Vector<CacheTexture*>& cacheTextures) { diff --git a/libs/hwui/FontRenderer.h b/libs/hwui/FontRenderer.h index 5c96c6b..dfb107c 100644 --- a/libs/hwui/FontRenderer.h +++ b/libs/hwui/FontRenderer.h @@ -17,7 +17,12 @@ #ifndef ANDROID_HWUI_FONT_RENDERER_H #define ANDROID_HWUI_FONT_RENDERER_H -#include <utils/Functor.h> +#include "font/FontUtil.h" +#include "font/CacheTexture.h" +#include "font/CachedGlyphInfo.h" +#include "font/Font.h" +#include "utils/SortedList.h" + #include <utils/LruCache.h> #include <utils/Vector.h> #include <utils/StrongPointer.h> @@ -26,14 +31,6 @@ #include <GLES2/gl2.h> -#include "font/FontUtil.h" -#include "font/CacheTexture.h" -#include "font/CachedGlyphInfo.h" -#include "font/Font.h" -#include "utils/SortedList.h" -#include "Matrix.h" -#include "Properties.h" - #ifdef ANDROID_ENABLE_RENDERSCRIPT #include "RenderScript.h" namespace RSC { @@ -49,26 +46,20 @@ namespace uirenderer { class OpenGLRenderer; -/////////////////////////////////////////////////////////////////////////////// -// TextSetupFunctor -/////////////////////////////////////////////////////////////////////////////// -class TextSetupFunctor: public Functor { +class TextDrawFunctor { public: - struct Data { - Data(GLenum glyphFormat) : glyphFormat(glyphFormat) { - } - - GLenum glyphFormat; - }; - - TextSetupFunctor(OpenGLRenderer* renderer, float x, float y, bool pureTranslate, - int alpha, SkXfermode::Mode mode, const SkPaint* paint): Functor(), - renderer(renderer), x(x), y(y), pureTranslate(pureTranslate), - alpha(alpha), mode(mode), paint(paint) { + TextDrawFunctor(OpenGLRenderer* renderer, float x, float y, bool pureTranslate, + int alpha, SkXfermode::Mode mode, const SkPaint* paint) + : renderer(renderer) + , x(x) + , y(y) + , pureTranslate(pureTranslate) + , alpha(alpha) + , mode(mode) + , paint(paint) { } - ~TextSetupFunctor() { } - status_t operator ()(int what, void* data); + void draw(CacheTexture& texture, bool linearFiltering); OpenGLRenderer* renderer; float x; @@ -79,10 +70,6 @@ public: const SkPaint* paint; }; -/////////////////////////////////////////////////////////////////////////////// -// FontRenderer -/////////////////////////////////////////////////////////////////////////////// - class FontRenderer { public: FontRenderer(); @@ -103,22 +90,14 @@ public: // bounds is an out parameter bool renderPosText(const SkPaint* paint, const Rect* clip, const char *text, uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, const float* positions, - Rect* bounds, Functor* functor, bool forceFinish = true); + Rect* bounds, TextDrawFunctor* functor, bool forceFinish = true); // bounds is an out parameter bool renderTextOnPath(const SkPaint* paint, const Rect* clip, const char *text, uint32_t startIndex, uint32_t len, int numGlyphs, const SkPath* path, - float hOffset, float vOffset, Rect* bounds, Functor* functor); + float hOffset, float vOffset, Rect* bounds, TextDrawFunctor* functor); struct DropShadow { - DropShadow() { }; - - DropShadow(const DropShadow& dropShadow): - width(dropShadow.width), height(dropShadow.height), - image(dropShadow.image), penX(dropShadow.penX), - penY(dropShadow.penY) { - } - uint32_t width; uint32_t height; uint8_t* image; @@ -154,7 +133,7 @@ private: void flushAllAndInvalidate(); void checkInit(); - void initRender(const Rect* clip, Rect* bounds, Functor* functor); + void initRender(const Rect* clip, Rect* bounds, TextDrawFunctor* functor); void finishRender(); void issueDrawCommand(Vector<CacheTexture*>& cacheTextures); @@ -195,7 +174,7 @@ private: bool mUploadTexture; - Functor* mFunctor; + TextDrawFunctor* mFunctor; const Rect* mClip; Rect* mBounds; bool mDrawn; diff --git a/libs/hwui/FrameInfo.cpp b/libs/hwui/FrameInfo.cpp new file mode 100644 index 0000000..6da1fa8 --- /dev/null +++ b/libs/hwui/FrameInfo.cpp @@ -0,0 +1,28 @@ +/* + * 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 "FrameInfo.h" + +#include <cstring> + +namespace android { +namespace uirenderer { + +void FrameInfo::importUiThreadInfo(int64_t* info) { + memcpy(mFrameInfo, info, UI_THREAD_FRAME_INFO_SIZE * sizeof(int64_t)); +} + +} /* namespace uirenderer */ +} /* namespace android */ diff --git a/libs/hwui/FrameInfo.h b/libs/hwui/FrameInfo.h new file mode 100644 index 0000000..65daf03 --- /dev/null +++ b/libs/hwui/FrameInfo.h @@ -0,0 +1,125 @@ +/* + * 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. + */ +#ifndef FRAMEINFO_H_ +#define FRAMEINFO_H_ + +#include "utils/Macros.h" + +#include <cutils/compiler.h> +#include <utils/Timers.h> + +#include <memory.h> + +namespace android { +namespace uirenderer { + +#define UI_THREAD_FRAME_INFO_SIZE 9 + +enum class FrameInfoIndex { + kFlags = 0, + kIntendedVsync, + kVsync, + kOldestInputEvent, + kNewestInputEvent, + kHandleInputStart, + kAnimationStart, + kPerformTraversalsStart, + kDrawStart, + // End of UI frame info + + kSyncStart, + kIssueDrawCommandsStart, + kSwapBuffers, + kFrameCompleted, + + // Must be the last value! + kNumIndexes +}; + +enum class FrameInfoFlags { + kWindowLayoutChanged = 1 << 0, + kRTAnimation = 1 << 1, + kSurfaceCanvas = 1 << 2, +}; +MAKE_FLAGS_ENUM(FrameInfoFlags) + +class ANDROID_API UiFrameInfoBuilder { +public: + UiFrameInfoBuilder(int64_t* buffer) : mBuffer(buffer) { + memset(mBuffer, 0, UI_THREAD_FRAME_INFO_SIZE * sizeof(int64_t)); + } + + UiFrameInfoBuilder& setVsync(nsecs_t vsyncTime, nsecs_t intendedVsync) { + set(FrameInfoIndex::kVsync) = vsyncTime; + set(FrameInfoIndex::kIntendedVsync) = intendedVsync; + return *this; + } + + UiFrameInfoBuilder& addFlag(FrameInfoFlags flag) { + set(FrameInfoIndex::kFlags) |= static_cast<uint64_t>(flag); + return *this; + } + +private: + inline int64_t& set(FrameInfoIndex index) { + return mBuffer[static_cast<int>(index)]; + } + + int64_t* mBuffer; +}; + +class FrameInfo { +public: + void importUiThreadInfo(int64_t* info); + + void markSyncStart() { + set(FrameInfoIndex::kSyncStart) = systemTime(CLOCK_MONOTONIC); + } + + void markIssueDrawCommandsStart() { + set(FrameInfoIndex::kIssueDrawCommandsStart) = systemTime(CLOCK_MONOTONIC); + } + + void markSwapBuffers() { + set(FrameInfoIndex::kSwapBuffers) = systemTime(CLOCK_MONOTONIC); + } + + void markFrameCompleted() { + set(FrameInfoIndex::kFrameCompleted) = systemTime(CLOCK_MONOTONIC); + } + + int64_t operator[](FrameInfoIndex index) const { + if (index == FrameInfoIndex::kNumIndexes) return 0; + return mFrameInfo[static_cast<int>(index)]; + } + + int64_t operator[](int index) const { + if (index < 0 || index >= static_cast<int>(FrameInfoIndex::kNumIndexes)) return 0; + return mFrameInfo[index]; + } + +private: + inline int64_t& set(FrameInfoIndex index) { + return mFrameInfo[static_cast<int>(index)]; + } + + int64_t mFrameInfo[static_cast<int>(FrameInfoIndex::kNumIndexes)]; +}; + +} /* namespace uirenderer */ +} /* namespace android */ + +#endif /* FRAMEINFO_H_ */ diff --git a/libs/hwui/GammaFontRenderer.cpp b/libs/hwui/GammaFontRenderer.cpp index 06d2aad..0bcd83a 100644 --- a/libs/hwui/GammaFontRenderer.cpp +++ b/libs/hwui/GammaFontRenderer.cpp @@ -14,8 +14,6 @@ * limitations under the License. */ -#define LOG_TAG "OpenGLRenderer" - #include "Debug.h" #include "GammaFontRenderer.h" #include "Properties.h" @@ -61,7 +59,7 @@ GammaFontRenderer::GammaFontRenderer() { // Get the gamma mGamma = DEFAULT_TEXT_GAMMA; - if (property_get(PROPERTY_TEXT_GAMMA, property, NULL) > 0) { + if (property_get(PROPERTY_TEXT_GAMMA, property, nullptr) > 0) { INIT_LOGD(" Setting text gamma to %s", property); mGamma = atof(property); } else { @@ -70,7 +68,7 @@ GammaFontRenderer::GammaFontRenderer() { // Get the black gamma threshold mBlackThreshold = DEFAULT_TEXT_BLACK_GAMMA_THRESHOLD; - if (property_get(PROPERTY_TEXT_BLACK_GAMMA_THRESHOLD, property, NULL) > 0) { + if (property_get(PROPERTY_TEXT_BLACK_GAMMA_THRESHOLD, property, nullptr) > 0) { INIT_LOGD(" Setting text black gamma threshold to %s", property); mBlackThreshold = atoi(property); } else { @@ -80,7 +78,7 @@ GammaFontRenderer::GammaFontRenderer() { // Get the white gamma threshold mWhiteThreshold = DEFAULT_TEXT_WHITE_GAMMA_THRESHOLD; - if (property_get(PROPERTY_TEXT_WHITE_GAMMA_THRESHOLD, property, NULL) > 0) { + if (property_get(PROPERTY_TEXT_WHITE_GAMMA_THRESHOLD, property, nullptr) > 0) { INIT_LOGD(" Setting text white gamma threshold to %s", property); mWhiteThreshold = atoi(property); } else { @@ -96,15 +94,16 @@ GammaFontRenderer::~GammaFontRenderer() { // Shader-based renderer /////////////////////////////////////////////////////////////////////////////// -ShaderGammaFontRenderer::ShaderGammaFontRenderer(bool multiGamma): GammaFontRenderer() { +ShaderGammaFontRenderer::ShaderGammaFontRenderer(bool multiGamma) + : GammaFontRenderer() { INIT_LOGD("Creating shader gamma font renderer"); - mRenderer = NULL; + mRenderer = nullptr; mMultiGamma = multiGamma; } void ShaderGammaFontRenderer::describe(ProgramDescription& description, const SkPaint* paint) const { - if (paint->getShader() == NULL) { + if (paint->getShader() == nullptr) { if (mMultiGamma) { const int l = luminance(paint); @@ -123,9 +122,9 @@ void ShaderGammaFontRenderer::describe(ProgramDescription& description, } void ShaderGammaFontRenderer::setupProgram(ProgramDescription& description, - Program* program) const { + Program& program) const { if (description.hasGammaCorrection) { - glUniform1f(program->getUniform("gamma"), description.gamma); + glUniform1f(program.getUniform("gamma"), description.gamma); } } @@ -139,7 +138,8 @@ void ShaderGammaFontRenderer::endPrecaching() { // Lookup-based renderer /////////////////////////////////////////////////////////////////////////////// -LookupGammaFontRenderer::LookupGammaFontRenderer(): GammaFontRenderer() { +LookupGammaFontRenderer::LookupGammaFontRenderer() + : GammaFontRenderer() { INIT_LOGD("Creating lookup gamma font renderer"); // Compute the gamma tables @@ -149,7 +149,7 @@ LookupGammaFontRenderer::LookupGammaFontRenderer(): GammaFontRenderer() { mGammaTable[i] = uint8_t((float)::floor(pow(i / 255.0f, gamma) * 255.0f + 0.5f)); } - mRenderer = NULL; + mRenderer = nullptr; } void LookupGammaFontRenderer::endPrecaching() { @@ -162,7 +162,8 @@ void LookupGammaFontRenderer::endPrecaching() { // Lookup-based renderer, using 3 different correction tables /////////////////////////////////////////////////////////////////////////////// -Lookup3GammaFontRenderer::Lookup3GammaFontRenderer(): GammaFontRenderer() { +Lookup3GammaFontRenderer::Lookup3GammaFontRenderer() + : GammaFontRenderer() { INIT_LOGD("Creating lookup3 gamma font renderer"); // Compute the gamma tables @@ -183,12 +184,6 @@ Lookup3GammaFontRenderer::Lookup3GammaFontRenderer(): GammaFontRenderer() { memset(mRenderersUsageCount, 0, sizeof(uint32_t) * kGammaCount); } -Lookup3GammaFontRenderer::~Lookup3GammaFontRenderer() { - for (int i = 0; i < kGammaCount; i++) { - delete mRenderers[i]; - } -} - void Lookup3GammaFontRenderer::endPrecaching() { for (int i = 0; i < kGammaCount; i++) { if (mRenderers[i]) { @@ -199,8 +194,7 @@ void Lookup3GammaFontRenderer::endPrecaching() { void Lookup3GammaFontRenderer::clear() { for (int i = 0; i < kGammaCount; i++) { - delete mRenderers[i]; - mRenderers[i] = NULL; + mRenderers[i].release(); } } @@ -221,8 +215,7 @@ void Lookup3GammaFontRenderer::flush() { if (count <= 1 || min < 0) return; - delete mRenderers[min]; - mRenderers[min] = NULL; + mRenderers[min].release(); // Also eliminate the caches for large glyphs, as they consume significant memory for (int i = 0; i < kGammaCount; ++i) { @@ -233,18 +226,16 @@ void Lookup3GammaFontRenderer::flush() { } FontRenderer* Lookup3GammaFontRenderer::getRenderer(Gamma gamma) { - FontRenderer* renderer = mRenderers[gamma]; - if (!renderer) { - renderer = new FontRenderer(); - mRenderers[gamma] = renderer; - renderer->setGammaTable(&mGammaTable[gamma * 256]); + if (!mRenderers[gamma]) { + mRenderers[gamma].reset(new FontRenderer()); + mRenderers[gamma]->setGammaTable(&mGammaTable[gamma * 256]); } mRenderersUsageCount[gamma]++; - return renderer; + return mRenderers[gamma].get(); } FontRenderer& Lookup3GammaFontRenderer::getFontRenderer(const SkPaint* paint) { - if (paint->getShader() == NULL) { + if (paint->getShader() == nullptr) { const int l = luminance(paint); if (l <= mBlackThreshold) { diff --git a/libs/hwui/GammaFontRenderer.h b/libs/hwui/GammaFontRenderer.h index 46cfd04..ca55bf1 100644 --- a/libs/hwui/GammaFontRenderer.h +++ b/libs/hwui/GammaFontRenderer.h @@ -38,7 +38,7 @@ public: virtual uint32_t getFontRendererSize(uint32_t fontRenderer, GLenum format) const = 0; virtual void describe(ProgramDescription& description, const SkPaint* paint) const = 0; - virtual void setupProgram(ProgramDescription& description, Program* program) const = 0; + virtual void setupProgram(ProgramDescription& description, Program& program) const = 0; virtual void endPrecaching() = 0; @@ -59,36 +59,36 @@ public: delete mRenderer; } - void clear() { + void clear() override { delete mRenderer; - mRenderer = NULL; + mRenderer = nullptr; } - void flush() { + void flush() override { if (mRenderer) { mRenderer->flushLargeCaches(); } } - FontRenderer& getFontRenderer(const SkPaint* paint) { + FontRenderer& getFontRenderer(const SkPaint* paint) override { if (!mRenderer) { mRenderer = new FontRenderer; } return *mRenderer; } - uint32_t getFontRendererCount() const { + uint32_t getFontRendererCount() const override { return 1; } - uint32_t getFontRendererSize(uint32_t fontRenderer, GLenum format) const { + uint32_t getFontRendererSize(uint32_t fontRenderer, GLenum format) const override { return mRenderer ? mRenderer->getCacheSize(format) : 0; } - void describe(ProgramDescription& description, const SkPaint* paint) const; - void setupProgram(ProgramDescription& description, Program* program) const; + void describe(ProgramDescription& description, const SkPaint* paint) const override; + void setupProgram(ProgramDescription& description, Program& program) const override; - void endPrecaching(); + void endPrecaching() override; private: ShaderGammaFontRenderer(bool multiGamma); @@ -105,18 +105,18 @@ public: delete mRenderer; } - void clear() { + void clear() override { delete mRenderer; - mRenderer = NULL; + mRenderer = nullptr; } - void flush() { + void flush() override { if (mRenderer) { mRenderer->flushLargeCaches(); } } - FontRenderer& getFontRenderer(const SkPaint* paint) { + FontRenderer& getFontRenderer(const SkPaint* paint) override { if (!mRenderer) { mRenderer = new FontRenderer; mRenderer->setGammaTable(&mGammaTable[0]); @@ -124,21 +124,21 @@ public: return *mRenderer; } - uint32_t getFontRendererCount() const { + uint32_t getFontRendererCount() const override { return 1; } - uint32_t getFontRendererSize(uint32_t fontRenderer, GLenum format) const { + uint32_t getFontRendererSize(uint32_t fontRenderer, GLenum format) const override { return mRenderer ? mRenderer->getCacheSize(format) : 0; } - void describe(ProgramDescription& description, const SkPaint* paint) const { + void describe(ProgramDescription& description, const SkPaint* paint) const override { } - void setupProgram(ProgramDescription& description, Program* program) const { + void setupProgram(ProgramDescription& description, Program& program) const override { } - void endPrecaching(); + void endPrecaching() override; private: LookupGammaFontRenderer(); @@ -151,33 +151,30 @@ private: class Lookup3GammaFontRenderer: public GammaFontRenderer { public: - ~Lookup3GammaFontRenderer(); + void clear() override; + void flush() override; - void clear(); - void flush(); + FontRenderer& getFontRenderer(const SkPaint* paint) override; - FontRenderer& getFontRenderer(const SkPaint* paint); - - uint32_t getFontRendererCount() const { + uint32_t getFontRendererCount() const override { return kGammaCount; } - uint32_t getFontRendererSize(uint32_t fontRenderer, GLenum format) const { + uint32_t getFontRendererSize(uint32_t fontRenderer, GLenum format) const override { if (fontRenderer >= kGammaCount) return 0; - FontRenderer* renderer = mRenderers[fontRenderer]; - if (!renderer) return 0; + if (!mRenderers[fontRenderer]) return 0; - return renderer->getCacheSize(format); + return mRenderers[fontRenderer]->getCacheSize(format); } - void describe(ProgramDescription& description, const SkPaint* paint) const { + void describe(ProgramDescription& description, const SkPaint* paint) const override { } - void setupProgram(ProgramDescription& description, Program* program) const { + void setupProgram(ProgramDescription& description, Program& program) const override { } - void endPrecaching(); + void endPrecaching() override; private: Lookup3GammaFontRenderer(); @@ -192,7 +189,7 @@ private: FontRenderer* getRenderer(Gamma gamma); uint32_t mRenderersUsageCount[kGammaCount]; - FontRenderer* mRenderers[kGammaCount]; + std::unique_ptr<FontRenderer> mRenderers[kGammaCount]; uint8_t mGammaTable[256 * kGammaCount]; diff --git a/libs/hwui/Glop.h b/libs/hwui/Glop.h new file mode 100644 index 0000000..2c6f6c1 --- /dev/null +++ b/libs/hwui/Glop.h @@ -0,0 +1,153 @@ +/* + * 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. + */ + +#ifndef ANDROID_HWUI_GLOP_H +#define ANDROID_HWUI_GLOP_H + +#include "FloatColor.h" +#include "Matrix.h" +#include "Program.h" +#include "Rect.h" +#include "SkiaShader.h" +#include "utils/Macros.h" + +#include <GLES2/gl2.h> +#include <GLES2/gl2ext.h> +#include <SkXfermode.h> + +namespace android { +namespace uirenderer { + +class Program; +class RoundRectClipState; +class Texture; + +/* + * Enumerates optional vertex attributes + * + * Position is always enabled by MeshState, these other attributes + * are enabled/disabled dynamically based on mesh content. + */ +enum class VertexAttribFlags { + kNone = 0, + kTextureCoord = 1 << 0, + kColor = 1 << 1, + kAlpha = 1 << 2, +}; +MAKE_FLAGS_ENUM(VertexAttribFlags) + +/** + * Structure containing all data required to issue an OpenGL draw + * + * Includes all of the mesh, fill, and GL state required to perform + * the operation. Pieces of data are either directly copied into the + * structure, or stored as a pointer or GL object reference to data + * managed. + * + * Eventually, a Glop should be able to be drawn multiple times from + * a single construction, up until GL context destruction. Currently, + * vertex/index/Texture/RoundRectClipState pointers prevent this from + * being safe. + */ +// TODO: PREVENT_COPY_AND_ASSIGN(...) or similar +struct Glop { + struct Mesh { + GLuint primitiveMode; // GL_TRIANGLES and GL_TRIANGLE_STRIP supported + + // buffer object and void* are mutually exclusive. + // Only GL_UNSIGNED_SHORT supported. + struct Indices { + GLuint bufferObject; + const void* indices; + } indices; + + // buffer object and void*s are mutually exclusive. + // TODO: enforce mutual exclusion with restricted setters and/or unions + struct Vertices { + GLuint bufferObject; + int attribFlags; + const void* position; + const void* texCoord; + const void* color; + GLsizei stride; + } vertices; + + int elementCount; + TextureVertex mappedVertices[4]; + } mesh; + + struct Fill { + Program* program; + + struct TextureData { + Texture* texture; + GLenum target; + GLenum filter; + GLenum clamp; + Matrix4* textureTransform; + } texture; + + bool colorEnabled; + FloatColor color; + + ProgramDescription::ColorFilterMode filterMode; + union Filter { + struct Matrix { + float matrix[16]; + float vector[4]; + } matrix; + FloatColor color; + } filter; + + SkiaShaderData skiaShaderData; + } fill; + + struct Transform { + Matrix4 ortho; // TODO: out of op, since this is static per FBO + Matrix4 modelView; + Matrix4 canvas; + bool fudgingOffset; + } transform; + + const RoundRectClipState* roundRectClipState; + + /** + * Blending to be used by this draw - both GL_NONE if blending is disabled. + * + * Defined by fill step, but can be force-enabled by presence of kAlpha_Attrib + */ + struct Blend { + GLenum src; + GLenum dst; + } blend; + + /** + * Bounds of the drawing command in layer space. Only mapped into layer + * space once GlopBuilder::build() is called. + */ + Rect bounds; + + /** + * Additional render state to enumerate: + * - scissor + (bits for whether each of LTRB needed?) + * - stencil mode (draw into, mask, count, etc) + */ +}; + +} /* namespace uirenderer */ +} /* namespace android */ + +#endif // ANDROID_HWUI_GLOP_H diff --git a/libs/hwui/GlopBuilder.cpp b/libs/hwui/GlopBuilder.cpp new file mode 100644 index 0000000..9ca6bc6 --- /dev/null +++ b/libs/hwui/GlopBuilder.cpp @@ -0,0 +1,615 @@ +/* + * 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 "GlopBuilder.h" + +#include "Caches.h" +#include "Glop.h" +#include "Matrix.h" +#include "Patch.h" +#include "renderstate/MeshState.h" +#include "renderstate/RenderState.h" +#include "SkiaShader.h" +#include "Texture.h" +#include "utils/PaintUtils.h" +#include "VertexBuffer.h" + +#include <GLES2/gl2.h> +#include <SkPaint.h> + +namespace android { +namespace uirenderer { + +#define TRIGGER_STAGE(stageFlag) \ + LOG_ALWAYS_FATAL_IF((stageFlag) & mStageFlags, "Stage %d cannot be run twice", (stageFlag)); \ + mStageFlags = static_cast<StageFlags>(mStageFlags | (stageFlag)) + +#define REQUIRE_STAGES(requiredFlags) \ + LOG_ALWAYS_FATAL_IF((mStageFlags & (requiredFlags)) != (requiredFlags), \ + "not prepared for current stage") + +static void setUnitQuadTextureCoords(Rect uvs, TextureVertex* quadVertex) { + quadVertex[0] = {0, 0, uvs.left, uvs.top}; + quadVertex[1] = {1, 0, uvs.right, uvs.top}; + quadVertex[2] = {0, 1, uvs.left, uvs.bottom}; + quadVertex[3] = {1, 1, uvs.right, uvs.bottom}; +} + +GlopBuilder::GlopBuilder(RenderState& renderState, Caches& caches, Glop* outGlop) + : mRenderState(renderState) + , mCaches(caches) + , mShader(nullptr) + , mOutGlop(outGlop) { + mStageFlags = kInitialStage; +} + +//////////////////////////////////////////////////////////////////////////////// +// Mesh +//////////////////////////////////////////////////////////////////////////////// + +GlopBuilder& GlopBuilder::setMeshUnitQuad() { + TRIGGER_STAGE(kMeshStage); + + mOutGlop->mesh.primitiveMode = GL_TRIANGLE_STRIP; + mOutGlop->mesh.indices = { 0, nullptr }; + mOutGlop->mesh.vertices = { + mRenderState.meshState().getUnitQuadVBO(), + static_cast<int>(VertexAttribFlags::kNone), + nullptr, nullptr, nullptr, + kTextureVertexStride }; + mOutGlop->mesh.elementCount = 4; + return *this; +} + +GlopBuilder& GlopBuilder::setMeshTexturedUnitQuad(const UvMapper* uvMapper) { + if (uvMapper) { + // can't use unit quad VBO, so build UV vertices manually + return setMeshTexturedUvQuad(uvMapper, Rect(0, 0, 1, 1)); + } + + TRIGGER_STAGE(kMeshStage); + + mOutGlop->mesh.primitiveMode = GL_TRIANGLE_STRIP; + mOutGlop->mesh.indices = { 0, nullptr }; + mOutGlop->mesh.vertices = { + mRenderState.meshState().getUnitQuadVBO(), + static_cast<int>(VertexAttribFlags::kTextureCoord), + nullptr, (const void*) kMeshTextureOffset, nullptr, + kTextureVertexStride }; + mOutGlop->mesh.elementCount = 4; + return *this; +} + +GlopBuilder& GlopBuilder::setMeshTexturedUvQuad(const UvMapper* uvMapper, Rect uvs) { + TRIGGER_STAGE(kMeshStage); + + if (CC_UNLIKELY(uvMapper)) { + uvMapper->map(uvs); + } + setUnitQuadTextureCoords(uvs, &mOutGlop->mesh.mappedVertices[0]); + + const TextureVertex* textureVertex = mOutGlop->mesh.mappedVertices; + mOutGlop->mesh.primitiveMode = GL_TRIANGLE_STRIP; + mOutGlop->mesh.indices = { 0, nullptr }; + mOutGlop->mesh.vertices = { + 0, + static_cast<int>(VertexAttribFlags::kTextureCoord), + &textureVertex[0].x, &textureVertex[0].u, nullptr, + kTextureVertexStride }; + mOutGlop->mesh.elementCount = 4; + return *this; +} + +GlopBuilder& GlopBuilder::setMeshIndexedQuads(Vertex* vertexData, int quadCount) { + TRIGGER_STAGE(kMeshStage); + + mOutGlop->mesh.primitiveMode = GL_TRIANGLES; + mOutGlop->mesh.indices = { mRenderState.meshState().getQuadListIBO(), nullptr }; + mOutGlop->mesh.vertices = { + 0, + static_cast<int>(VertexAttribFlags::kNone), + vertexData, nullptr, nullptr, + kVertexStride }; + mOutGlop->mesh.elementCount = 6 * quadCount; + return *this; +} + +GlopBuilder& GlopBuilder::setMeshTexturedIndexedQuads(TextureVertex* vertexData, int elementCount) { + TRIGGER_STAGE(kMeshStage); + + mOutGlop->mesh.primitiveMode = GL_TRIANGLES; + mOutGlop->mesh.indices = { mRenderState.meshState().getQuadListIBO(), nullptr }; + mOutGlop->mesh.vertices = { + 0, + static_cast<int>(VertexAttribFlags::kTextureCoord), + &vertexData[0].x, &vertexData[0].u, nullptr, + kTextureVertexStride }; + mOutGlop->mesh.elementCount = elementCount; + 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, + static_cast<int>(VertexAttribFlags::kTextureCoord), + &vertexData[0].x, &vertexData[0].u, nullptr, + kTextureVertexStride }; + mOutGlop->mesh.elementCount = elementCount; + return *this; +} + +GlopBuilder& GlopBuilder::setMeshColoredTexturedMesh(ColorTextureVertex* vertexData, int elementCount) { + TRIGGER_STAGE(kMeshStage); + + mOutGlop->mesh.primitiveMode = GL_TRIANGLES; + mOutGlop->mesh.indices = { 0, nullptr }; + mOutGlop->mesh.vertices = { + 0, + VertexAttribFlags::kTextureCoord | VertexAttribFlags::kColor, + &vertexData[0].x, &vertexData[0].u, &vertexData[0].r, + kColorTextureVertexStride }; + mOutGlop->mesh.elementCount = elementCount; + return *this; +} + +GlopBuilder& GlopBuilder::setMeshVertexBuffer(const VertexBuffer& vertexBuffer, bool shadowInterp) { + TRIGGER_STAGE(kMeshStage); + + const VertexBuffer::MeshFeatureFlags flags = vertexBuffer.getMeshFeatureFlags(); + + bool alphaVertex = flags & VertexBuffer::kAlpha; + bool indices = flags & VertexBuffer::kIndices; + + mOutGlop->mesh.primitiveMode = GL_TRIANGLE_STRIP; + mOutGlop->mesh.indices = { 0, vertexBuffer.getIndices() }; + mOutGlop->mesh.vertices = { + 0, + static_cast<int>(alphaVertex ? VertexAttribFlags::kAlpha : VertexAttribFlags::kNone), + vertexBuffer.getBuffer(), nullptr, nullptr, + alphaVertex ? kAlphaVertexStride : kVertexStride }; + mOutGlop->mesh.elementCount = indices + ? vertexBuffer.getIndexCount() : vertexBuffer.getVertexCount(); + + mDescription.useShadowAlphaInterp = shadowInterp; + return *this; +} + +GlopBuilder& GlopBuilder::setMeshPatchQuads(const Patch& patch) { + TRIGGER_STAGE(kMeshStage); + + mOutGlop->mesh.primitiveMode = GL_TRIANGLES; + mOutGlop->mesh.indices = { mRenderState.meshState().getQuadListIBO(), nullptr }; + mOutGlop->mesh.vertices = { + mCaches.patchCache.getMeshBuffer(), + static_cast<int>(VertexAttribFlags::kTextureCoord), + (void*)patch.positionOffset, (void*)patch.textureOffset, nullptr, + kTextureVertexStride }; + mOutGlop->mesh.elementCount = patch.indexCount; + return *this; +} + +//////////////////////////////////////////////////////////////////////////////// +// Fill +//////////////////////////////////////////////////////////////////////////////// + +void GlopBuilder::setFill(int color, float alphaScale, + SkXfermode::Mode mode, Blend::ModeOrderSwap modeUsage, + const SkShader* shader, const SkColorFilter* colorFilter) { + if (mode != SkXfermode::kClear_Mode) { + float alpha = (SkColorGetA(color) / 255.0f) * alphaScale; + if (!shader) { + float colorScale = alpha / 255.0f; + mOutGlop->fill.color = { + colorScale * SkColorGetR(color), + colorScale * SkColorGetG(color), + colorScale * SkColorGetB(color), + alpha + }; + } else { + mOutGlop->fill.color = { 1, 1, 1, alpha }; + } + } else { + mOutGlop->fill.color = { 0, 0, 0, 1 }; + } + + mOutGlop->blend = { GL_ZERO, GL_ZERO }; + if (mOutGlop->fill.color.a < 1.0f + || (mOutGlop->mesh.vertices.attribFlags & VertexAttribFlags::kAlpha) + || (mOutGlop->fill.texture.texture && mOutGlop->fill.texture.texture->blend) + || mOutGlop->roundRectClipState + || PaintUtils::isBlendedShader(shader) + || PaintUtils::isBlendedColorFilter(colorFilter) + || mode != SkXfermode::kSrcOver_Mode) { + if (CC_LIKELY(mode <= SkXfermode::kScreen_Mode)) { + Blend::getFactors(mode, modeUsage, + &mOutGlop->blend.src, &mOutGlop->blend.dst); + } else { + // These blend modes are not supported by OpenGL directly and have + // to be implemented using shaders. Since the shader will perform + // the blending, don't enable GL blending off here + // If the blend mode cannot be implemented using shaders, fall + // back to the default SrcOver blend mode instead + if (CC_UNLIKELY(mCaches.extensions().hasFramebufferFetch())) { + mDescription.framebufferMode = mode; + mDescription.swapSrcDst = (modeUsage == Blend::ModeOrderSwap::Swap); + // blending in shader, don't enable + } else { + // unsupported + Blend::getFactors(SkXfermode::kSrcOver_Mode, modeUsage, + &mOutGlop->blend.src, &mOutGlop->blend.dst); + } + } + } + mShader = shader; // shader resolved in ::build() + + if (colorFilter) { + SkColor color; + SkXfermode::Mode mode; + SkScalar srcColorMatrix[20]; + if (colorFilter->asColorMode(&color, &mode)) { + mOutGlop->fill.filterMode = mDescription.colorOp = ProgramDescription::kColorBlend; + 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, + }; + } else if (colorFilter->asColorMatrix(srcColorMatrix)) { + mOutGlop->fill.filterMode = mDescription.colorOp = ProgramDescription::kColorMatrix; + + float* colorMatrix = mOutGlop->fill.filter.matrix.matrix; + memcpy(colorMatrix, srcColorMatrix, 4 * sizeof(float)); + memcpy(&colorMatrix[4], &srcColorMatrix[5], 4 * sizeof(float)); + memcpy(&colorMatrix[8], &srcColorMatrix[10], 4 * sizeof(float)); + memcpy(&colorMatrix[12], &srcColorMatrix[15], 4 * sizeof(float)); + + // 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; + } else { + LOG_ALWAYS_FATAL("unsupported ColorFilter"); + } + } else { + mOutGlop->fill.filterMode = ProgramDescription::kColorNone; + } +} + +GlopBuilder& GlopBuilder::setFillTexturePaint(Texture& texture, int textureFillFlags, + const SkPaint* paint, float alphaScale) { + TRIGGER_STAGE(kFillStage); + REQUIRE_STAGES(kMeshStage); + + GLenum filter = (textureFillFlags & TextureFillFlags::kForceFilter) + ? GL_LINEAR : PaintUtils::getFilter(paint); + mOutGlop->fill.texture = { &texture, + GL_TEXTURE_2D, filter, GL_CLAMP_TO_EDGE, nullptr }; + + if (paint) { + int color = paint->getColor(); + SkShader* shader = paint->getShader(); + + if (!(textureFillFlags & TextureFillFlags::kIsAlphaMaskTexture)) { + // Texture defines color, so disable shaders, and reset all non-alpha color channels + color |= 0x00FFFFFF; + shader = nullptr; + } + setFill(color, alphaScale, + PaintUtils::getXfermode(paint->getXfermode()), Blend::ModeOrderSwap::NoSwap, + shader, paint->getColorFilter()); + } else { + mOutGlop->fill.color = { alphaScale, alphaScale, alphaScale, alphaScale }; + + if (alphaScale < 1.0f + || (mOutGlop->mesh.vertices.attribFlags & VertexAttribFlags::kAlpha) + || texture.blend + || mOutGlop->roundRectClipState) { + Blend::getFactors(SkXfermode::kSrcOver_Mode, Blend::ModeOrderSwap::NoSwap, + &mOutGlop->blend.src, &mOutGlop->blend.dst); + } else { + mOutGlop->blend = { GL_ZERO, GL_ZERO }; + } + } + + if (textureFillFlags & TextureFillFlags::kIsAlphaMaskTexture) { + mDescription.modulate = mOutGlop->fill.color.isNotBlack(); + mDescription.hasAlpha8Texture = true; + } else { + mDescription.modulate = mOutGlop->fill.color.a < 1.0f; + } + return *this; +} + +GlopBuilder& GlopBuilder::setFillPaint(const SkPaint& paint, float alphaScale) { + TRIGGER_STAGE(kFillStage); + REQUIRE_STAGES(kMeshStage); + + mOutGlop->fill.texture = { nullptr, GL_INVALID_ENUM, GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr }; + + setFill(paint.getColor(), alphaScale, + PaintUtils::getXfermode(paint.getXfermode()), Blend::ModeOrderSwap::NoSwap, + paint.getShader(), paint.getColorFilter()); + mDescription.modulate = mOutGlop->fill.color.a < 1.0f; + return *this; +} + +GlopBuilder& GlopBuilder::setFillPathTexturePaint(PathTexture& texture, + const SkPaint& paint, float alphaScale) { + TRIGGER_STAGE(kFillStage); + REQUIRE_STAGES(kMeshStage); + + //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 }; + + setFill(paint.getColor(), alphaScale, + PaintUtils::getXfermode(paint.getXfermode()), Blend::ModeOrderSwap::NoSwap, + paint.getShader(), paint.getColorFilter()); + + mDescription.hasAlpha8Texture = true; + mDescription.modulate = mOutGlop->fill.color.isNotBlack(); + return *this; +} + +GlopBuilder& GlopBuilder::setFillShadowTexturePaint(ShadowTexture& texture, int shadowColor, + const SkPaint& paint, float alphaScale) { + TRIGGER_STAGE(kFillStage); + REQUIRE_STAGES(kMeshStage); + + //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 }; + + const int ALPHA_BITMASK = SK_ColorBLACK; + const int COLOR_BITMASK = ~ALPHA_BITMASK; + if ((shadowColor & ALPHA_BITMASK) == ALPHA_BITMASK) { + // shadow color is fully opaque: override its alpha with that of paint + shadowColor &= paint.getColor() | COLOR_BITMASK; + } + + setFill(shadowColor, alphaScale, + PaintUtils::getXfermode(paint.getXfermode()), Blend::ModeOrderSwap::NoSwap, + paint.getShader(), paint.getColorFilter()); + + mDescription.hasAlpha8Texture = true; + mDescription.modulate = mOutGlop->fill.color.isNotBlack(); + return *this; +} + +GlopBuilder& GlopBuilder::setFillBlack() { + TRIGGER_STAGE(kFillStage); + REQUIRE_STAGES(kMeshStage); + + 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, + nullptr, nullptr); + return *this; +} + +GlopBuilder& GlopBuilder::setFillClear() { + TRIGGER_STAGE(kFillStage); + REQUIRE_STAGES(kMeshStage); + + 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, + nullptr, nullptr); + return *this; +} + +GlopBuilder& GlopBuilder::setFillLayer(Texture& texture, const SkColorFilter* colorFilter, + float alpha, SkXfermode::Mode mode, Blend::ModeOrderSwap modeUsage) { + TRIGGER_STAGE(kFillStage); + REQUIRE_STAGES(kMeshStage); + + mOutGlop->fill.texture = { &texture, + GL_TEXTURE_2D, GL_LINEAR, GL_CLAMP_TO_EDGE, nullptr }; + mOutGlop->fill.color = { alpha, alpha, alpha, alpha }; + + setFill(SK_ColorWHITE, alpha, mode, modeUsage, nullptr, colorFilter); + + mDescription.modulate = mOutGlop->fill.color.a < 1.0f; + return *this; +} + +GlopBuilder& GlopBuilder::setFillTextureLayer(Layer& layer, float alpha) { + TRIGGER_STAGE(kFillStage); + REQUIRE_STAGES(kMeshStage); + + mOutGlop->fill.texture = { &(layer.getTexture()), + layer.getRenderTarget(), GL_LINEAR, GL_CLAMP_TO_EDGE, &layer.getTexTransform() }; + mOutGlop->fill.color = { alpha, alpha, alpha, alpha }; + + setFill(SK_ColorWHITE, alpha, layer.getMode(), Blend::ModeOrderSwap::NoSwap, + nullptr, layer.getColorFilter()); + + mDescription.modulate = mOutGlop->fill.color.a < 1.0f; + mDescription.hasTextureTransform = true; + return *this; +} + +//////////////////////////////////////////////////////////////////////////////// +// Transform +//////////////////////////////////////////////////////////////////////////////// + +GlopBuilder& GlopBuilder::setTransform(const Matrix4& ortho, + const Matrix4& transform, bool fudgingOffset) { + TRIGGER_STAGE(kTransformStage); + + mOutGlop->transform.ortho.load(ortho); + mOutGlop->transform.canvas.load(transform); + mOutGlop->transform.fudgingOffset = fudgingOffset; + return *this; +} + +//////////////////////////////////////////////////////////////////////////////// +// ModelView +//////////////////////////////////////////////////////////////////////////////// + +GlopBuilder& GlopBuilder::setModelViewMapUnitToRect(const Rect destination) { + TRIGGER_STAGE(kModelViewStage); + + mOutGlop->transform.modelView.loadTranslate(destination.left, destination.top, 0.0f); + mOutGlop->transform.modelView.scale(destination.getWidth(), destination.getHeight(), 1.0f); + mOutGlop->bounds = destination; + return *this; +} + +GlopBuilder& GlopBuilder::setModelViewMapUnitToRectSnap(const Rect destination) { + TRIGGER_STAGE(kModelViewStage); + REQUIRE_STAGES(kTransformStage | kFillStage); + + float left = destination.left; + float top = destination.top; + + const Matrix4& canvasTransform = mOutGlop->transform.canvas; + if (CC_LIKELY(canvasTransform.isPureTranslate())) { + // snap by adjusting the model view matrix + const float translateX = canvasTransform.getTranslateX(); + const float translateY = canvasTransform.getTranslateY(); + + left = (int) floorf(left + translateX + 0.5f) - translateX; + top = (int) floorf(top + translateY + 0.5f) - translateY; + mOutGlop->fill.texture.filter = GL_NEAREST; + } + + mOutGlop->transform.modelView.loadTranslate(left, top, 0.0f); + mOutGlop->transform.modelView.scale(destination.getWidth(), destination.getHeight(), 1.0f); + mOutGlop->bounds = destination; + return *this; +} + +GlopBuilder& GlopBuilder::setModelViewOffsetRect(float offsetX, float offsetY, const Rect source) { + TRIGGER_STAGE(kModelViewStage); + + mOutGlop->transform.modelView.loadTranslate(offsetX, offsetY, 0.0f); + mOutGlop->bounds = source; + mOutGlop->bounds.translate(offsetX, offsetY); + return *this; +} + +GlopBuilder& GlopBuilder::setModelViewOffsetRectSnap(float offsetX, float offsetY, const Rect source) { + TRIGGER_STAGE(kModelViewStage); + REQUIRE_STAGES(kTransformStage | kFillStage); + + const Matrix4& canvasTransform = mOutGlop->transform.canvas; + if (CC_LIKELY(canvasTransform.isPureTranslate())) { + // snap by adjusting the model view matrix + const float translateX = canvasTransform.getTranslateX(); + const float translateY = canvasTransform.getTranslateY(); + + offsetX = (int) floorf(offsetX + translateX + source.left + 0.5f) - translateX - source.left; + offsetY = (int) floorf(offsetY + translateY + source.top + 0.5f) - translateY - source.top; + mOutGlop->fill.texture.filter = GL_NEAREST; + } + + mOutGlop->transform.modelView.loadTranslate(offsetX, offsetY, 0.0f); + mOutGlop->bounds = source; + mOutGlop->bounds.translate(offsetX, offsetY); + return *this; +} + +//////////////////////////////////////////////////////////////////////////////// +// RoundRectClip +//////////////////////////////////////////////////////////////////////////////// + +GlopBuilder& GlopBuilder::setRoundRectClipState(const RoundRectClipState* roundRectClipState) { + TRIGGER_STAGE(kRoundRectClipStage); + + mOutGlop->roundRectClipState = roundRectClipState; + mDescription.hasRoundRectClip = roundRectClipState != nullptr; + return *this; +} + +//////////////////////////////////////////////////////////////////////////////// +// Build +//////////////////////////////////////////////////////////////////////////////// + +void verify(const ProgramDescription& description, const Glop& glop) { + if (glop.fill.texture.texture != nullptr) { + LOG_ALWAYS_FATAL_IF(((description.hasTexture && description.hasExternalTexture) + || (!description.hasTexture && !description.hasExternalTexture) + || ((glop.mesh.vertices.attribFlags & VertexAttribFlags::kTextureCoord) == 0)), + "Texture %p, hT%d, hET %d, attribFlags %x", + glop.fill.texture.texture, + description.hasTexture, description.hasExternalTexture, + glop.mesh.vertices.attribFlags); + } else { + LOG_ALWAYS_FATAL_IF((description.hasTexture + || description.hasExternalTexture + || ((glop.mesh.vertices.attribFlags & VertexAttribFlags::kTextureCoord) != 0)), + "No texture, hT%d, hET %d, attribFlags %x", + description.hasTexture, description.hasExternalTexture, + glop.mesh.vertices.attribFlags); + } + + if ((glop.mesh.vertices.attribFlags & VertexAttribFlags::kAlpha) + && glop.mesh.vertices.bufferObject) { + LOG_ALWAYS_FATAL("VBO and alpha attributes are not currently compatible"); + } + + if (description.hasTextureTransform != (glop.fill.texture.textureTransform != nullptr)) { + LOG_ALWAYS_FATAL("Texture transform incorrectly specified"); + } +} + +void GlopBuilder::build() { + REQUIRE_STAGES(kAllStages); + if (mOutGlop->mesh.vertices.attribFlags & VertexAttribFlags::kTextureCoord) { + if (mOutGlop->fill.texture.target == GL_TEXTURE_2D) { + mDescription.hasTexture = true; + } else { + mDescription.hasExternalTexture = true; + } + + } + mDescription.hasColors = mOutGlop->mesh.vertices.attribFlags & VertexAttribFlags::kColor; + mDescription.hasVertexAlpha = mOutGlop->mesh.vertices.attribFlags & VertexAttribFlags::kAlpha; + + // Enable debug highlight when what we're about to draw is tested against + // the stencil buffer and if stencil highlight debugging is on + mDescription.hasDebugHighlight = !mCaches.debugOverdraw + && mCaches.debugStencilClip == Caches::kStencilShowHighlight + && mRenderState.stencil().isTestEnabled(); + + // serialize shader info into ShaderData + GLuint textureUnit = mOutGlop->fill.texture.texture ? 1 : 0; + SkiaShader::store(mCaches, mShader, mOutGlop->transform.modelView, + &textureUnit, &mDescription, &(mOutGlop->fill.skiaShaderData)); + + // duplicates ProgramCache's definition of color uniform presence + const bool singleColor = !mDescription.hasTexture + && !mDescription.hasExternalTexture + && !mDescription.hasGradient + && !mDescription.hasBitmap; + mOutGlop->fill.colorEnabled = mDescription.modulate || singleColor; + + verify(mDescription, *mOutGlop); + + // Final step: populate program and map bounds into render target space + mOutGlop->fill.program = mCaches.programCache.get(mDescription); + mOutGlop->transform.canvas.mapRect(mOutGlop->bounds); +} + +} /* namespace uirenderer */ +} /* namespace android */ diff --git a/libs/hwui/GlopBuilder.h b/libs/hwui/GlopBuilder.h new file mode 100644 index 0000000..b335a2c --- /dev/null +++ b/libs/hwui/GlopBuilder.h @@ -0,0 +1,122 @@ +/* + * 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. + */ +#ifndef RENDERSTATE_GLOPBUILDER_H +#define RENDERSTATE_GLOPBUILDER_H + +#include "OpenGLRenderer.h" +#include "Program.h" +#include "renderstate/Blend.h" +#include "utils/Macros.h" + +class SkPaint; +class SkShader; + +namespace android { +namespace uirenderer { + +class Caches; +class Matrix4; +class RenderState; +class Texture; +class VertexBuffer; +struct Glop; + +enum class TextureFillFlags { + kNone = 0, + kIsAlphaMaskTexture = 1 << 0, + kForceFilter = 1 << 1, +}; +MAKE_FLAGS_ENUM(TextureFillFlags); + +class GlopBuilder { + PREVENT_COPY_AND_ASSIGN(GlopBuilder); +public: + GlopBuilder(RenderState& renderState, Caches& caches, Glop* outGlop); + + GlopBuilder& setMeshUnitQuad(); + GlopBuilder& setMeshTexturedUnitQuad(const UvMapper* uvMapper); + GlopBuilder& setMeshTexturedUvQuad(const UvMapper* uvMapper, const Rect uvs); + GlopBuilder& setMeshVertexBuffer(const VertexBuffer& vertexBuffer, bool shadowInterp); + GlopBuilder& setMeshIndexedQuads(Vertex* vertexData, int quadCount); + GlopBuilder& setMeshTexturedMesh(TextureVertex* vertexData, int elementCount); // TODO: use indexed quads + GlopBuilder& setMeshColoredTexturedMesh(ColorTextureVertex* vertexData, int elementCount); // TODO: use indexed quads + GlopBuilder& setMeshTexturedIndexedQuads(TextureVertex* vertexData, int elementCount); // TODO: take quadCount + GlopBuilder& setMeshPatchQuads(const Patch& patch); + + GlopBuilder& setFillPaint(const SkPaint& paint, float alphaScale); + GlopBuilder& setFillTexturePaint(Texture& texture, int textureFillFlags, + const SkPaint* paint, float alphaScale); + GlopBuilder& setFillPathTexturePaint(PathTexture& texture, + const SkPaint& paint, float alphaScale); + GlopBuilder& setFillShadowTexturePaint(ShadowTexture& texture, int shadowColor, + const SkPaint& paint, float alphaScale); + GlopBuilder& setFillBlack(); + GlopBuilder& setFillClear(); + GlopBuilder& setFillLayer(Texture& texture, const SkColorFilter* colorFilter, + float alpha, SkXfermode::Mode mode, Blend::ModeOrderSwap modeUsage); + GlopBuilder& setFillTextureLayer(Layer& layer, float alpha); + + GlopBuilder& setTransform(const Matrix4& ortho, const Matrix4& transform, bool fudgingOffset); + + GlopBuilder& setModelViewMapUnitToRect(const Rect destination); + GlopBuilder& setModelViewMapUnitToRectSnap(const Rect destination); + GlopBuilder& setModelViewMapUnitToRectOptionalSnap(bool snap, const Rect& destination) { + if (snap) { + return setModelViewMapUnitToRectSnap(destination); + } else { + return setModelViewMapUnitToRect(destination); + } + } + GlopBuilder& setModelViewOffsetRect(float offsetX, float offsetY, const Rect source); + GlopBuilder& setModelViewOffsetRectSnap(float offsetX, float offsetY, const Rect source); + GlopBuilder& setModelViewOffsetRectOptionalSnap(bool snap, + float offsetX, float offsetY, const Rect& source) { + if (snap) { + return setModelViewOffsetRectSnap(offsetX, offsetY, source); + } else { + return setModelViewOffsetRect(offsetX, offsetY, source); + } + } + + GlopBuilder& setRoundRectClipState(const RoundRectClipState* roundRectClipState); + + void build(); +private: + void setFill(int color, float alphaScale, + SkXfermode::Mode mode, Blend::ModeOrderSwap modeUsage, + const SkShader* shader, const SkColorFilter* colorFilter); + + enum StageFlags { + kInitialStage = 0, + kMeshStage = 1 << 0, + kTransformStage = 1 << 1, + kModelViewStage = 1 << 2, + kFillStage = 1 << 3, + kRoundRectClipStage = 1 << 4, + kAllStages = kMeshStage | kFillStage | kTransformStage | kModelViewStage | kRoundRectClipStage, + } mStageFlags; + + ProgramDescription mDescription; + RenderState& mRenderState; + Caches& mCaches; + const SkShader* mShader; + Glop* mOutGlop; +}; + +} /* namespace uirenderer */ +} /* namespace android */ + +#endif // RENDERSTATE_GLOPBUILDER_H diff --git a/libs/hwui/GradientCache.cpp b/libs/hwui/GradientCache.cpp index ffd1e8c..ea93e7f 100644 --- a/libs/hwui/GradientCache.cpp +++ b/libs/hwui/GradientCache.cpp @@ -52,21 +52,24 @@ int GradientCacheEntry::compare(const GradientCacheEntry& lhs, const GradientCac int deltaInt = int(lhs.count) - int(rhs.count); if (deltaInt != 0) return deltaInt; - deltaInt = memcmp(lhs.colors, rhs.colors, lhs.count * sizeof(uint32_t)); + deltaInt = memcmp(lhs.colors.get(), rhs.colors.get(), lhs.count * sizeof(uint32_t)); if (deltaInt != 0) return deltaInt; - return memcmp(lhs.positions, rhs.positions, lhs.count * sizeof(float)); + return memcmp(lhs.positions.get(), rhs.positions.get(), lhs.count * sizeof(float)); } /////////////////////////////////////////////////////////////////////////////// // Constructors/destructor /////////////////////////////////////////////////////////////////////////////// -GradientCache::GradientCache(): - mCache(LruCache<GradientCacheEntry, Texture*>::kUnlimitedCapacity), - mSize(0), mMaxSize(MB(DEFAULT_GRADIENT_CACHE_SIZE)) { +GradientCache::GradientCache(Extensions& extensions) + : mCache(LruCache<GradientCacheEntry, Texture*>::kUnlimitedCapacity) + , mSize(0) + , mMaxSize(MB(DEFAULT_GRADIENT_CACHE_SIZE)) + , mUseFloatTexture(extensions.hasFloatTextures()) + , mHasNpot(extensions.hasNPot()){ char property[PROPERTY_VALUE_MAX]; - if (property_get(PROPERTY_GRADIENT_CACHE_SIZE, property, NULL) > 0) { + if (property_get(PROPERTY_GRADIENT_CACHE_SIZE, property, nullptr) > 0) { INIT_LOGD(" Setting gradient cache size to %sMB", property); setMaxSize(MB(atof(property))); } else { @@ -76,16 +79,6 @@ GradientCache::GradientCache(): glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize); mCache.setOnEntryRemovedListener(this); - - const Extensions& extensions = Extensions::getInstance(); - mUseFloatTexture = extensions.hasFloatTextures(); - mHasNpot = extensions.hasNPot(); -} - -GradientCache::GradientCache(uint32_t maxByteSize): - mCache(LruCache<GradientCacheEntry, Texture*>::kUnlimitedCapacity), - mSize(0), mMaxSize(maxByteSize) { - mCache.setOnEntryRemovedListener(this); } GradientCache::~GradientCache() { @@ -173,7 +166,7 @@ Texture* GradientCache::addLinearGradient(GradientCacheEntry& gradient, GradientInfo info; getGradientInfo(colors, count, info); - Texture* texture = new Texture(); + Texture* texture = new Texture(Caches::getInstance()); texture->width = info.width; texture->height = 2; texture->blend = info.hasAlpha; @@ -285,7 +278,7 @@ void GradientCache::generateTexture(uint32_t* colors, float* positions, Texture* memcpy(pixels + rowBytes, pixels, rowBytes); glGenTextures(1, &texture->id); - Caches::getInstance().bindTexture(texture->id); + Caches::getInstance().textureState().bindTexture(texture->id); glPixelStorei(GL_UNPACK_ALIGNMENT, 4); if (mUseFloatTexture) { diff --git a/libs/hwui/GradientCache.h b/libs/hwui/GradientCache.h index 6a783b1..08319ea 100644 --- a/libs/hwui/GradientCache.h +++ b/libs/hwui/GradientCache.h @@ -17,6 +17,8 @@ #ifndef ANDROID_HWUI_GRADIENT_CACHE_H #define ANDROID_HWUI_GRADIENT_CACHE_H +#include <memory> + #include <GLES3/gl3.h> #include <SkShader.h> @@ -25,16 +27,16 @@ #include <utils/Mutex.h> #include <utils/Vector.h> -#include "Texture.h" - namespace android { namespace uirenderer { +class Texture; + struct GradientCacheEntry { GradientCacheEntry() { count = 0; - colors = NULL; - positions = NULL; + colors = nullptr; + positions = nullptr; } GradientCacheEntry(uint32_t* colors, float* positions, uint32_t count) { @@ -42,20 +44,12 @@ struct GradientCacheEntry { } GradientCacheEntry(const GradientCacheEntry& entry) { - copy(entry.colors, entry.positions, entry.count); - } - - ~GradientCacheEntry() { - delete[] colors; - delete[] positions; + copy(entry.colors.get(), entry.positions.get(), entry.count); } GradientCacheEntry& operator=(const GradientCacheEntry& entry) { if (this != &entry) { - delete[] colors; - delete[] positions; - - copy(entry.colors, entry.positions, entry.count); + copy(entry.colors.get(), entry.positions.get(), entry.count); } return *this; @@ -73,18 +67,18 @@ struct GradientCacheEntry { return compare(*this, other) != 0; } - uint32_t* colors; - float* positions; + std::unique_ptr<uint32_t[]> colors; + std::unique_ptr<float[]> positions; uint32_t count; private: void copy(uint32_t* colors, float* positions, uint32_t count) { this->count = count; - this->colors = new uint32_t[count]; - this->positions = new float[count]; + this->colors.reset(new uint32_t[count]); + this->positions.reset(new float[count]); - memcpy(this->colors, colors, count * sizeof(uint32_t)); - memcpy(this->positions, positions, count * sizeof(float)); + memcpy(this->colors.get(), colors, count * sizeof(uint32_t)); + memcpy(this->positions.get(), positions, count * sizeof(float)); } }; // GradientCacheEntry @@ -110,15 +104,14 @@ inline hash_t hash_type(const GradientCacheEntry& entry) { */ class GradientCache: public OnEntryRemoved<GradientCacheEntry, Texture*> { public: - GradientCache(); - GradientCache(uint32_t maxByteSize); + GradientCache(Extensions& extensions); ~GradientCache(); /** * Used as a callback when an entry is removed from the cache. * Do not invoke directly. */ - void operator()(GradientCacheEntry& shader, Texture*& texture); + void operator()(GradientCacheEntry& shader, Texture*& texture) override; /** * Returns the texture associated with the specified shader. diff --git a/libs/hwui/Image.cpp b/libs/hwui/Image.cpp index edf3930..a31c546 100644 --- a/libs/hwui/Image.cpp +++ b/libs/hwui/Image.cpp @@ -39,7 +39,7 @@ Image::Image(sp<GraphicBuffer> buffer) { } else { // Create a 2D texture to sample from the EGLImage glGenTextures(1, &mTexture); - Caches::getInstance().bindTexture(mTexture); + Caches::getInstance().textureState().bindTexture(mTexture); glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, mImage); GLenum status = GL_NO_ERROR; @@ -54,7 +54,7 @@ Image::~Image() { eglDestroyImageKHR(eglGetDisplay(EGL_DEFAULT_DISPLAY), mImage); mImage = EGL_NO_IMAGE_KHR; - Caches::getInstance().deleteTexture(mTexture); + Caches::getInstance().textureState().deleteTexture(mTexture); mTexture = 0; } } diff --git a/libs/hwui/Interpolator.cpp b/libs/hwui/Interpolator.cpp index ff8ff73..e1b0fc3 100644 --- a/libs/hwui/Interpolator.cpp +++ b/libs/hwui/Interpolator.cpp @@ -14,8 +14,6 @@ * limitations under the License. */ -#define LOG_TAG "Interpolator" - #include "Interpolator.h" #include <cmath> @@ -90,14 +88,12 @@ float OvershootInterpolator::interpolate(float t) { return t * t * ((mTension + 1) * t + mTension) + 1.0f; } -LUTInterpolator::LUTInterpolator(float* values, size_t size) { - mValues = values; - mSize = size; +LUTInterpolator::LUTInterpolator(float* values, size_t size) + : mValues(values) + , mSize(size) { } LUTInterpolator::~LUTInterpolator() { - delete mValues; - mValues = 0; } float LUTInterpolator::interpolate(float input) { @@ -114,7 +110,7 @@ float LUTInterpolator::interpolate(float input) { LOG_ALWAYS_FATAL_IF(i1 < 0 || i2 < 0, "negatives in interpolation!" " i1=%d, i2=%d, input=%f, lutpos=%f, size=%zu, values=%p, ipart=%f, weight=%f", - i1, i2, input, lutpos, mSize, mValues, ipart, weight); + i1, i2, input, lutpos, mSize, mValues.get(), ipart, weight); float v1 = mValues[i1]; float v2 = mValues[i2]; diff --git a/libs/hwui/Interpolator.h b/libs/hwui/Interpolator.h index dfa0a85..66ce119 100644 --- a/libs/hwui/Interpolator.h +++ b/libs/hwui/Interpolator.h @@ -17,6 +17,7 @@ #define INTERPOLATOR_H #include <stddef.h> +#include <memory> #include <cutils/compiler.h> @@ -37,13 +38,13 @@ protected: class ANDROID_API AccelerateDecelerateInterpolator : public Interpolator { public: - virtual float interpolate(float input); + virtual float interpolate(float input) override; }; class ANDROID_API AccelerateInterpolator : public Interpolator { public: AccelerateInterpolator(float factor) : mFactor(factor), mDoubleFactor(factor*2) {} - virtual float interpolate(float input); + virtual float interpolate(float input) override; private: const float mFactor; const float mDoubleFactor; @@ -52,7 +53,7 @@ private: class ANDROID_API AnticipateInterpolator : public Interpolator { public: AnticipateInterpolator(float tension) : mTension(tension) {} - virtual float interpolate(float input); + virtual float interpolate(float input) override; private: const float mTension; }; @@ -60,20 +61,20 @@ private: class ANDROID_API AnticipateOvershootInterpolator : public Interpolator { public: AnticipateOvershootInterpolator(float tension) : mTension(tension) {} - virtual float interpolate(float input); + virtual float interpolate(float input) override; private: const float mTension; }; class ANDROID_API BounceInterpolator : public Interpolator { public: - virtual float interpolate(float input); + virtual float interpolate(float input) override; }; class ANDROID_API CycleInterpolator : public Interpolator { public: CycleInterpolator(float cycles) : mCycles(cycles) {} - virtual float interpolate(float input); + virtual float interpolate(float input) override; private: const float mCycles; }; @@ -81,20 +82,20 @@ private: class ANDROID_API DecelerateInterpolator : public Interpolator { public: DecelerateInterpolator(float factor) : mFactor(factor) {} - virtual float interpolate(float input); + virtual float interpolate(float input) override; private: const float mFactor; }; class ANDROID_API LinearInterpolator : public Interpolator { public: - virtual float interpolate(float input) { return input; } + virtual float interpolate(float input) override { return input; } }; class ANDROID_API OvershootInterpolator : public Interpolator { public: OvershootInterpolator(float tension) : mTension(tension) {} - virtual float interpolate(float input); + virtual float interpolate(float input) override; private: const float mTension; }; @@ -104,10 +105,10 @@ public: LUTInterpolator(float* values, size_t size); ~LUTInterpolator(); - virtual float interpolate(float input); + virtual float interpolate(float input) override; private: - float* mValues; + std::unique_ptr<float[]> mValues; size_t mSize; }; diff --git a/libs/hwui/JankTracker.cpp b/libs/hwui/JankTracker.cpp new file mode 100644 index 0000000..cc5c403 --- /dev/null +++ b/libs/hwui/JankTracker.cpp @@ -0,0 +1,276 @@ +/* + * 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 "JankTracker.h" + +#include <algorithm> +#include <cutils/ashmem.h> +#include <cutils/log.h> +#include <cstdio> +#include <errno.h> +#include <inttypes.h> +#include <sys/mman.h> + +namespace android { +namespace uirenderer { + +static const char* JANK_TYPE_NAMES[] = { + "Missed Vsync", + "High input latency", + "Slow UI thread", + "Slow bitmap uploads", + "Slow draw", +}; + +struct Comparison { + FrameInfoIndex start; + FrameInfoIndex end; +}; + +static const Comparison COMPARISONS[] = { + {FrameInfoIndex::kIntendedVsync, FrameInfoIndex::kVsync}, + {FrameInfoIndex::kOldestInputEvent, FrameInfoIndex::kVsync}, + {FrameInfoIndex::kVsync, FrameInfoIndex::kSyncStart}, + {FrameInfoIndex::kSyncStart, FrameInfoIndex::kIssueDrawCommandsStart}, + {FrameInfoIndex::kIssueDrawCommandsStart, FrameInfoIndex::kFrameCompleted}, +}; + +// If the event exceeds 10 seconds throw it away, this isn't a jank event +// it's an ANR and will be handled as such +static const int64_t IGNORE_EXCEEDING = seconds_to_nanoseconds(10); + +/* + * Frames that are exempt from jank metrics. + * First-draw frames, for example, are expected to + * be slow, this is hidden from the user with window animations and + * other tricks + * + * Similarly, we don't track direct-drawing via Surface:lockHardwareCanvas() + * for now + * + * TODO: kSurfaceCanvas can negatively impact other drawing by using up + * time on the RenderThread, figure out how to attribute that as a jank-causer + */ +static const int64_t EXEMPT_FRAMES_FLAGS + = FrameInfoFlags::kWindowLayoutChanged + | FrameInfoFlags::kSurfaceCanvas; + +// The bucketing algorithm controls so to speak +// If a frame is <= to this it goes in bucket 0 +static const uint32_t kBucketMinThreshold = 7; +// If a frame is > this, start counting in increments of 2ms +static const uint32_t kBucket2msIntervals = 32; +// If a frame is > this, start counting in increments of 4ms +static const uint32_t kBucket4msIntervals = 48; + +// This will be called every frame, performance sensitive +// Uses bit twiddling to avoid branching while achieving the packing desired +static uint32_t frameCountIndexForFrameTime(nsecs_t frameTime, uint32_t max) { + uint32_t index = static_cast<uint32_t>(ns2ms(frameTime)); + // If index > kBucketMinThreshold mask will be 0xFFFFFFFF as a result + // of negating 1 (twos compliment, yaay) else mask will be 0 + uint32_t mask = -(index > kBucketMinThreshold); + // If index > threshold, this will essentially perform: + // amountAboveThreshold = index - threshold; + // index = threshold + (amountAboveThreshold / 2) + // However if index is <= this will do nothing. It will underflow, do + // a right shift by 0 (no-op), then overflow back to the original value + index = ((index - kBucket4msIntervals) >> (index > kBucket4msIntervals)) + + kBucket4msIntervals; + index = ((index - kBucket2msIntervals) >> (index > kBucket2msIntervals)) + + kBucket2msIntervals; + // If index was < minThreshold at the start of all this it's going to + // be a pretty garbage value right now. However, mask is 0 so we'll end + // up with the desired result of 0. + index = (index - kBucketMinThreshold) & mask; + return index < max ? index : max; +} + +// Only called when dumping stats, less performance sensitive +static uint32_t frameTimeForFrameCountIndex(uint32_t index) { + index = index + kBucketMinThreshold; + if (index > kBucket2msIntervals) { + index += (index - kBucket2msIntervals); + } + if (index > kBucket4msIntervals) { + // This works because it was already doubled by the above if + // 1 is added to shift slightly more towards the middle of the bucket + index += (index - kBucket4msIntervals) + 1; + } + return index; +} + +JankTracker::JankTracker(nsecs_t frameIntervalNanos) { + // By default this will use malloc memory. It may be moved later to ashmem + // if there is shared space for it and a request comes in to do that. + mData = new ProfileData; + reset(); + setFrameInterval(frameIntervalNanos); +} + +JankTracker::~JankTracker() { + freeData(); +} + +void JankTracker::freeData() { + if (mIsMapped) { + munmap(mData, sizeof(ProfileData)); + } else { + delete mData; + } + mIsMapped = false; + mData = nullptr; +} + +void JankTracker::switchStorageToAshmem(int ashmemfd) { + int regionSize = ashmem_get_size_region(ashmemfd); + if (regionSize < static_cast<int>(sizeof(ProfileData))) { + ALOGW("Ashmem region is too small! Received %d, required %u", + regionSize, static_cast<unsigned int>(sizeof(ProfileData))); + return; + } + ProfileData* newData = reinterpret_cast<ProfileData*>( + mmap(NULL, sizeof(ProfileData), PROT_READ | PROT_WRITE, + MAP_SHARED, ashmemfd, 0)); + if (newData == MAP_FAILED) { + int err = errno; + ALOGW("Failed to move profile data to ashmem fd %d, error = %d", + ashmemfd, err); + return; + } + + // The new buffer may have historical data that we want to build on top of + // But let's make sure we don't overflow Just In Case + uint32_t divider = 0; + if (newData->totalFrameCount > (1 << 24)) { + divider = 4; + } + for (size_t i = 0; i < mData->jankTypeCounts.size(); i++) { + newData->jankTypeCounts[i] >>= divider; + newData->jankTypeCounts[i] += mData->jankTypeCounts[i]; + } + for (size_t i = 0; i < mData->frameCounts.size(); i++) { + newData->frameCounts[i] >>= divider; + newData->frameCounts[i] += mData->frameCounts[i]; + } + newData->jankFrameCount >>= divider; + newData->jankFrameCount += mData->jankFrameCount; + newData->totalFrameCount >>= divider; + newData->totalFrameCount += mData->totalFrameCount; + if (newData->statStartTime > mData->statStartTime + || newData->statStartTime == 0) { + newData->statStartTime = mData->statStartTime; + } + + freeData(); + mData = newData; + mIsMapped = true; +} + +void JankTracker::setFrameInterval(nsecs_t frameInterval) { + mFrameInterval = frameInterval; + mThresholds[kMissedVsync] = 1; + /* + * Due to interpolation and sample rate differences between the touch + * panel and the display (example, 85hz touch panel driving a 60hz display) + * we call high latency 1.5 * frameinterval + * + * NOTE: Be careful when tuning this! A theoretical 1,000hz touch panel + * on a 60hz display will show kOldestInputEvent - kIntendedVsync of being 15ms + * Thus this must always be larger than frameInterval, or it will fail + */ + mThresholds[kHighInputLatency] = static_cast<int64_t>(1.5 * frameInterval); + + // Note that these do not add up to 1. This is intentional. It's to deal + // with variance in values, and should be sort of an upper-bound on what + // is reasonable to expect. + mThresholds[kSlowUI] = static_cast<int64_t>(.5 * frameInterval); + mThresholds[kSlowSync] = static_cast<int64_t>(.2 * frameInterval); + mThresholds[kSlowRT] = static_cast<int64_t>(.75 * frameInterval); + +} + +void JankTracker::addFrame(const FrameInfo& frame) { + mData->totalFrameCount++; + // Fast-path for jank-free frames + int64_t totalDuration = + frame[FrameInfoIndex::kFrameCompleted] - frame[FrameInfoIndex::kIntendedVsync]; + uint32_t framebucket = frameCountIndexForFrameTime( + totalDuration, mData->frameCounts.size()); + // Keep the fast path as fast as possible. + if (CC_LIKELY(totalDuration < mFrameInterval)) { + mData->frameCounts[framebucket]++; + return; + } + + if (frame[FrameInfoIndex::kFlags] & EXEMPT_FRAMES_FLAGS) { + return; + } + + mData->frameCounts[framebucket]++; + mData->jankFrameCount++; + + for (int i = 0; i < NUM_BUCKETS; i++) { + int64_t delta = frame[COMPARISONS[i].end] - frame[COMPARISONS[i].start]; + if (delta >= mThresholds[i] && delta < IGNORE_EXCEEDING) { + mData->jankTypeCounts[i]++; + } + } +} + +void JankTracker::dumpBuffer(const void* buffer, size_t bufsize, int fd) { + if (bufsize < sizeof(ProfileData)) { + return; + } + const ProfileData* data = reinterpret_cast<const ProfileData*>(buffer); + dumpData(data, fd); +} + +void JankTracker::dumpData(const ProfileData* data, int fd) { + dprintf(fd, "\nStats since: %" PRIu64 "ns", data->statStartTime); + dprintf(fd, "\nTotal frames rendered: %u", data->totalFrameCount); + dprintf(fd, "\nJanky frames: %u (%.2f%%)", data->jankFrameCount, + (float) data->jankFrameCount / (float) data->totalFrameCount * 100.0f); + dprintf(fd, "\n90th percentile: %ums", findPercentile(data, 90)); + dprintf(fd, "\n95th percentile: %ums", findPercentile(data, 95)); + dprintf(fd, "\n99th percentile: %ums", findPercentile(data, 99)); + for (int i = 0; i < NUM_BUCKETS; i++) { + dprintf(fd, "\nNumber %s: %u", JANK_TYPE_NAMES[i], data->jankTypeCounts[i]); + } + dprintf(fd, "\n"); +} + +void JankTracker::reset() { + mData->jankTypeCounts.fill(0); + mData->frameCounts.fill(0); + mData->totalFrameCount = 0; + mData->jankFrameCount = 0; + mData->statStartTime = systemTime(CLOCK_MONOTONIC); +} + +uint32_t JankTracker::findPercentile(const ProfileData* data, int percentile) { + int pos = percentile * data->totalFrameCount / 100; + int remaining = data->totalFrameCount - pos; + for (int i = data->frameCounts.size() - 1; i >= 0; i--) { + remaining -= data->frameCounts[i]; + if (remaining <= 0) { + return frameTimeForFrameCountIndex(i); + } + } + return 0; +} + +} /* namespace uirenderer */ +} /* namespace android */ diff --git a/libs/hwui/JankTracker.h b/libs/hwui/JankTracker.h new file mode 100644 index 0000000..3887e5e --- /dev/null +++ b/libs/hwui/JankTracker.h @@ -0,0 +1,87 @@ +/* + * 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. + */ +#ifndef JANKTRACKER_H_ +#define JANKTRACKER_H_ + +#include "FrameInfo.h" +#include "renderthread/TimeLord.h" +#include "utils/RingBuffer.h" + +#include <cutils/compiler.h> + +#include <array> +#include <memory> + +namespace android { +namespace uirenderer { + +enum JankType { + kMissedVsync = 0, + kHighInputLatency, + kSlowUI, + kSlowSync, + kSlowRT, + + // must be last + NUM_BUCKETS, +}; + +// Try to keep as small as possible, should match ASHMEM_SIZE in +// GraphicsStatsService.java +struct ProfileData { + std::array<uint32_t, NUM_BUCKETS> jankTypeCounts; + // See comments on kBucket* constants for what this holds + std::array<uint32_t, 55> frameCounts; + + uint32_t totalFrameCount; + uint32_t jankFrameCount; + nsecs_t statStartTime; +}; + +// TODO: Replace DrawProfiler with this +class JankTracker { +public: + JankTracker(nsecs_t frameIntervalNanos); + ~JankTracker(); + + void addFrame(const FrameInfo& frame); + + void dump(int fd) { dumpData(mData, fd); } + void reset(); + + void switchStorageToAshmem(int ashmemfd); + + uint32_t findPercentile(int p) { return findPercentile(mData, p); } + + ANDROID_API static void dumpBuffer(const void* buffer, size_t bufsize, int fd); + +private: + void freeData(); + void setFrameInterval(nsecs_t frameIntervalNanos); + + static uint32_t findPercentile(const ProfileData* data, int p); + static void dumpData(const ProfileData* data, int fd); + + std::array<int64_t, NUM_BUCKETS> mThresholds; + int64_t mFrameInterval; + ProfileData* mData; + bool mIsMapped = false; +}; + +} /* namespace uirenderer */ +} /* namespace android */ + +#endif /* JANKTRACKER_H_ */ diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp index 05259ff..458f35b 100644 --- a/libs/hwui/Layer.cpp +++ b/libs/hwui/Layer.cpp @@ -16,17 +16,18 @@ #define LOG_TAG "OpenGLRenderer" -#include <utils/Log.h> +#include "Layer.h" #include "Caches.h" #include "DeferredDisplayList.h" -#include "Layer.h" #include "LayerRenderer.h" #include "OpenGLRenderer.h" #include "RenderNode.h" -#include "RenderState.h" +#include "renderstate/RenderState.h" #include "utils/TraceUtils.h" +#include <utils/Log.h> + #define ATRACE_LAYER_WORK(label) \ ATRACE_FORMAT("%s HW Layer DisplayList %s %ux%u", \ label, \ @@ -36,7 +37,7 @@ namespace android { namespace uirenderer { -Layer::Layer(Type layerType, RenderState& renderState, const uint32_t layerWidth, const uint32_t layerHeight) +Layer::Layer(Type layerType, RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight) : state(kState_Uncached) , caches(Caches::getInstance()) , renderState(renderState) @@ -44,27 +45,10 @@ Layer::Layer(Type layerType, RenderState& renderState, const uint32_t layerWidth , type(layerType) { // 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(0); - mesh = NULL; - meshElementCount = 0; - cacheable = true; - dirty = false; + incStrong(nullptr); renderTarget = GL_TEXTURE_2D; texture.width = layerWidth; texture.height = layerHeight; - colorFilter = NULL; - deferredUpdateScheduled = false; - renderer = NULL; - renderNode = NULL; - fbo = 0; - stencil = NULL; - debugDrawUpdate = false; - hasDrawnSinceUpdate = false; - forceFilter = false; - deferredList = NULL; - convexMask = NULL; - rendererLightPosDirty = true; - wasBuildLayered = false; renderState.registerLayer(this); } @@ -79,8 +63,6 @@ Layer::~Layer() { } delete[] mesh; - delete deferredList; - delete renderer; } void Layer::onGlContextLost() { @@ -98,7 +80,7 @@ uint32_t Layer::computeIdealHeight(uint32_t layerHeight) { void Layer::requireRenderer() { if (!renderer) { - renderer = new LayerRenderer(renderState, this); + renderer.reset(new LayerRenderer(renderState, this)); renderer->initProperties(); } } @@ -137,7 +119,7 @@ bool Layer::resize(const uint32_t width, const uint32_t height) { setSize(desiredWidth, desiredHeight); if (fbo) { - caches.activeTexture(0); + caches.textureState().activateTexture(0); bindTexture(); allocateTexture(); @@ -168,7 +150,7 @@ void Layer::removeFbo(bool flush) { renderState.bindFramebuffer(previousFbo); caches.renderBufferCache.put(stencil); - stencil = NULL; + stencil = nullptr; } if (fbo) { @@ -189,7 +171,7 @@ void Layer::updateDeferred(RenderNode* renderNode, int left, int top, int right, void Layer::setPaint(const SkPaint* paint) { OpenGLRenderer::getAlphaAndModeDirect(paint, &alpha, &mode); - setColorFilter((paint) ? paint->getColorFilter() : NULL); + setColorFilter((paint) ? paint->getColorFilter() : nullptr); } void Layer::setColorFilter(SkColorFilter* filter) { @@ -198,7 +180,7 @@ void Layer::setColorFilter(SkColorFilter* filter) { void Layer::bindTexture() const { if (texture.id) { - caches.bindTexture(renderTarget, texture.id); + caches.textureState().bindTexture(renderTarget, texture.id); } } @@ -222,7 +204,7 @@ void Layer::deleteTexture() { } void Layer::clearTexture() { - caches.unbindTexture(texture.id); + caches.textureState().unbindTexture(texture.id); texture.id = 0; } @@ -233,7 +215,7 @@ void Layer::allocateTexture() { if (texture.id) { glPixelStorei(GL_UNPACK_ALIGNMENT, 4); glTexImage2D(renderTarget, 0, GL_RGBA, getWidth(), getHeight(), 0, - GL_RGBA, GL_UNSIGNED_BYTE, NULL); + GL_RGBA, GL_UNSIGNED_BYTE, nullptr); } } @@ -249,8 +231,7 @@ void Layer::defer(const OpenGLRenderer& rootRenderer) { dirtyRect.set(0, 0, width, height); } - delete deferredList; - deferredList = new DeferredDisplayList(dirtyRect); + deferredList.reset(new DeferredDisplayList(dirtyRect)); DeferStateStruct deferredState(*deferredList, *renderer, RenderNode::kReplayFlag_ClipChildren); @@ -266,19 +247,16 @@ void Layer::defer(const OpenGLRenderer& rootRenderer) { } void Layer::cancelDefer() { - renderNode = NULL; + renderNode = nullptr; deferredUpdateScheduled = false; - if (deferredList) { - delete deferredList; - deferredList = NULL; - } + deferredList.release(); } 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() != NULL) ? renderNode->getName() : "Layer"); + renderer->startMark((renderNode.get() != nullptr) ? renderNode->getName() : "Layer"); renderer->setViewport(layer.getWidth(), layer.getHeight()); renderer->prepareDirty(dirtyRect.left, dirtyRect.top, dirtyRect.right, dirtyRect.bottom, @@ -289,7 +267,7 @@ void Layer::flush() { renderer->finish(); dirtyRect.setEmpty(); - renderNode = NULL; + renderNode = nullptr; renderer->endMark(); } @@ -310,7 +288,7 @@ void Layer::render(const OpenGLRenderer& rootRenderer) { dirtyRect.setEmpty(); deferredUpdateScheduled = false; - renderNode = NULL; + renderNode = nullptr; } void Layer::postDecStrong() { diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h index e196cb1..b670870 100644 --- a/libs/hwui/Layer.h +++ b/libs/hwui/Layer.h @@ -20,6 +20,8 @@ #include <cutils/compiler.h> #include <sys/types.h> #include <utils/StrongPointer.h> +#include <utils/RefBase.h> +#include <memory> #include <GLES2/gl2.h> @@ -43,11 +45,11 @@ namespace uirenderer { // Forward declarations class Caches; +class RenderNode; class RenderState; class OpenGLRenderer; -class RenderNode; class DeferredDisplayList; -class DeferStateStruct; +struct DeferStateStruct; /** * A layer has dimensions and is backed by an OpenGL texture or FBO. @@ -70,7 +72,7 @@ public: }; State state; // public for logging/debugging purposes - Layer(Type type, RenderState& renderState, const uint32_t layerWidth, const uint32_t layerHeight); + Layer(Type type, RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight); ~Layer(); static uint32_t computeIdealWidth(uint32_t layerWidth); @@ -198,10 +200,14 @@ public: return stencil; } - inline GLuint getTexture() const { + inline GLuint getTextureId() const { return texture.id; } + inline Texture& getTexture() { + return texture; + } + inline GLenum getRenderTarget() const { return renderTarget; } @@ -318,19 +324,19 @@ public: /** * If the layer can be rendered as a mesh, this is non-null. */ - TextureVertex* mesh; - GLsizei meshElementCount; + TextureVertex* mesh = nullptr; + GLsizei meshElementCount = 0; /** * Used for deferred updates. */ - bool deferredUpdateScheduled; - OpenGLRenderer* renderer; + bool deferredUpdateScheduled = false; + std::unique_ptr<OpenGLRenderer> renderer; sp<RenderNode> renderNode; Rect dirtyRect; - bool debugDrawUpdate; - bool hasDrawnSinceUpdate; - bool wasBuildLayered; + bool debugDrawUpdate = false; + bool hasDrawnSinceUpdate = false; + bool wasBuildLayered = false; private: void requireRenderer(); @@ -344,17 +350,17 @@ private: * 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; + GLuint fbo = 0; /** * The render buffer used as the stencil buffer. */ - RenderBuffer* stencil; + RenderBuffer* stencil = nullptr; /** * Indicates whether this layer has been used already. */ - bool empty; + bool empty = true; /** * The texture backing this layer. @@ -364,7 +370,7 @@ private: /** * If set to true (by default), the layer can be reused. */ - bool cacheable; + bool cacheable = true; /** * Denotes whether the layer is a DisplayList, or Texture layer. @@ -375,32 +381,32 @@ private: * When set to true, this layer is dirty and should be cleared * before any rendering occurs. */ - bool dirty; + bool dirty = false; /** * Indicates the render target. */ - GLenum renderTarget; + GLenum renderTarget = GL_TEXTURE_2D; /** * Color filter used to draw this layer. Optional. */ - SkColorFilter* colorFilter; + SkColorFilter* colorFilter = nullptr; /** * Indicates raster data backing the layer is scaled, requiring filtration. */ - bool forceFilter; + bool forceFilter = false; /** * Opacity of the layer. */ - int alpha; + int alpha = 255; /** * Blending mode of the layer. */ - SkXfermode::Mode mode; + SkXfermode::Mode mode = SkXfermode::kSrcOver_Mode; /** * Optional texture coordinates transform. @@ -416,20 +422,20 @@ private: * Cached transform of layer in window, updated only on creation / resize */ mat4 cachedInvTransformInWindow; - bool rendererLightPosDirty; + bool rendererLightPosDirty = true; /** * Used to defer display lists when the layer is updated with a * display list. */ - DeferredDisplayList* deferredList; + 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; + const SkPath* convexMask = nullptr; }; // struct Layer diff --git a/libs/hwui/LayerCache.cpp b/libs/hwui/LayerCache.cpp index 3033dc6..bcbd412 100644 --- a/libs/hwui/LayerCache.cpp +++ b/libs/hwui/LayerCache.cpp @@ -33,7 +33,7 @@ namespace uirenderer { LayerCache::LayerCache(): mSize(0), mMaxSize(MB(DEFAULT_LAYER_CACHE_SIZE)) { char property[PROPERTY_VALUE_MAX]; - if (property_get(PROPERTY_LAYER_CACHE_SIZE, property, NULL) > 0) { + if (property_get(PROPERTY_LAYER_CACHE_SIZE, property, nullptr) > 0) { INIT_LOGD(" Setting layer cache size to %sMB", property); setMaxSize(MB(atof(property))); } else { @@ -84,7 +84,7 @@ void LayerCache::deleteLayer(Layer* layer) { layer->getFbo()); mSize -= layer->getWidth() * layer->getHeight() * 4; layer->state = Layer::kState_DeletedFromCache; - layer->decStrong(0); + layer->decStrong(nullptr); } } @@ -97,7 +97,7 @@ void LayerCache::clear() { } Layer* LayerCache::get(RenderState& renderState, const uint32_t width, const uint32_t height) { - Layer* layer = NULL; + Layer* layer = nullptr; LayerEntry entry(width, height); ssize_t index = mCache.indexOf(entry); @@ -116,9 +116,6 @@ Layer* LayerCache::get(RenderState& renderState, const uint32_t width, const uin layer = new Layer(Layer::kType_DisplayList, renderState, entry.mWidth, entry.mHeight); layer->setBlend(true); - layer->setEmpty(true); - layer->setFbo(0); - layer->generateTexture(); layer->bindTexture(); layer->setFilter(GL_NEAREST); @@ -137,7 +134,7 @@ void LayerCache::dump() { size_t size = mCache.size(); for (size_t i = 0; i < size; i++) { const LayerEntry& entry = mCache.itemAt(i); - LAYER_LOGD(" Layer size %dx%d", entry.mWidth, entry.mHeight); + ALOGD(" Layer size %dx%d", entry.mWidth, entry.mHeight); } } diff --git a/libs/hwui/LayerCache.h b/libs/hwui/LayerCache.h index 6b93e8f..7d17b9b 100644 --- a/libs/hwui/LayerCache.h +++ b/libs/hwui/LayerCache.h @@ -30,7 +30,6 @@ class RenderState; // Defines /////////////////////////////////////////////////////////////////////////////// -// Debug #if DEBUG_LAYERS #define LAYER_LOGD(...) ALOGD(__VA_ARGS__) #else @@ -97,10 +96,10 @@ public: private: struct LayerEntry { LayerEntry(): - mLayer(NULL), mWidth(0), mHeight(0) { + mLayer(nullptr), mWidth(0), mHeight(0) { } - LayerEntry(const uint32_t layerWidth, const uint32_t layerHeight): mLayer(NULL) { + LayerEntry(const uint32_t layerWidth, const uint32_t layerHeight): mLayer(nullptr) { mWidth = Layer::computeIdealWidth(layerWidth); mHeight = Layer::computeIdealHeight(layerHeight); } diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp index 83f9c6a..223bdf0 100644 --- a/libs/hwui/LayerRenderer.cpp +++ b/libs/hwui/LayerRenderer.cpp @@ -17,18 +17,19 @@ #define LOG_TAG "OpenGLRenderer" #define ATRACE_TAG ATRACE_TAG_VIEW -#include <ui/Rect.h> - -#include <private/hwui/DrawGlInfo.h> - -#include "RenderState.h" #include "LayerCache.h" #include "LayerRenderer.h" #include "Matrix.h" #include "Properties.h" #include "Rect.h" +#include "renderstate/RenderState.h" #include "utils/TraceUtils.h" +#include <ui/Rect.h> + +#include <private/hwui/DrawGlInfo.h> + + namespace android { namespace uirenderer { @@ -44,11 +45,11 @@ LayerRenderer::LayerRenderer(RenderState& renderState, Layer* layer) LayerRenderer::~LayerRenderer() { } -status_t LayerRenderer::prepareDirty(float left, float top, float right, float bottom, +void LayerRenderer::prepareDirty(float left, float top, float right, float bottom, bool opaque) { LAYER_RENDERER_LOGD("Rendering into layer, fbo = %d", mLayer->getFbo()); - renderState().bindFramebuffer(mLayer->getFbo()); + mRenderState.bindFramebuffer(mLayer->getFbo()); const float width = mLayer->layer.getWidth(); const float height = mLayer->layer.getHeight(); @@ -65,25 +66,23 @@ status_t LayerRenderer::prepareDirty(float left, float top, float right, float b } mLayer->clipRect.set(dirty); - return OpenGLRenderer::prepareDirty(dirty.left, dirty.top, dirty.right, dirty.bottom, opaque); + OpenGLRenderer::prepareDirty(dirty.left, dirty.top, dirty.right, dirty.bottom, opaque); } -status_t LayerRenderer::clear(float left, float top, float right, float bottom, bool opaque) { +void LayerRenderer::clear(float left, float top, float right, float bottom, bool opaque) { if (mLayer->isDirty()) { - getCaches().disableScissor(); + mRenderState.scissor().setEnabled(false); glClear(GL_COLOR_BUFFER_BIT); - getCaches().resetScissor(); + mRenderState.scissor().reset(); mLayer->setDirty(false); - - return DrawGlInfo::kStatusDone; + } else { + OpenGLRenderer::clear(left, top, right, bottom, opaque); } - - return OpenGLRenderer::clear(left, top, right, bottom, opaque); } -void LayerRenderer::finish() { - OpenGLRenderer::finish(); +bool LayerRenderer::finish() { + bool retval = OpenGLRenderer::finish(); generateMesh(); @@ -91,6 +90,7 @@ void LayerRenderer::finish() { // 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 { @@ -118,7 +118,7 @@ void LayerRenderer::ensureStencilBuffer() { /////////////////////////////////////////////////////////////////////////////// Region* LayerRenderer::getRegion() const { - if (currentSnapshot()->flags & Snapshot::kFlagFboTarget) { + if (mState.currentFlags() & Snapshot::kFlagFboTarget) { return OpenGLRenderer::getRegion(); } return &mLayer->region; @@ -131,7 +131,7 @@ void LayerRenderer::generateMesh() { if (mLayer->region.isRect() || mLayer->region.isEmpty()) { if (mLayer->mesh) { delete[] mLayer->mesh; - mLayer->mesh = NULL; + mLayer->mesh = nullptr; mLayer->meshElementCount = 0; } @@ -152,7 +152,7 @@ void LayerRenderer::generateMesh() { if (mLayer->mesh && mLayer->meshElementCount < elementCount) { delete[] mLayer->mesh; - mLayer->mesh = NULL; + mLayer->mesh = nullptr; } if (!mLayer->mesh) { @@ -193,14 +193,14 @@ Layer* LayerRenderer::createRenderLayer(RenderState& renderState, uint32_t width GLuint fbo = caches.fboCache.get(); if (!fbo) { ALOGW("Could not obtain an FBO"); - return NULL; + return nullptr; } - caches.activeTexture(0); + caches.textureState().activateTexture(0); Layer* layer = caches.layerCache.get(renderState, width, height); if (!layer) { ALOGW("Could not obtain a layer"); - return NULL; + return nullptr; } // We first obtain a layer before comparing against the max texture size @@ -213,9 +213,9 @@ Layer* LayerRenderer::createRenderLayer(RenderState& renderState, uint32_t width // 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(0); + layer->decStrong(nullptr); - return NULL; + return nullptr; } layer->setFbo(fbo); @@ -223,7 +223,7 @@ Layer* LayerRenderer::createRenderLayer(RenderState& renderState, uint32_t width layer->texCoords.set(0.0f, height / float(layer->getHeight()), width / float(layer->getWidth()), 0.0f); layer->setAlpha(255, SkXfermode::kSrcOver_Mode); - layer->setColorFilter(NULL); + layer->setColorFilter(nullptr); layer->setDirty(true); layer->region.clear(); @@ -241,13 +241,13 @@ Layer* LayerRenderer::createRenderLayer(RenderState& renderState, uint32_t width if (glGetError() != GL_NO_ERROR) { ALOGE("Could not allocate texture for layer (fbo=%d %dx%d)", fbo, width, height); renderState.bindFramebuffer(previousFbo); - layer->decStrong(0); - return NULL; + layer->decStrong(nullptr); + return nullptr; } } glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, - layer->getTexture(), 0); + layer->getTextureId(), 0); renderState.bindFramebuffer(previousFbo); @@ -275,15 +275,12 @@ Layer* LayerRenderer::createTextureLayer(RenderState& renderState) { Layer* layer = new Layer(Layer::kType_Texture, renderState, 0, 0); layer->setCacheable(false); - layer->setEmpty(true); - layer->setFbo(0); - layer->setAlpha(255, SkXfermode::kSrcOver_Mode); 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().activeTexture(0); + Caches::getInstance().textureState().activateTexture(0); layer->generateTexture(); return layer; @@ -317,7 +314,7 @@ void LayerRenderer::destroyLayer(Layer* layer) { if (!Caches::getInstance().layerCache.put(layer)) { LAYER_RENDERER_LOGD(" Destroyed!"); - layer->decStrong(0); + layer->decStrong(nullptr); } else { LAYER_RENDERER_LOGD(" Cached!"); #if DEBUG_LAYER_RENDERER @@ -337,7 +334,7 @@ void LayerRenderer::flushLayer(RenderState& renderState, Layer* layer) { if (fbo) { // If possible, discard any enqueud operations on deferred // rendering architectures - if (Extensions::getInstance().hasDiscardFramebuffer()) { + if (Caches::getInstance().extensions().hasDiscardFramebuffer()) { GLuint previousFbo = renderState.getFramebuffer(); if (fbo != previousFbo) { renderState.bindFramebuffer(fbo); @@ -356,8 +353,9 @@ void LayerRenderer::flushLayer(RenderState& renderState, Layer* layer) { bool LayerRenderer::copyLayer(RenderState& renderState, Layer* layer, SkBitmap* bitmap) { Caches& caches = Caches::getInstance(); - if (layer && bitmap->width() <= caches.maxTextureSize && - bitmap->height() <= caches.maxTextureSize) { + if (layer + && bitmap->width() <= caches.maxTextureSize + && bitmap->height() <= caches.maxTextureSize) { GLuint fbo = caches.fboCache.get(); if (!fbo) { @@ -412,8 +410,8 @@ bool LayerRenderer::copyLayer(RenderState& renderState, Layer* layer, SkBitmap* glGenTextures(1, &texture); if ((error = glGetError()) != GL_NO_ERROR) goto error; - caches.activeTexture(0); - caches.bindTexture(texture); + caches.textureState().activateTexture(0); + caches.textureState().bindTexture(texture); glPixelStorei(GL_PACK_ALIGNMENT, bitmap->bytesPerPixel()); @@ -424,7 +422,7 @@ bool LayerRenderer::copyLayer(RenderState& renderState, Layer* layer, SkBitmap* 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, NULL); + 0, format, type, nullptr); if ((error = glGetError()) != GL_NO_ERROR) goto error; glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, @@ -437,7 +435,7 @@ bool LayerRenderer::copyLayer(RenderState& renderState, Layer* layer, SkBitmap* renderer.OpenGLRenderer::prepareDirty(0.0f, 0.0f, bitmap->width(), bitmap->height(), !layer->isBlend()); - caches.disableScissor(); + renderState.scissor().setEnabled(false); renderer.translate(0.0f, bitmap->height()); renderer.scale(1.0f, -1.0f); @@ -475,7 +473,7 @@ error: renderState.bindFramebuffer(previousFbo); layer->setAlpha(alpha, mode); layer->setFbo(previousLayerFbo); - caches.deleteTexture(texture); + caches.textureState().deleteTexture(texture); caches.fboCache.put(fbo); renderState.setViewport(previousViewportWidth, previousViewportHeight); diff --git a/libs/hwui/LayerRenderer.h b/libs/hwui/LayerRenderer.h index 4d8620b..47ded7e 100644 --- a/libs/hwui/LayerRenderer.h +++ b/libs/hwui/LayerRenderer.h @@ -49,10 +49,11 @@ public: LayerRenderer(RenderState& renderState, Layer* layer); virtual ~LayerRenderer(); - virtual void onViewportInitialized() { /* do nothing */ } - virtual status_t prepareDirty(float left, float top, float right, float bottom, bool opaque); - virtual status_t clear(float left, float top, float right, float bottom, bool opaque); - virtual void finish(); + virtual void onViewportInitialized() override { /* do nothing */ } + virtual void prepareDirty(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); @@ -65,11 +66,11 @@ public: static void flushLayer(RenderState& renderState, Layer* layer); protected: - virtual void ensureStencilBuffer(); - virtual bool hasLayer() const; - virtual Region* getRegion() const; - virtual GLuint getTargetFbo() const; - virtual bool suppressErrorChecks() const; + 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(); diff --git a/libs/hwui/Matrix.cpp b/libs/hwui/Matrix.cpp index 9f2014f..061d26a 100644 --- a/libs/hwui/Matrix.cpp +++ b/libs/hwui/Matrix.cpp @@ -203,6 +203,34 @@ void Matrix4::copyTo(SkMatrix& v) const { } void Matrix4::loadInverse(const Matrix4& v) { + // Fast case for common translation matrices + if (v.isPureTranslate()) { + // Reset the matrix + // Unnamed fields are never written to except by + // loadIdentity(), they don't need to be reset + data[kScaleX] = 1.0f; + data[kSkewX] = 0.0f; + + data[kScaleY] = 1.0f; + data[kSkewY] = 0.0f; + + data[kScaleZ] = 1.0f; + + data[kPerspective0] = 0.0f; + data[kPerspective1] = 0.0f; + data[kPerspective2] = 1.0f; + + // No need to deal with kTranslateZ because isPureTranslate() + // only returns true when the kTranslateZ component is 0 + data[kTranslateX] = -v.data[kTranslateX]; + data[kTranslateY] = -v.data[kTranslateY]; + data[kTranslateZ] = 0.0f; + + // A "pure translate" matrix can be identity or translation + mType = v.getType(); + return; + } + double scale = 1.0 / (v.data[kScaleX] * ((double) v.data[kScaleY] * v.data[kPerspective2] - (double) v.data[kTranslateY] * v.data[kPerspective1]) + @@ -212,18 +240,18 @@ void Matrix4::loadInverse(const Matrix4& v) { (double) v.data[kScaleY] * v.data[kPerspective0])); data[kScaleX] = (v.data[kScaleY] * v.data[kPerspective2] - - v.data[kTranslateY] * v.data[kPerspective1]) * scale; + v.data[kTranslateY] * v.data[kPerspective1]) * scale; data[kSkewX] = (v.data[kTranslateX] * v.data[kPerspective1] - v.data[kSkewX] * v.data[kPerspective2]) * scale; data[kTranslateX] = (v.data[kSkewX] * v.data[kTranslateY] - - v.data[kTranslateX] * v.data[kScaleY]) * scale; + v.data[kTranslateX] * v.data[kScaleY]) * scale; data[kSkewY] = (v.data[kTranslateY] * v.data[kPerspective0] - v.data[kSkewY] * v.data[kPerspective2]) * scale; data[kScaleY] = (v.data[kScaleX] * v.data[kPerspective2] - - v.data[kTranslateX] * v.data[kPerspective0]) * scale; + v.data[kTranslateX] * v.data[kPerspective0]) * scale; data[kTranslateY] = (v.data[kTranslateX] * v.data[kSkewY] - - v.data[kScaleX] * v.data[kTranslateY]) * scale; + v.data[kScaleX] * v.data[kTranslateY]) * scale; data[kPerspective0] = (v.data[kSkewY] * v.data[kPerspective1] - v.data[kScaleY] * v.data[kPerspective0]) * scale; diff --git a/libs/hwui/Matrix.h b/libs/hwui/Matrix.h index 1c5c578..a760135 100644 --- a/libs/hwui/Matrix.h +++ b/libs/hwui/Matrix.h @@ -210,7 +210,7 @@ public: void decomposeScale(float& sx, float& sy) const; - void dump(const char* label = NULL) const; + void dump(const char* label = nullptr) const; static const Matrix4& identity(); diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp index 6f1e8a2..30935d5 100755..100644 --- a/libs/hwui/OpenGLRenderer.cpp +++ b/libs/hwui/OpenGLRenderer.cpp @@ -14,7 +14,25 @@ * limitations under the License. */ -#define LOG_TAG "OpenGLRenderer" +#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 "utils/GLUtils.h" +#include "utils/PaintUtils.h" +#include "utils/TraceUtils.h" #include <stdlib.h> #include <stdint.h> @@ -32,20 +50,6 @@ #include <ui/Rect.h> -#include "OpenGLRenderer.h" -#include "DeferredDisplayList.h" -#include "DisplayListRenderer.h" -#include "Fence.h" -#include "RenderState.h" -#include "PathTessellator.h" -#include "Properties.h" -#include "ShadowTessellator.h" -#include "SkiaShader.h" -#include "utils/GLUtils.h" -#include "utils/TraceUtils.h" -#include "Vector.h" -#include "VertexBuffer.h" - #if DEBUG_DETAILED_EVENTS #define EVENT_LOGD(...) eventMarkDEBUG(__VA_ARGS__) #else @@ -55,88 +59,19 @@ namespace android { namespace uirenderer { -static GLenum getFilter(const SkPaint* paint) { - if (!paint || paint->getFilterLevel() != SkPaint::kNone_FilterLevel) { - return GL_LINEAR; - } - return GL_NEAREST; -} - -/////////////////////////////////////////////////////////////////////////////// -// Globals -/////////////////////////////////////////////////////////////////////////////// - -/** - * Structure mapping Skia xfermodes to OpenGL blending factors. - */ -struct Blender { - SkXfermode::Mode mode; - GLenum src; - GLenum dst; -}; // struct Blender - -// In this array, the index of each Blender equals the value of the first -// entry. For instance, gBlends[1] == gBlends[SkXfermode::kSrc_Mode] -static const Blender gBlends[] = { - { 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 } -}; - -// This array contains the swapped version of each SkXfermode. 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. -static const Blender gBlendsSwap[] = { - { 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 } -}; - -/////////////////////////////////////////////////////////////////////////////// -// Functions -/////////////////////////////////////////////////////////////////////////////// - -template<typename T> -static inline T min(T a, T b) { - return a < b ? a : b; -} - /////////////////////////////////////////////////////////////////////////////// // Constructors/destructor /////////////////////////////////////////////////////////////////////////////// OpenGLRenderer::OpenGLRenderer(RenderState& renderState) - : mFrameStarted(false) + : mState(*this) , mCaches(Caches::getInstance()) - , mExtensions(Extensions::getInstance()) , mRenderState(renderState) + , mFrameStarted(false) , mScissorOptimizationDisabled(false) , mSuppressTiling(false) , mFirstFrameAfterResize(true) + , mDirty(false) , mLightCenter((Vector3){FLT_MIN, FLT_MIN, FLT_MIN}) , mLightRadius(FLT_MIN) , mAmbientShadowAlpha(0) @@ -144,8 +79,6 @@ OpenGLRenderer::OpenGLRenderer(RenderState& renderState) // *set* draw modifiers to be 0 memset(&mDrawModifiers, 0, sizeof(mDrawModifiers)); mDrawModifiers.mOverrideLayerAlpha = 1.0f; - - memcpy(mMeshVertices, gMeshVertices, sizeof(gMeshVertices)); } OpenGLRenderer::~OpenGLRenderer() { @@ -179,28 +112,26 @@ void OpenGLRenderer::initLight(const Vector3& lightCenter, float lightRadius, void OpenGLRenderer::onViewportInitialized() { glDisable(GL_DITHER); glClearColor(0.0f, 0.0f, 0.0f, 0.0f); - - glEnableVertexAttribArray(Program::kBindingPosition); mFirstFrameAfterResize = true; } void OpenGLRenderer::setupFrameState(float left, float top, float right, float bottom, bool opaque) { mCaches.clearGarbage(); - initializeSaveStack(left, top, right, bottom, mLightCenter); + mState.initializeSaveStack(left, top, right, bottom, mLightCenter); mOpaque = opaque; mTilingClip.set(left, top, right, bottom); } -status_t OpenGLRenderer::startFrame() { - if (mFrameStarted) return DrawGlInfo::kStatusDone; +void OpenGLRenderer::startFrame() { + if (mFrameStarted) return; mFrameStarted = true; - mDirtyClip = true; + mState.setDirtyClip(true); discardFramebuffer(mTilingClip.left, mTilingClip.top, mTilingClip.right, mTilingClip.bottom); - mRenderState.setViewport(getWidth(), getHeight()); + mRenderState.setViewport(mState.getWidth(), mState.getHeight()); // Functors break the tiling extension in pretty spectacular ways // This ensures we don't use tiling when a functor is going to be @@ -213,11 +144,11 @@ status_t OpenGLRenderer::startFrame() { debugOverdraw(true, true); - return clear(mTilingClip.left, mTilingClip.top, + clear(mTilingClip.left, mTilingClip.top, mTilingClip.right, mTilingClip.bottom, mOpaque); } -status_t OpenGLRenderer::prepareDirty(float left, float top, +void OpenGLRenderer::prepareDirty(float left, float top, float right, float bottom, bool opaque) { setupFrameState(left, top, right, bottom, opaque); @@ -227,21 +158,19 @@ status_t OpenGLRenderer::prepareDirty(float left, float top, // for each layer and wait until the first drawing command // to start the frame if (currentSnapshot()->fbo == 0) { - syncState(); + mRenderState.blend().syncEnabled(); updateLayers(); } else { - return startFrame(); + startFrame(); } - - return DrawGlInfo::kStatusDone; } 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 (mExtensions.hasDiscardFramebuffer() && - left <= 0.0f && top <= 0.0f && right >= getWidth() && bottom >= getHeight()) { + 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, @@ -250,24 +179,16 @@ void OpenGLRenderer::discardFramebuffer(float left, float top, float right, floa } } -status_t OpenGLRenderer::clear(float left, float top, float right, float bottom, bool opaque) { +void OpenGLRenderer::clear(float left, float top, float right, float bottom, bool opaque) { if (!opaque) { - mCaches.enableScissor(); - mCaches.setScissor(left, getViewportHeight() - bottom, right - left, bottom - top); + mRenderState.scissor().setEnabled(true); + mRenderState.scissor().set(left, getViewportHeight() - bottom, right - left, bottom - top); glClear(GL_COLOR_BUFFER_BIT); - return DrawGlInfo::kStatusDrew; + mDirty = true; + return; } - mCaches.resetScissor(); - return DrawGlInfo::kStatusDone; -} - -void OpenGLRenderer::syncState() { - if (mCaches.blend) { - glEnable(GL_BLEND); - } else { - glDisable(GL_BLEND); - } + mRenderState.scissor().reset(); } void OpenGLRenderer::startTilingCurrentClip(bool opaque, bool expand) { @@ -307,13 +228,9 @@ void OpenGLRenderer::endTiling() { if (!mSuppressTiling) mCaches.endTiling(); } -void OpenGLRenderer::finish() { +bool OpenGLRenderer::finish() { renderOverdraw(); endTiling(); - - for (size_t i = 0; i < mTempPaths.size(); i++) { - delete mTempPaths[i]; - } mTempPaths.clear(); // When finish() is invoked on FBO 0 we've reached the end @@ -338,6 +255,8 @@ void OpenGLRenderer::finish() { } mFrameStarted = false; + + return reportAndClearDirty(); } void OpenGLRenderer::resumeAfterLayer() { @@ -345,14 +264,14 @@ void OpenGLRenderer::resumeAfterLayer() { mRenderState.bindFramebuffer(currentSnapshot()->fbo); debugOverdraw(true, false); - mCaches.resetScissor(); + mRenderState.scissor().reset(); dirtyClip(); } -status_t OpenGLRenderer::callDrawGLFunction(Functor* functor, Rect& dirty) { - if (currentSnapshot()->isIgnored()) return DrawGlInfo::kStatusDone; +void OpenGLRenderer::callDrawGLFunction(Functor* functor, Rect& dirty) { + if (mState.currentlyIgnored()) return; - Rect clip(*currentClipRect()); + Rect clip(mState.currentClipRect()); clip.snapToPixelBoundaries(); // Since we don't know what the functor will draw, let's dirty @@ -371,12 +290,12 @@ status_t OpenGLRenderer::callDrawGLFunction(Functor* functor, Rect& dirty) { info.height = getViewportHeight(); currentTransform()->copyTo(&info.transform[0]); - bool prevDirtyClip = mDirtyClip; + bool prevDirtyClip = mState.getDirtyClip(); // setup GL state for functor - if (mDirtyClip) { + if (mState.getDirtyClip()) { setStencilFromClip(); // can issue draws, so must precede enableScissor()/interrupt() } - if (mCaches.enableScissor() || prevDirtyClip) { + if (mRenderState.scissor().setEnabled(true) || prevDirtyClip) { setScissorFromClip(); } @@ -384,7 +303,7 @@ status_t OpenGLRenderer::callDrawGLFunction(Functor* functor, Rect& dirty) { // Scissor may have been modified, reset dirty clip dirtyClip(); - return DrawGlInfo::kStatusDrew; + mDirty = true; } /////////////////////////////////////////////////////////////////////////////// @@ -426,27 +345,29 @@ void OpenGLRenderer::renderOverdraw() { if (mCaches.debugOverdraw && getTargetFbo() == 0) { const Rect* clip = &mTilingClip; - mCaches.enableScissor(); - mCaches.setScissor(clip->left, firstSnapshot()->getViewportHeight() - clip->bottom, - clip->right - clip->left, clip->bottom - clip->top); + mRenderState.scissor().setEnabled(true); + mRenderState.scissor().set(clip->left, + mState.firstSnapshot()->getViewportHeight() - clip->bottom, + clip->right - clip->left, + clip->bottom - clip->top); // 1x overdraw - mCaches.stencil.enableDebugTest(2); + mRenderState.stencil().enableDebugTest(2); drawColor(mCaches.getOverdrawColor(1), SkXfermode::kSrcOver_Mode); // 2x overdraw - mCaches.stencil.enableDebugTest(3); + mRenderState.stencil().enableDebugTest(3); drawColor(mCaches.getOverdrawColor(2), SkXfermode::kSrcOver_Mode); // 3x overdraw - mCaches.stencil.enableDebugTest(4); + mRenderState.stencil().enableDebugTest(4); drawColor(mCaches.getOverdrawColor(3), SkXfermode::kSrcOver_Mode); // 4x overdraw and higher - mCaches.stencil.enableDebugTest(4, true); + mRenderState.stencil().enableDebugTest(4, true); drawColor(mCaches.getOverdrawColor(4), SkXfermode::kSrcOver_Mode); - mCaches.stencil.disable(); + mRenderState.stencil().disable(); } } @@ -457,7 +378,6 @@ void OpenGLRenderer::renderOverdraw() { bool OpenGLRenderer::updateLayer(Layer* layer, bool inFrame) { if (layer->deferredUpdateScheduled && layer->renderer && layer->renderNode.get() && layer->renderNode->isRenderable()) { - Rect& dirty = layer->dirtyRect; if (inFrame) { endTiling(); @@ -555,11 +475,11 @@ void OpenGLRenderer::cancelLayerUpdate(Layer* layer) { void OpenGLRenderer::flushLayerUpdates() { ATRACE_NAME("Update HW Layers"); - syncState(); + mRenderState.blend().syncEnabled(); updateLayers(); flushLayers(); // Wait for all the layer updates to be executed - AutoFence fence; + glFinish(); } void OpenGLRenderer::markLayersAsBuildLayers() { @@ -603,9 +523,9 @@ int OpenGLRenderer::saveLayer(float left, float top, float right, float bottom, // force matrix/clip isolation for layer flags |= SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag; - const int count = saveSnapshot(flags); + const int count = mState.saveSnapshot(flags); - if (!currentSnapshot()->isIgnored()) { + if (!mState.currentlyIgnored()) { createLayer(left, top, right, bottom, paint, flags, convexMask); } @@ -618,7 +538,7 @@ void OpenGLRenderer::calculateLayerBoundsAndClip(Rect& bounds, Rect& clip, bool currentTransform()->mapRect(bounds); // Layers only make sense if they are in the framebuffer's bounds - if (bounds.intersect(*currentClipRect())) { + if (bounds.intersect(mState.currentClipRect())) { // We cannot work with sub-pixels in this case bounds.snapToPixelBoundaries(); @@ -652,17 +572,17 @@ void OpenGLRenderer::updateSnapshotIgnoreForLayer(const Rect& bounds, const Rect if (bounds.isEmpty() || bounds.getWidth() > mCaches.maxTextureSize || bounds.getHeight() > mCaches.maxTextureSize || (fboLayer && clip.isEmpty())) { - mSnapshot->empty = fboLayer; + writableSnapshot()->empty = fboLayer; } else { - mSnapshot->invisible = mSnapshot->invisible || (alpha <= 0 && fboLayer); + 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 = saveSnapshot(flags); + const int count = mState.saveSnapshot(flags); - if (!currentSnapshot()->isIgnored() && (flags & SkCanvas::kClipToLayer_SaveFlag)) { + if (!mState.currentlyIgnored() && (flags & SkCanvas::kClipToLayer_SaveFlag)) { // 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) @@ -672,11 +592,11 @@ int OpenGLRenderer::saveLayerDeferred(float left, float top, float right, float calculateLayerBoundsAndClip(bounds, clip, true); updateSnapshotIgnoreForLayer(bounds, clip, true, getAlphaDirect(paint)); - if (!currentSnapshot()->isIgnored()) { - mSnapshot->resetTransform(-bounds.left, -bounds.top, 0.0f); - mSnapshot->resetClip(clip.left, clip.top, clip.right, clip.bottom); - mSnapshot->initializeViewport(bounds.getWidth(), bounds.getHeight()); - mSnapshot->roundRectClipState = NULL; + 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; } } @@ -748,11 +668,11 @@ bool OpenGLRenderer::createLayer(float left, float top, float right, float botto updateSnapshotIgnoreForLayer(bounds, clip, fboLayer, getAlphaDirect(paint)); // Bail out if we won't draw in this snapshot - if (currentSnapshot()->isIgnored()) { + if (mState.currentlyIgnored()) { return false; } - mCaches.activeTexture(0); + mCaches.textureState().activateTexture(0); Layer* layer = mCaches.layerCache.get(mRenderState, bounds.getWidth(), bounds.getHeight()); if (!layer) { return false; @@ -768,8 +688,8 @@ bool OpenGLRenderer::createLayer(float left, float top, float right, float botto layer->setConvexMask(convexMask); // note: the mask must be cleared before returning to the cache // Save the layer in the snapshot - mSnapshot->flags |= Snapshot::kFlagIsLayer; - mSnapshot->layer = layer; + writableSnapshot()->flags |= Snapshot::kFlagIsLayer; + writableSnapshot()->layer = layer; ATRACE_FORMAT_BEGIN("%ssaveLayer %ux%u", fboLayer ? "" : "unclipped ", @@ -787,7 +707,7 @@ bool OpenGLRenderer::createLayer(float left, float top, float right, float botto // 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, NULL); + 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); layer->setEmpty(false); } @@ -796,7 +716,7 @@ bool OpenGLRenderer::createLayer(float left, float top, float right, float botto bounds.getWidth(), bounds.getHeight()); // Enqueue the buffer coordinates to clear the corresponding region later - mLayers.push(new Rect(bounds)); + mLayers.push_back(Rect(bounds)); } } @@ -807,13 +727,13 @@ bool OpenGLRenderer::createFboLayer(Layer* layer, Rect& bounds, Rect& clip) { layer->clipRect.set(clip); layer->setFbo(mCaches.fboCache.get()); - mSnapshot->region = &mSnapshot->layer->region; - mSnapshot->flags |= Snapshot::kFlagFboTarget | Snapshot::kFlagIsFboLayer; - mSnapshot->fbo = layer->getFbo(); - mSnapshot->resetTransform(-bounds.left, -bounds.top, 0.0f); - mSnapshot->resetClip(clip.left, clip.top, clip.right, clip.bottom); - mSnapshot->initializeViewport(bounds.getWidth(), bounds.getHeight()); - mSnapshot->roundRectClipState = NULL; + 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; endTiling(); debugOverdraw(false, false); @@ -828,14 +748,14 @@ bool OpenGLRenderer::createFboLayer(Layer* layer, Rect& bounds, Rect& clip) { } glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, - layer->getTexture(), 0); + layer->getTextureId(), 0); // Expand the startTiling region by 1 startTilingCurrentClip(true, true); // Clear the FBO, expand the clear region by 1 to get nice bilinear filtering - mCaches.enableScissor(); - mCaches.setScissor(clip.left - 1.0f, bounds.getHeight() - clip.bottom - 1.0f, + 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); @@ -860,9 +780,9 @@ void OpenGLRenderer::composeLayer(const Snapshot& removed, const Snapshot& resto const bool fboLayer = removed.flags & Snapshot::kFlagIsFboLayer; bool clipRequired = false; - calculateQuickRejectForScissor(rect.left, rect.top, rect.right, rect.bottom, - &clipRequired, NULL, false); // safely ignore return, should never be rejected - mCaches.setScissorEnabled(mScissorOptimizationDisabled || clipRequired); + 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) { endTiling(); @@ -890,9 +810,9 @@ void OpenGLRenderer::composeLayer(const Snapshot& removed, const Snapshot& resto layer->setAlpha(255); } - mCaches.unbindMeshBuffer(); + mRenderState.meshState().unbindMeshBuffer(); - mCaches.activeTexture(0); + 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 @@ -905,7 +825,7 @@ void OpenGLRenderer::composeLayer(const Snapshot& removed, const Snapshot& resto 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) - mSnapshot->alpha = 1.0f; + writableSnapshot()->alpha = 1.0f; composeLayerRect(layer, rect, true); restore(); } @@ -913,97 +833,50 @@ void OpenGLRenderer::composeLayer(const Snapshot& removed, const Snapshot& resto dirtyClip(); // Failing to add the layer to the cache should happen only if the layer is too large - layer->setConvexMask(NULL); + layer->setConvexMask(nullptr); if (!mCaches.layerCache.put(layer)) { LAYER_LOGD("Deleting layer"); - layer->decStrong(0); + layer->decStrong(nullptr); } } void OpenGLRenderer::drawTextureLayer(Layer* layer, const Rect& rect) { - float alpha = getLayerAlpha(layer); - - setupDraw(); - if (layer->getRenderTarget() == GL_TEXTURE_2D) { - setupDrawWithTexture(); - } else { - setupDrawWithExternalTexture(); - } - setupDrawTextureTransform(); - setupDrawColor(alpha, alpha, alpha, alpha); - setupDrawColorFilter(layer->getColorFilter()); - setupDrawBlending(layer); - setupDrawProgram(); - setupDrawPureColorUniforms(); - setupDrawColorFilterUniforms(layer->getColorFilter()); - if (layer->getRenderTarget() == GL_TEXTURE_2D) { - setupDrawTexture(layer->getTexture()); - } else { - setupDrawExternalTexture(layer->getTexture()); - } - if (currentTransform()->isPureTranslate() && - !layer->getForceFilter() && - layer->getWidth() == (uint32_t) rect.getWidth() && - layer->getHeight() == (uint32_t) rect.getHeight()) { - const float x = (int) floorf(rect.left + currentTransform()->getTranslateX() + 0.5f); - const float y = (int) floorf(rect.top + currentTransform()->getTranslateY() + 0.5f); - - layer->setFilter(GL_NEAREST); - setupDrawModelView(kModelViewMode_TranslateAndScale, false, - x, y, x + rect.getWidth(), y + rect.getHeight(), true); - } else { - layer->setFilter(GL_LINEAR); - setupDrawModelView(kModelViewMode_TranslateAndScale, false, - rect.left, rect.top, rect.right, rect.bottom); - } - setupDrawTextureTransformUniforms(layer->getTexTransform()); - setupDrawMesh(&mMeshVertices[0].x, &mMeshVertices[0].u); - - glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount); + bool snap = !layer->getForceFilter() + && layer->getWidth() == (uint32_t) rect.getWidth() + && layer->getHeight() == (uint32_t) rect.getHeight(); + Glop glop; + GlopBuilder(mRenderState, mCaches, &glop) + .setMeshTexturedUvQuad(nullptr, Rect(0, 1, 1, 0)) // TODO: simplify with VBO + .setFillTextureLayer(*layer, getLayerAlpha(layer)) + .setTransform(currentSnapshot()->getOrthoMatrix(), *currentTransform(), false) + .setModelViewMapUnitToRectOptionalSnap(snap, rect) + .setRoundRectClipState(currentSnapshot()->roundRectClipState) + .build(); + renderGlop(glop); } void OpenGLRenderer::composeLayerRect(Layer* layer, const Rect& rect, bool swap) { if (layer->isTextureLayer()) { EVENT_LOGD("composeTextureLayerRect"); - resetDrawTextureTexCoords(0.0f, 1.0f, 1.0f, 0.0f); drawTextureLayer(layer, rect); - resetDrawTextureTexCoords(0.0f, 0.0f, 1.0f, 1.0f); } else { EVENT_LOGD("composeHardwareLayerRect"); - const Rect& texCoords = layer->texCoords; - resetDrawTextureTexCoords(texCoords.left, texCoords.top, - texCoords.right, texCoords.bottom); - - float x = rect.left; - float y = rect.top; - bool simpleTransform = currentTransform()->isPureTranslate() && - layer->getWidth() == (uint32_t) rect.getWidth() && - layer->getHeight() == (uint32_t) rect.getHeight(); - - if (simpleTransform) { - // When we're swapping, the layer is already in screen coordinates - if (!swap) { - x = (int) floorf(rect.left + currentTransform()->getTranslateX() + 0.5f); - y = (int) floorf(rect.top + currentTransform()->getTranslateY() + 0.5f); - } - - layer->setFilter(GL_NEAREST, true); - } else { - layer->setFilter(GL_LINEAR, true); - } - SkPaint layerPaint; - layerPaint.setAlpha(getLayerAlpha(layer) * 255); - layerPaint.setXfermodeMode(layer->getMode()); - layerPaint.setColorFilter(layer->getColorFilter()); - - bool blend = layer->isBlend() || getLayerAlpha(layer) < 1.0f; - drawTextureMesh(x, y, x + rect.getWidth(), y + rect.getHeight(), - layer->getTexture(), &layerPaint, blend, - &mMeshVertices[0].x, &mMeshVertices[0].u, - GL_TRIANGLE_STRIP, gMeshCount, swap, swap || simpleTransform); - - resetDrawTextureTexCoords(0.0f, 0.0f, 1.0f, 1.0f); + Blend::ModeOrderSwap modeUsage = swap ? + Blend::ModeOrderSwap::Swap : Blend::ModeOrderSwap::NoSwap; + const Matrix4& transform = swap ? Matrix4::identity() : *currentTransform(); + bool snap = !swap + && layer->getWidth() == static_cast<uint32_t>(rect.getWidth()) + && layer->getHeight() == static_cast<uint32_t>(rect.getHeight()); + Glop glop; + GlopBuilder(mRenderState, mCaches, &glop) + .setMeshTexturedUvQuad(nullptr, layer->texCoords) + .setFillLayer(layer->getTexture(), layer->getColorFilter(), getLayerAlpha(layer), layer->getMode(), modeUsage) + .setTransform(currentSnapshot()->getOrthoMatrix(), transform, false) + .setModelViewMapUnitToRectOptionalSnap(snap, rect) + .setRoundRectClipState(currentSnapshot()->roundRectClipState) + .build(); + renderGlop(glop); } } @@ -1033,14 +906,14 @@ public: , mLayer(layer) { } - virtual bool asACustomShader(void** data) const { + virtual bool asACustomShader(void** data) const override { if (data) { *data = static_cast<void*>(mLayer); } return true; } - virtual bool isOpaque() const { + virtual bool isOpaque() const override { return !mLayer->isBlend(); } @@ -1049,13 +922,13 @@ protected: LOG_ALWAYS_FATAL("LayerShader should never be drawn with raster backend."); } - virtual void flatten(SkWriteBuffer&) const { + virtual void flatten(SkWriteBuffer&) const override { LOG_ALWAYS_FATAL("LayerShader should never be flattened."); } - virtual Factory getFactory() const { + virtual Factory getFactory() const override { LOG_ALWAYS_FATAL("LayerShader should never be created from a stream."); - return NULL; + return nullptr; } private: // Unowned. @@ -1088,7 +961,7 @@ void OpenGLRenderer::composeLayerRegion(Layer* layer, const Rect& rect) { const SkPath* maskPath = layer->getConvexMask(); DRAW_DOUBLE_STENCIL(drawConvexPath(*maskPath, &paint)); - paint.setShader(NULL); + paint.setShader(nullptr); restore(); return; @@ -1115,42 +988,12 @@ void OpenGLRenderer::composeLayerRegion(Layer* layer, const Rect& rect) { rects = safeRegion.getArray(&count); } - const float alpha = getLayerAlpha(layer); const float texX = 1.0f / float(layer->getWidth()); const float texY = 1.0f / float(layer->getHeight()); const float height = rect.getHeight(); - setupDraw(); - - // We must get (and therefore bind) the region mesh buffer - // after we setup drawing in case we need to mess with the - // stencil buffer in setupDraw() - TextureVertex* mesh = mCaches.getRegionMesh(); - uint32_t numQuads = 0; - - setupDrawWithTexture(); - setupDrawColor(alpha, alpha, alpha, alpha); - setupDrawColorFilter(layer->getColorFilter()); - setupDrawBlending(layer); - setupDrawProgram(); - setupDrawDirtyRegionsDisabled(); - setupDrawPureColorUniforms(); - setupDrawColorFilterUniforms(layer->getColorFilter()); - setupDrawTexture(layer->getTexture()); - if (currentTransform()->isPureTranslate()) { - const float x = (int) floorf(rect.left + currentTransform()->getTranslateX() + 0.5f); - const float y = (int) floorf(rect.top + currentTransform()->getTranslateY() + 0.5f); - - layer->setFilter(GL_NEAREST); - setupDrawModelView(kModelViewMode_Translate, false, - x, y, x + rect.getWidth(), y + rect.getHeight(), true); - } else { - layer->setFilter(GL_LINEAR); - setupDrawModelView(kModelViewMode_Translate, false, - rect.left, rect.top, rect.right, rect.bottom); - } - setupDrawMeshIndices(&mesh[0].x, &mesh[0].u); - + TextureVertex quadVertices[count * 4]; + TextureVertex* mesh = &quadVertices[0]; for (size_t i = 0; i < count; i++) { const android::Rect* r = &rects[i]; @@ -1164,21 +1007,17 @@ void OpenGLRenderer::composeLayerRegion(Layer* layer, const Rect& rect) { 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); - - numQuads++; - - if (numQuads >= gMaxNumberOfQuads) { - DRAW_DOUBLE_STENCIL(glDrawElements(GL_TRIANGLES, numQuads * 6, - GL_UNSIGNED_SHORT, NULL)); - numQuads = 0; - mesh = mCaches.getRegionMesh(); - } - } - - if (numQuads > 0) { - DRAW_DOUBLE_STENCIL(glDrawElements(GL_TRIANGLES, numQuads * 6, - GL_UNSIGNED_SHORT, NULL)); } + Rect modelRect = Rect(rect.getWidth(), rect.getHeight()); + Glop glop; + GlopBuilder(mRenderState, mCaches, &glop) + .setMeshTexturedIndexedQuads(&quadVertices[0], count * 6) + .setFillLayer(layer->getTexture(), layer->getColorFilter(), getLayerAlpha(layer), layer->getMode(), Blend::ModeOrderSwap::NoSwap) + .setTransform(currentSnapshot()->getOrthoMatrix(), *currentTransform(), false) + .setModelViewOffsetRectSnap(rect.left, rect.top, modelRect) + .setRoundRectClipState(currentSnapshot()->roundRectClipState) + .build(); + DRAW_DOUBLE_STENCIL_IF(!layer->hasDrawnSinceUpdate, renderGlop(glop)); #if DEBUG_LAYERS_AS_REGIONS drawRegionRectsDebug(layer->region); @@ -1231,7 +1070,7 @@ void OpenGLRenderer::drawRegionRects(const SkRegion& region, const SkPaint& pain } void OpenGLRenderer::dirtyLayer(const float left, const float top, - const float right, const float bottom, const mat4 transform) { + const float right, const float bottom, const Matrix4& transform) { if (hasLayer()) { Rect bounds(left, top, right, bottom); transform.mapRect(bounds); @@ -1248,7 +1087,7 @@ void OpenGLRenderer::dirtyLayer(const float left, const float top, } void OpenGLRenderer::dirtyLayerUnchecked(Rect& bounds, Region* region) { - if (bounds.intersect(*currentClipRect())) { + if (bounds.intersect(mState.currentClipRect())) { bounds.snapToPixelBoundaries(); android::Rect dirty(bounds.left, bounds.top, bounds.right, bounds.bottom); if (!dirty.isEmpty()) { @@ -1257,26 +1096,11 @@ void OpenGLRenderer::dirtyLayerUnchecked(Rect& bounds, Region* region) { } } -void OpenGLRenderer::issueIndexedQuadDraw(Vertex* mesh, GLsizei quadsCount) { - GLsizei elementsCount = quadsCount * 6; - while (elementsCount > 0) { - GLsizei drawCount = min(elementsCount, (GLsizei) gMaxNumberOfQuads * 6); - - setupDrawIndexedVertices(&mesh[0].x); - glDrawElements(GL_TRIANGLES, drawCount, GL_UNSIGNED_SHORT, NULL); - - elementsCount -= drawCount; - // Though there are 4 vertices in a quad, we use 6 indices per - // quad to draw with GL_TRIANGLES - mesh += (drawCount / 6) * 4; - } -} - void OpenGLRenderer::clearLayerRegions() { - const size_t count = mLayers.size(); - if (count == 0) return; + const size_t quadCount = mLayers.size(); + if (quadCount == 0) return; - if (!currentSnapshot()->isIgnored()) { + if (!mState.currentlyIgnored()) { EVENT_LOGD("clearLayerRegions"); // Doing several glScissor/glClear here can negatively impact // GPUs with a tiler architecture, instead we draw quads with @@ -1285,44 +1109,36 @@ void OpenGLRenderer::clearLayerRegions() { // 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 = mCaches.disableScissor(); + bool scissorChanged = mRenderState.scissor().setEnabled(false); - Vertex mesh[count * 4]; + Vertex mesh[quadCount * 4]; Vertex* vertex = mesh; - for (uint32_t i = 0; i < count; i++) { - Rect* bounds = mLayers.itemAt(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); + for (uint32_t i = 0; i < quadCount; i++) { + const Rect& bounds = mLayers[i]; - delete bounds; + 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 setupDraw() to prevent stencil setup to do - // the same thing again + // call clearLayerRegions() in renderGlop to prevent + // stencil setup from doing the same thing again mLayers.clear(); - SkPaint clearPaint; - clearPaint.setXfermodeMode(SkXfermode::kClear_Mode); - - setupDraw(false); - setupDrawColor(0.0f, 0.0f, 0.0f, 1.0f); - setupDrawBlending(&clearPaint, true); - setupDrawProgram(); - setupDrawPureColorUniforms(); - setupDrawModelView(kModelViewMode_Translate, false, - 0.0f, 0.0f, 0.0f, 0.0f, true); - - issueIndexedQuadDraw(&mesh[0], count); - - if (scissorChanged) mCaches.enableScissor(); + Glop glop; + GlopBuilder(mRenderState, mCaches, &glop) + .setMeshIndexedQuads(&mesh[0], quadCount) + .setFillClear() + .setTransform(currentSnapshot()->getOrthoMatrix(), Matrix4::identity(), false) + .setModelViewOffsetRect(0, 0, Rect(currentSnapshot()->getClipRect())) + .setRoundRectClipState(currentSnapshot()->roundRectClipState) + .build(); + renderGlop(glop, false); + + if (scissorChanged) mRenderState.scissor().setEnabled(true); } else { - for (uint32_t i = 0; i < count; i++) { - delete mLayers.itemAt(i); - } mLayers.clear(); } } @@ -1332,7 +1148,7 @@ void OpenGLRenderer::clearLayerRegions() { /////////////////////////////////////////////////////////////////////////////// bool OpenGLRenderer::storeDisplayState(DeferredDisplayState& state, int stateDeferFlags) { - const Rect* currentClip = currentClipRect(); + const Rect& currentClip = mState.currentClipRect(); const mat4* currentMatrix = currentTransform(); if (stateDeferFlags & kStateDeferFlag_Draw) { @@ -1344,32 +1160,32 @@ bool OpenGLRenderer::storeDisplayState(DeferredDisplayState& state, int stateDef // is used, it should more closely duplicate the quickReject logic (in how it uses // snapToPixelBoundaries) - if(!clippedBounds.intersect(*currentClip)) { + if (!clippedBounds.intersect(currentClip)) { // quick rejected return true; } state.mClipSideFlags = kClipSide_None; - if (!currentClip->contains(state.mBounds)) { + 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; + 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.mBounds.set(currentClip); } } state.mClipValid = (stateDeferFlags & kStateDeferFlag_Clip); if (state.mClipValid) { - state.mClip.set(*currentClip); + state.mClip.set(currentClip); } // Transform, drawModifiers, and alpha always deferred, since they are used by state operations @@ -1385,12 +1201,12 @@ bool OpenGLRenderer::storeDisplayState(DeferredDisplayState& state, int stateDef void OpenGLRenderer::restoreDisplayState(const DeferredDisplayState& state, bool skipClipRestore) { setMatrix(state.mMatrix); - mSnapshot->alpha = state.mAlpha; + writableSnapshot()->alpha = state.mAlpha; mDrawModifiers = state.mDrawModifiers; - mSnapshot->roundRectClipState = state.mRoundRectClipState; + writableSnapshot()->roundRectClipState = state.mRoundRectClipState; if (state.mClipValid && !skipClipRestore) { - mSnapshot->setClip(state.mClip.left, state.mClip.top, + writableSnapshot()->setClip(state.mClip.left, state.mClip.top, state.mClip.right, state.mClip.bottom); dirtyClip(); } @@ -1404,13 +1220,14 @@ void OpenGLRenderer::restoreDisplayState(const DeferredDisplayState& state, bool * This method should be called when restoreDisplayState() won't be restoring the clip */ void OpenGLRenderer::setupMergedMultiDraw(const Rect* clipRect) { - if (clipRect != NULL) { - mSnapshot->setClip(clipRect->left, clipRect->top, clipRect->right, clipRect->bottom); + if (clipRect != nullptr) { + writableSnapshot()->setClip(clipRect->left, clipRect->top, clipRect->right, clipRect->bottom); } else { - mSnapshot->setClip(0, 0, getWidth(), getHeight()); + writableSnapshot()->setClip(0, 0, mState.getWidth(), mState.getHeight()); } dirtyClip(); - mCaches.setScissorEnabled(clipRect != NULL || mScissorOptimizationDisabled); + bool enableScissor = (clipRect != nullptr) || mScissorOptimizationDisabled; + mRenderState.scissor().setEnabled(enableScissor); } /////////////////////////////////////////////////////////////////////////////// @@ -1418,12 +1235,12 @@ void OpenGLRenderer::setupMergedMultiDraw(const Rect* clipRect) { /////////////////////////////////////////////////////////////////////////////// void OpenGLRenderer::setScissorFromClip() { - Rect clip(*currentClipRect()); + Rect clip(mState.currentClipRect()); clip.snapToPixelBoundaries(); - if (mCaches.setScissor(clip.left, getViewportHeight() - clip.bottom, + if (mRenderState.scissor().set(clip.left, getViewportHeight() - clip.bottom, clip.getWidth(), clip.getHeight())) { - mDirtyClip = false; + mState.setDirtyClip(false); } } @@ -1445,34 +1262,104 @@ void OpenGLRenderer::attachStencilBufferToLayer(Layer* layer) { endTiling(); RenderBuffer* buffer = mCaches.renderBufferCache.get( - Stencil::getSmallestStencilFormat(), layer->getWidth(), layer->getHeight()); + Stencil::getSmallestStencilFormat(), + layer->getWidth(), layer->getHeight()); layer->setStencilRenderBuffer(buffer); startTiling(layer->clipRect, layer->layer.getHeight()); } } +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); + if (!bounds.intersect(scissorBox)) { + bounds.setEmpty(); + } else { + 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()); + + Glop glop; + Vertex* vertices = &rectangleVertices[0]; + GlopBuilder(mRenderState, mCaches, &glop) + .setMeshIndexedQuads(vertices, rectangleVertices.size() / 4) + .setFillBlack() + .setTransform(currentSnapshot()->getOrthoMatrix(), Matrix4::identity(), false) + .setModelViewOffsetRect(0, 0, scissorBox) + .setRoundRectClipState(currentSnapshot()->roundRectClipState) + .build(); + renderGlop(glop); +} + void OpenGLRenderer::setStencilFromClip() { if (!mCaches.debugOverdraw) { - if (!currentSnapshot()->clipRegion->isEmpty()) { + 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 - mDirtyClip = false; + mState.setDirtyClip(false); ensureStencilBuffer(); - mCaches.stencil.enableWrite(); + const ClipArea& clipArea = currentSnapshot()->getClipArea(); - // Clear and update the stencil, but first make sure we restrict drawing + 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 = mCaches.enableScissor(); + bool resetScissor = mRenderState.scissor().setEnabled(true); if (resetScissor) { // The scissor was not set so we now need to update it setScissorFromClip(); } - mCaches.stencil.clear(); + + mRenderState.stencil().clear(); // stash and disable the outline clip state, since stencil doesn't account for outline bool storedSkipOutlineClip = mSkipOutlineClip; @@ -1482,28 +1369,34 @@ void OpenGLRenderer::setStencilFromClip() { paint.setColor(SK_ColorBLACK); paint.setXfermodeMode(SkXfermode::kSrc_Mode); - // 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. + 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(*(currentSnapshot()->clipRegion), paint, false); - if (resetScissor) mCaches.disableScissor(); + // 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; - mCaches.stencil.enableTest(); + mRenderState.stencil().enableTest(incrementThreshold); // Draw the region used to generate the stencil if the appropriate debug // mode is enabled - if (mCaches.debugStencilClip == Caches::kStencilShowRegion) { + // TODO: Implement for rectangle list clip areas + if (mCaches.debugStencilClip == Caches::kStencilShowRegion && + !clipArea.isRectangleList()) { paint.setColor(0x7f0000ff); paint.setXfermodeMode(SkXfermode::kSrcOver_Mode); - drawRegionRects(*(currentSnapshot()->clipRegion), paint); + drawRegionRects(currentSnapshot()->getClipRegion(), paint); } } else { EVENT_LOGD("setStencilFromClip - disabling"); - mCaches.stencil.disable(); + mRenderState.stencil().disable(); } } } @@ -1528,13 +1421,13 @@ bool OpenGLRenderer::quickRejectSetupScissor(float left, float top, float right, bool clipRequired = false; bool roundRectClipRequired = false; - if (calculateQuickRejectForScissor(left, top, right, bottom, + if (mState.calculateQuickRejectForScissor(left, top, right, bottom, &clipRequired, &roundRectClipRequired, snapOut)) { return true; } // not quick rejected, so enable the scissor if clipRequired - mCaches.setScissorEnabled(mScissorOptimizationDisabled || clipRequired); + mRenderState.scissor().setEnabled(mScissorOptimizationDisabled || clipRequired); mSkipOutlineClip = !roundRectClipRequired; return false; } @@ -1550,410 +1443,60 @@ void OpenGLRenderer::debugClip() { #endif } -/////////////////////////////////////////////////////////////////////////////// -// Drawing commands -/////////////////////////////////////////////////////////////////////////////// - -void OpenGLRenderer::setupDraw(bool clearLayer) { +void OpenGLRenderer::renderGlop(const Glop& glop, bool clearLayer) { // TODO: It would be best if we could do this before quickRejectSetupScissor() // changes the scissor test state if (clearLayer) clearLayerRegions(); - // Make sure setScissor & setStencil happen at the beginning of - // this method - if (mDirtyClip) { - if (mCaches.scissorEnabled) { + + if (mState.getDirtyClip()) { + if (mRenderState.scissor().isEnabled()) { setScissorFromClip(); } setStencilFromClip(); } - - mDescription.reset(); - - mSetShaderColor = false; - mColorSet = false; - mColorA = mColorR = mColorG = mColorB = 0.0f; - mTextureUnit = 0; - mTrackDirtyRegions = true; - - // Enable debug highlight when what we're about to draw is tested against - // the stencil buffer and if stencil highlight debugging is on - mDescription.hasDebugHighlight = !mCaches.debugOverdraw && - mCaches.debugStencilClip == Caches::kStencilShowHighlight && - mCaches.stencil.isTestEnabled(); -} - -void OpenGLRenderer::setupDrawWithTexture(bool isAlpha8) { - mDescription.hasTexture = true; - mDescription.hasAlpha8Texture = isAlpha8; -} - -void OpenGLRenderer::setupDrawWithTextureAndColor(bool isAlpha8) { - mDescription.hasTexture = true; - mDescription.hasColors = true; - mDescription.hasAlpha8Texture = isAlpha8; -} - -void OpenGLRenderer::setupDrawWithExternalTexture() { - mDescription.hasExternalTexture = true; -} - -void OpenGLRenderer::setupDrawNoTexture() { - mCaches.disableTexCoordsVertexArray(); -} - -void OpenGLRenderer::setupDrawVertexAlpha(bool useShadowAlphaInterp) { - mDescription.hasVertexAlpha = true; - mDescription.useShadowAlphaInterp = useShadowAlphaInterp; -} - -void OpenGLRenderer::setupDrawColor(int color, int alpha) { - mColorA = alpha / 255.0f; - mColorR = mColorA * ((color >> 16) & 0xFF) / 255.0f; - mColorG = mColorA * ((color >> 8) & 0xFF) / 255.0f; - mColorB = mColorA * ((color ) & 0xFF) / 255.0f; - mColorSet = true; - mSetShaderColor = mDescription.setColorModulate(mColorA); -} - -void OpenGLRenderer::setupDrawAlpha8Color(int color, int alpha) { - mColorA = alpha / 255.0f; - mColorR = mColorA * ((color >> 16) & 0xFF) / 255.0f; - mColorG = mColorA * ((color >> 8) & 0xFF) / 255.0f; - mColorB = mColorA * ((color ) & 0xFF) / 255.0f; - mColorSet = true; - mSetShaderColor = mDescription.setAlpha8ColorModulate(mColorR, mColorG, mColorB, mColorA); -} - -void OpenGLRenderer::setupDrawTextGamma(const SkPaint* paint) { - mCaches.fontRenderer->describe(mDescription, paint); -} - -void OpenGLRenderer::setupDrawColor(float r, float g, float b, float a) { - mColorA = a; - mColorR = r; - mColorG = g; - mColorB = b; - mColorSet = true; - mSetShaderColor = mDescription.setColorModulate(a); -} - -void OpenGLRenderer::setupDrawShader(const SkShader* shader) { - if (shader != NULL) { - SkiaShader::describe(&mCaches, mDescription, mExtensions, *shader); - } -} - -void OpenGLRenderer::setupDrawColorFilter(const SkColorFilter* filter) { - if (filter == NULL) { - return; - } - - SkXfermode::Mode mode; - if (filter->asColorMode(NULL, &mode)) { - mDescription.colorOp = ProgramDescription::kColorBlend; - mDescription.colorMode = mode; - } else if (filter->asColorMatrix(NULL)) { - mDescription.colorOp = ProgramDescription::kColorMatrix; - } -} - -void OpenGLRenderer::accountForClear(SkXfermode::Mode mode) { - if (mColorSet && mode == SkXfermode::kClear_Mode) { - mColorA = 1.0f; - mColorR = mColorG = mColorB = 0.0f; - mSetShaderColor = mDescription.modulate = true; - } -} - -void OpenGLRenderer::setupDrawBlending(const Layer* layer, bool swapSrcDst) { - SkXfermode::Mode mode = layer->getMode(); - // When the blending mode is kClear_Mode, we need to use a modulate color - // argb=1,0,0,0 - accountForClear(mode); - // TODO: check shader blending, once we have shader drawing support for layers. - bool blend = layer->isBlend() || getLayerAlpha(layer) < 1.0f || - (mColorSet && mColorA < 1.0f) || isBlendedColorFilter(layer->getColorFilter()); - chooseBlending(blend, mode, mDescription, swapSrcDst); -} - -void OpenGLRenderer::setupDrawBlending(const SkPaint* paint, bool blend, bool swapSrcDst) { - SkXfermode::Mode mode = getXfermodeDirect(paint); - // When the blending mode is kClear_Mode, we need to use a modulate color - // argb=1,0,0,0 - accountForClear(mode); - blend |= (mColorSet && mColorA < 1.0f) || - (getShader(paint) && !getShader(paint)->isOpaque()) || - isBlendedColorFilter(getColorFilter(paint)); - chooseBlending(blend, mode, mDescription, swapSrcDst); -} - -void OpenGLRenderer::setupDrawProgram() { - useProgram(mCaches.programCache.get(mDescription)); - if (mDescription.hasRoundRectClip) { - // TODO: avoid doing this repeatedly, stashing state pointer in program - const RoundRectClipState* state = mSnapshot->roundRectClipState; - const Rect& innerRect = state->innerRect; - glUniform4f(mCaches.currentProgram->getUniform("roundRectInnerRectLTRB"), - innerRect.left, innerRect.top, - innerRect.right, innerRect.bottom); - glUniformMatrix4fv(mCaches.currentProgram->getUniform("roundRectInvTransform"), - 1, GL_FALSE, &state->matrix.data[0]); - - // add half pixel to round out integer rect space to cover pixel centers - float roundedOutRadius = state->radius + 0.5f; - glUniform1f(mCaches.currentProgram->getUniform("roundRectRadius"), - roundedOutRadius); - } -} - -void OpenGLRenderer::setupDrawDirtyRegionsDisabled() { - mTrackDirtyRegions = false; -} - -void OpenGLRenderer::setupDrawModelView(ModelViewMode mode, bool offset, - float left, float top, float right, float bottom, bool ignoreTransform) { - mModelViewMatrix.loadTranslate(left, top, 0.0f); - if (mode == kModelViewMode_TranslateAndScale) { - mModelViewMatrix.scale(right - left, bottom - top, 1.0f); - } - - bool dirty = right - left > 0.0f && bottom - top > 0.0f; - const Matrix4& transformMatrix = ignoreTransform ? Matrix4::identity() : *currentTransform(); - mCaches.currentProgram->set(mSnapshot->getOrthoMatrix(), mModelViewMatrix, transformMatrix, offset); - if (dirty && mTrackDirtyRegions) { - if (!ignoreTransform) { - dirtyLayer(left, top, right, bottom, *currentTransform()); - } else { - dirtyLayer(left, top, right, bottom); - } - } -} - -void OpenGLRenderer::setupDrawColorUniforms(bool hasShader) { - if ((mColorSet && !hasShader) || (hasShader && mSetShaderColor)) { - mCaches.currentProgram->setColor(mColorR, mColorG, mColorB, mColorA); - } -} - -void OpenGLRenderer::setupDrawPureColorUniforms() { - if (mSetShaderColor) { - mCaches.currentProgram->setColor(mColorR, mColorG, mColorB, mColorA); - } -} - -void OpenGLRenderer::setupDrawShaderUniforms(const SkShader* shader, bool ignoreTransform) { - if (shader == NULL) { - return; - } - - if (ignoreTransform) { - // if ignoreTransform=true was passed to setupDrawModelView, undo currentTransform() - // because it was built into modelView / the geometry, and the description needs to - // compensate. - mat4 modelViewWithoutTransform; - modelViewWithoutTransform.loadInverse(*currentTransform()); - modelViewWithoutTransform.multiply(mModelViewMatrix); - mModelViewMatrix.load(modelViewWithoutTransform); - } - - SkiaShader::setupProgram(&mCaches, mModelViewMatrix, &mTextureUnit, mExtensions, *shader); -} - -void OpenGLRenderer::setupDrawColorFilterUniforms(const SkColorFilter* filter) { - if (NULL == filter) { - return; - } - - SkColor color; - SkXfermode::Mode mode; - if (filter->asColorMode(&color, &mode)) { - const int alpha = SkColorGetA(color); - const GLfloat a = alpha / 255.0f; - const GLfloat r = a * SkColorGetR(color) / 255.0f; - const GLfloat g = a * SkColorGetG(color) / 255.0f; - const GLfloat b = a * SkColorGetB(color) / 255.0f; - glUniform4f(mCaches.currentProgram->getUniform("colorBlend"), r, g, b, a); - return; - } - - SkScalar srcColorMatrix[20]; - if (filter->asColorMatrix(srcColorMatrix)) { - - float colorMatrix[16]; - memcpy(colorMatrix, srcColorMatrix, 4 * sizeof(float)); - memcpy(&colorMatrix[4], &srcColorMatrix[5], 4 * sizeof(float)); - memcpy(&colorMatrix[8], &srcColorMatrix[10], 4 * sizeof(float)); - memcpy(&colorMatrix[12], &srcColorMatrix[15], 4 * sizeof(float)); - - // 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[4]; - colorVector[0] = srcColorMatrix[4] / 255.0f; - colorVector[1] = srcColorMatrix[9] / 255.0f; - colorVector[2] = srcColorMatrix[14] / 255.0f; - colorVector[3] = srcColorMatrix[19] / 255.0f; - - glUniformMatrix4fv(mCaches.currentProgram->getUniform("colorMatrix"), 1, - GL_FALSE, colorMatrix); - glUniform4fv(mCaches.currentProgram->getUniform("colorMatrixVector"), 1, colorVector); - return; - } - - // it is an error if we ever get here -} - -void OpenGLRenderer::setupDrawTextGammaUniforms() { - mCaches.fontRenderer->setupProgram(mDescription, mCaches.currentProgram); -} - -void OpenGLRenderer::setupDrawSimpleMesh() { - bool force = mCaches.bindMeshBuffer(); - mCaches.bindPositionVertexPointer(force, 0); - mCaches.unbindIndicesBuffer(); -} - -void OpenGLRenderer::setupDrawTexture(GLuint texture) { - if (texture) bindTexture(texture); - mTextureUnit++; - mCaches.enableTexCoordsVertexArray(); -} - -void OpenGLRenderer::setupDrawExternalTexture(GLuint texture) { - bindExternalTexture(texture); - mTextureUnit++; - mCaches.enableTexCoordsVertexArray(); -} - -void OpenGLRenderer::setupDrawTextureTransform() { - mDescription.hasTextureTransform = true; -} - -void OpenGLRenderer::setupDrawTextureTransformUniforms(mat4& transform) { - glUniformMatrix4fv(mCaches.currentProgram->getUniform("mainTextureTransform"), 1, - GL_FALSE, &transform.data[0]); -} - -void OpenGLRenderer::setupDrawMesh(const GLvoid* vertices, - const GLvoid* texCoords, GLuint vbo) { - bool force = false; - if (!vertices || vbo) { - force = mCaches.bindMeshBuffer(vbo == 0 ? mCaches.meshBuffer : vbo); - } else { - force = mCaches.unbindMeshBuffer(); - } - - mCaches.bindPositionVertexPointer(force, vertices); - if (mCaches.currentProgram->texCoords >= 0) { - mCaches.bindTexCoordsVertexPointer(force, texCoords); + mRenderState.render(glop); + if (!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? + dirtyLayer(glop.bounds.left, glop.bounds.top, glop.bounds.right, glop.bounds.bottom); + mDirty = true; } - - mCaches.unbindIndicesBuffer(); -} - -void OpenGLRenderer::setupDrawMesh(const GLvoid* vertices, - const GLvoid* texCoords, const GLvoid* colors) { - bool force = mCaches.unbindMeshBuffer(); - GLsizei stride = sizeof(ColorTextureVertex); - - mCaches.bindPositionVertexPointer(force, vertices, stride); - if (mCaches.currentProgram->texCoords >= 0) { - mCaches.bindTexCoordsVertexPointer(force, texCoords, stride); - } - int slot = mCaches.currentProgram->getAttrib("colors"); - if (slot >= 0) { - glEnableVertexAttribArray(slot); - glVertexAttribPointer(slot, 4, GL_FLOAT, GL_FALSE, stride, colors); - } - - mCaches.unbindIndicesBuffer(); -} - -void OpenGLRenderer::setupDrawMeshIndices(const GLvoid* vertices, - const GLvoid* texCoords, GLuint vbo) { - bool force = false; - // If vbo is != 0 we want to treat the vertices parameter as an offset inside - // a VBO. However, if vertices is set to NULL and vbo == 0 then we want to - // use the default VBO found in Caches - if (!vertices || vbo) { - force = mCaches.bindMeshBuffer(vbo == 0 ? mCaches.meshBuffer : vbo); - } else { - force = mCaches.unbindMeshBuffer(); - } - mCaches.bindQuadIndicesBuffer(); - - mCaches.bindPositionVertexPointer(force, vertices); - if (mCaches.currentProgram->texCoords >= 0) { - mCaches.bindTexCoordsVertexPointer(force, texCoords); - } -} - -void OpenGLRenderer::setupDrawIndexedVertices(GLvoid* vertices) { - bool force = mCaches.unbindMeshBuffer(); - mCaches.bindQuadIndicesBuffer(); - mCaches.bindPositionVertexPointer(force, vertices, gVertexStride); } /////////////////////////////////////////////////////////////////////////////// // Drawing /////////////////////////////////////////////////////////////////////////////// -status_t OpenGLRenderer::drawRenderNode(RenderNode* renderNode, Rect& dirty, int32_t replayFlags) { - status_t status; +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(mCaches.drawDeferDisabled)) { - status = startFrame(); + startFrame(); ReplayStateStruct replayStruct(*this, dirty, replayFlags); renderNode->replay(replayStruct, 0); - return status | replayStruct.mDrawGlStatus; + return; } // Don't avoid overdraw when visualizing, since that makes it harder to // debug where it's coming from, and when the problem occurs. bool avoidOverdraw = !mCaches.debugOverdraw; - DeferredDisplayList deferredList(*currentClipRect(), avoidOverdraw); + DeferredDisplayList deferredList(mState.currentClipRect(), avoidOverdraw); DeferStateStruct deferStruct(deferredList, *this, replayFlags); renderNode->defer(deferStruct, 0); flushLayers(); - status = startFrame(); + startFrame(); - return deferredList.flush(*this, dirty) | status; - } - - // Even if there is no drawing command(Ex: invisible), - // it still needs startFrame to clear buffer and start tiling. - return startFrame(); -} - -void OpenGLRenderer::drawAlphaBitmap(Texture* texture, float left, float top, const SkPaint* paint) { - int color = paint != NULL ? paint->getColor() : 0; - - float x = left; - float y = top; - - texture->setWrap(GL_CLAMP_TO_EDGE, true); - - bool ignoreTransform = false; - if (currentTransform()->isPureTranslate()) { - x = (int) floorf(left + currentTransform()->getTranslateX() + 0.5f); - y = (int) floorf(top + currentTransform()->getTranslateY() + 0.5f); - ignoreTransform = true; - - texture->setFilter(GL_NEAREST, true); + deferredList.flush(*this, dirty); } else { - texture->setFilter(getFilter(paint), true); + // Even if there is no drawing command(Ex: invisible), + // it still needs startFrame to clear buffer and start tiling. + startFrame(); } - - // No need to check for a UV mapper on the texture object, only ARGB_8888 - // bitmaps get packed in the atlas - drawAlpha8TextureMesh(x, y, x + texture->width, y + texture->height, texture->id, - paint, (GLvoid*) NULL, (GLvoid*) gMeshTextureOffset, - GL_TRIANGLE_STRIP, gMeshCount, ignoreTransform); } /** @@ -1961,102 +1504,79 @@ void OpenGLRenderer::drawAlphaBitmap(Texture* texture, float left, float top, co * will not set the scissor enable or dirty the current layer, if any. * The caller is responsible for properly dirtying the current layer. */ -status_t OpenGLRenderer::drawBitmaps(const SkBitmap* bitmap, AssetAtlas::Entry* entry, +void OpenGLRenderer::drawBitmaps(const SkBitmap* bitmap, AssetAtlas::Entry* entry, int bitmapCount, TextureVertex* vertices, bool pureTranslate, const Rect& bounds, const SkPaint* paint) { - mCaches.activeTexture(0); Texture* texture = entry ? entry->texture : mCaches.textureCache.get(bitmap); - if (!texture) return DrawGlInfo::kStatusDone; + if (!texture) return; const AutoTexture autoCleanup(texture); - texture->setWrap(GL_CLAMP_TO_EDGE, true); - texture->setFilter(pureTranslate ? GL_NEAREST : getFilter(paint), true); - - const float x = (int) floorf(bounds.left + 0.5f); - const float y = (int) floorf(bounds.top + 0.5f); - if (CC_UNLIKELY(bitmap->colorType() == kAlpha_8_SkColorType)) { - drawAlpha8TextureMesh(x, y, x + bounds.getWidth(), y + bounds.getHeight(), - texture->id, paint, &vertices[0].x, &vertices[0].u, - GL_TRIANGLES, bitmapCount * 6, true, - kModelViewMode_Translate, false); - } else { - drawTextureMesh(x, y, x + bounds.getWidth(), y + bounds.getHeight(), - texture->id, paint, texture->blend, &vertices[0].x, &vertices[0].u, - GL_TRIANGLES, bitmapCount * 6, false, true, 0, - kModelViewMode_Translate, false); - } - - return DrawGlInfo::kStatusDrew; -} - -status_t OpenGLRenderer::drawBitmap(const SkBitmap* bitmap, const SkPaint* paint) { + // 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); + int textureFillFlags = static_cast<int>((bitmap->colorType() == kAlpha_8_SkColorType) + ? TextureFillFlags::kIsAlphaMaskTexture : TextureFillFlags::kNone); + Glop glop; + GlopBuilder(mRenderState, mCaches, &glop) + .setMeshTexturedMesh(vertices, bitmapCount * 6) + .setFillTexturePaint(*texture, textureFillFlags, paint, currentSnapshot()->alpha) + .setTransform(currentSnapshot()->getOrthoMatrix(), Matrix4::identity(), false) + .setModelViewOffsetRectOptionalSnap(snap, x, y, Rect(0, 0, bounds.getWidth(), bounds.getHeight())) + .setRoundRectClipState(currentSnapshot()->roundRectClipState) + .build(); + renderGlop(glop); +} + +void OpenGLRenderer::drawBitmap(const SkBitmap* bitmap, const SkPaint* paint) { if (quickRejectSetupScissor(0, 0, bitmap->width(), bitmap->height())) { - return DrawGlInfo::kStatusDone; + return; } - mCaches.activeTexture(0); + mCaches.textureState().activateTexture(0); Texture* texture = getTexture(bitmap); - if (!texture) return DrawGlInfo::kStatusDone; + if (!texture) return; const AutoTexture autoCleanup(texture); - if (CC_UNLIKELY(bitmap->colorType() == kAlpha_8_SkColorType)) { - drawAlphaBitmap(texture, 0, 0, paint); - } else { - drawTextureRect(0, 0, bitmap->width(), bitmap->height(), texture, paint); - } - - return DrawGlInfo::kStatusDrew; + int textureFillFlags = static_cast<int>((bitmap->colorType() == kAlpha_8_SkColorType) + ? TextureFillFlags::kIsAlphaMaskTexture : TextureFillFlags::kNone); + Glop glop; + GlopBuilder(mRenderState, mCaches, &glop) + .setMeshTexturedUnitQuad(texture->uvMapper) + .setFillTexturePaint(*texture, textureFillFlags, paint, currentSnapshot()->alpha) + .setTransform(currentSnapshot()->getOrthoMatrix(), *currentTransform(), false) + .setModelViewMapUnitToRectSnap(Rect(0, 0, texture->width, texture->height)) + .setRoundRectClipState(currentSnapshot()->roundRectClipState) + .build(); + renderGlop(glop); } -status_t OpenGLRenderer::drawBitmapData(const SkBitmap* bitmap, const SkPaint* paint) { - if (quickRejectSetupScissor(0, 0, bitmap->width(), bitmap->height())) { - return DrawGlInfo::kStatusDone; - } - - mCaches.activeTexture(0); - Texture* texture = mCaches.textureCache.getTransient(bitmap); - const AutoTexture autoCleanup(texture); - - if (CC_UNLIKELY(bitmap->colorType() == kAlpha_8_SkColorType)) { - drawAlphaBitmap(texture, 0, 0, paint); - } else { - drawTextureRect(0, 0, bitmap->width(), bitmap->height(), texture, paint); - } - - return DrawGlInfo::kStatusDrew; -} - -status_t OpenGLRenderer::drawBitmapMesh(const SkBitmap* bitmap, int meshWidth, int meshHeight, +void OpenGLRenderer::drawBitmapMesh(const SkBitmap* bitmap, int meshWidth, int meshHeight, const float* vertices, const int* colors, const SkPaint* paint) { - if (!vertices || currentSnapshot()->isIgnored()) { - return DrawGlInfo::kStatusDone; + if (!vertices || mState.currentlyIgnored()) { + return; } - // TODO: use quickReject on bounds from vertices - mCaches.enableScissor(); - float left = FLT_MAX; float top = FLT_MAX; float right = FLT_MIN; float bottom = FLT_MIN; - const uint32_t count = meshWidth * meshHeight * 6; + const uint32_t elementCount = meshWidth * meshHeight * 6; - Vector<ColorTextureVertex> mesh; // TODO: use C++11 unique_ptr - mesh.setCapacity(count); - ColorTextureVertex* vertex = mesh.editArray(); + std::unique_ptr<ColorTextureVertex[]> mesh(new ColorTextureVertex[elementCount]); + ColorTextureVertex* vertex = &mesh[0]; - bool cleanupColors = false; + std::unique_ptr<int[]> tempColors; if (!colors) { uint32_t colorsCount = (meshWidth + 1) * (meshHeight + 1); - int* newColors = new int[colorsCount]; - memset(newColors, 0xff, colorsCount * sizeof(int)); - colors = newColors; - cleanupColors = true; + tempColors.reset(new int[colorsCount]); + memset(tempColors.get(), 0xff, colorsCount * sizeof(int)); + colors = tempColors.get(); } - mCaches.activeTexture(0); Texture* texture = mRenderState.assetAtlas().getEntryTexture(bitmap); const UvMapper& mapper(getMapper(texture)); @@ -2096,210 +1616,83 @@ status_t OpenGLRenderer::drawBitmapMesh(const SkBitmap* bitmap, int meshWidth, i } if (quickRejectSetupScissor(left, top, right, bottom)) { - if (cleanupColors) delete[] colors; - return DrawGlInfo::kStatusDone; + return; } if (!texture) { texture = mCaches.textureCache.get(bitmap); if (!texture) { - if (cleanupColors) delete[] colors; - return DrawGlInfo::kStatusDone; + return; } } const AutoTexture autoCleanup(texture); - texture->setWrap(GL_CLAMP_TO_EDGE, true); - texture->setFilter(getFilter(paint), true); - - int alpha; - SkXfermode::Mode mode; - getAlphaAndMode(paint, &alpha, &mode); - - float a = alpha / 255.0f; - - if (hasLayer()) { - dirtyLayer(left, top, right, bottom, *currentTransform()); - } - - setupDraw(); - setupDrawWithTextureAndColor(); - setupDrawColor(a, a, a, a); - setupDrawColorFilter(getColorFilter(paint)); - setupDrawBlending(paint, true); - setupDrawProgram(); - setupDrawDirtyRegionsDisabled(); - setupDrawModelView(kModelViewMode_TranslateAndScale, false, 0.0f, 0.0f, 1.0f, 1.0f); - setupDrawTexture(texture->id); - setupDrawPureColorUniforms(); - setupDrawColorFilterUniforms(getColorFilter(paint)); - setupDrawMesh(&mesh[0].x, &mesh[0].u, &mesh[0].r); - - glDrawArrays(GL_TRIANGLES, 0, count); - - int slot = mCaches.currentProgram->getAttrib("colors"); - if (slot >= 0) { - glDisableVertexAttribArray(slot); - } - - if (cleanupColors) delete[] colors; - - return DrawGlInfo::kStatusDrew; -} - -status_t OpenGLRenderer::drawBitmap(const SkBitmap* bitmap, - float srcLeft, float srcTop, float srcRight, float srcBottom, - float dstLeft, float dstTop, float dstRight, float dstBottom, - const SkPaint* paint) { - if (quickRejectSetupScissor(dstLeft, dstTop, dstRight, dstBottom)) { - return DrawGlInfo::kStatusDone; + /* + * TODO: handle alpha_8 textures correctly by applying paint color, but *not* + * shader in that case to mimic the behavior in SkiaCanvas::drawBitmapMesh. + */ + Glop glop; + GlopBuilder(mRenderState, mCaches, &glop) + .setMeshColoredTexturedMesh(mesh.get(), elementCount) + .setFillTexturePaint(*texture, static_cast<int>(TextureFillFlags::kNone), paint, currentSnapshot()->alpha) + .setTransform(currentSnapshot()->getOrthoMatrix(), *currentTransform(), false) + .setModelViewOffsetRect(0, 0, Rect(left, top, right, bottom)) + .setRoundRectClipState(currentSnapshot()->roundRectClipState) + .build(); + renderGlop(glop); +} + +void OpenGLRenderer::drawBitmap(const SkBitmap* bitmap, Rect src, Rect dst, const SkPaint* paint) { + if (quickRejectSetupScissor(dst)) { + return; } - mCaches.activeTexture(0); Texture* texture = getTexture(bitmap); - if (!texture) return DrawGlInfo::kStatusDone; + if (!texture) return; const AutoTexture autoCleanup(texture); - const float width = texture->width; - const float height = texture->height; - - float u1 = fmax(0.0f, srcLeft / width); - float v1 = fmax(0.0f, srcTop / height); - float u2 = fmin(1.0f, srcRight / width); - float v2 = fmin(1.0f, srcBottom / height); - - getMapper(texture).map(u1, v1, u2, v2); - - mCaches.unbindMeshBuffer(); - resetDrawTextureTexCoords(u1, v1, u2, v2); - - texture->setWrap(GL_CLAMP_TO_EDGE, true); - - float scaleX = (dstRight - dstLeft) / (srcRight - srcLeft); - float scaleY = (dstBottom - dstTop) / (srcBottom - srcTop); - - bool scaled = scaleX != 1.0f || scaleY != 1.0f; - // Apply a scale transform on the canvas only when a shader is in use - // Skia handles the ratio between the dst and src rects as a scale factor - // when a shader is set - bool useScaleTransform = getShader(paint) && scaled; - bool ignoreTransform = false; - - if (CC_LIKELY(currentTransform()->isPureTranslate() && !useScaleTransform)) { - float x = (int) floorf(dstLeft + currentTransform()->getTranslateX() + 0.5f); - float y = (int) floorf(dstTop + currentTransform()->getTranslateY() + 0.5f); - - dstRight = x + (dstRight - dstLeft); - dstBottom = y + (dstBottom - dstTop); - - dstLeft = x; - dstTop = y; - - texture->setFilter(scaled ? getFilter(paint) : GL_NEAREST, true); - ignoreTransform = true; - } else { - texture->setFilter(getFilter(paint), true); - } - - if (CC_UNLIKELY(useScaleTransform)) { - save(SkCanvas::kMatrix_SaveFlag); - translate(dstLeft, dstTop); - scale(scaleX, scaleY); - - dstLeft = 0.0f; - dstTop = 0.0f; - - dstRight = srcRight - srcLeft; - dstBottom = srcBottom - srcTop; - } - - if (CC_UNLIKELY(bitmap->colorType() == kAlpha_8_SkColorType)) { - drawAlpha8TextureMesh(dstLeft, dstTop, dstRight, dstBottom, - texture->id, paint, - &mMeshVertices[0].x, &mMeshVertices[0].u, - GL_TRIANGLE_STRIP, gMeshCount, ignoreTransform); - } else { - drawTextureMesh(dstLeft, dstTop, dstRight, dstBottom, - texture->id, paint, texture->blend, - &mMeshVertices[0].x, &mMeshVertices[0].u, - GL_TRIANGLE_STRIP, gMeshCount, false, ignoreTransform); - } - - if (CC_UNLIKELY(useScaleTransform)) { - restore(); - } - - resetDrawTextureTexCoords(0.0f, 0.0f, 1.0f, 1.0f); - - return DrawGlInfo::kStatusDrew; -} - -status_t OpenGLRenderer::drawPatch(const SkBitmap* bitmap, const Res_png_9patch* patch, - float left, float top, float right, float bottom, const SkPaint* paint) { - if (quickRejectSetupScissor(left, top, right, bottom)) { - return DrawGlInfo::kStatusDone; - } - - AssetAtlas::Entry* entry = mRenderState.assetAtlas().getEntry(bitmap); - const Patch* mesh = mCaches.patchCache.get(entry, bitmap->width(), bitmap->height(), - right - left, bottom - top, patch); - - return drawPatch(bitmap, mesh, entry, left, top, right, bottom, paint); -} - -status_t OpenGLRenderer::drawPatch(const SkBitmap* bitmap, const Patch* mesh, + Rect uv(fmax(0.0f, src.left / texture->width), + fmax(0.0f, src.top / texture->height), + fmin(1.0f, src.right / texture->width), + fmin(1.0f, src.bottom / texture->height)); + + int textureFillFlags = static_cast<int>((bitmap->colorType() == kAlpha_8_SkColorType) + ? TextureFillFlags::kIsAlphaMaskTexture : TextureFillFlags::kNone); + Glop glop; + GlopBuilder(mRenderState, mCaches, &glop) + .setMeshTexturedUvQuad(texture->uvMapper, uv) + .setFillTexturePaint(*texture, textureFillFlags, paint, currentSnapshot()->alpha) + .setTransform(currentSnapshot()->getOrthoMatrix(), *currentTransform(), false) + .setModelViewMapUnitToRectSnap(dst) + .setRoundRectClipState(currentSnapshot()->roundRectClipState) + .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 (quickRejectSetupScissor(left, top, right, bottom)) { - return DrawGlInfo::kStatusDone; - } - - if (CC_LIKELY(mesh && mesh->verticesCount > 0)) { - mCaches.activeTexture(0); - Texture* texture = entry ? entry->texture : mCaches.textureCache.get(bitmap); - if (!texture) return DrawGlInfo::kStatusDone; - const AutoTexture autoCleanup(texture); - - texture->setWrap(GL_CLAMP_TO_EDGE, true); - texture->setFilter(GL_LINEAR, true); - - const bool pureTranslate = currentTransform()->isPureTranslate(); - // Mark the current layer dirty where we are going to draw the patch - if (hasLayer() && mesh->hasEmptyQuads) { - const float offsetX = left + currentTransform()->getTranslateX(); - const float offsetY = top + currentTransform()->getTranslateY(); - const size_t count = mesh->quads.size(); - for (size_t i = 0; i < count; i++) { - const Rect& bounds = mesh->quads.itemAt(i); - if (CC_LIKELY(pureTranslate)) { - const float x = (int) floorf(bounds.left + offsetX + 0.5f); - const float y = (int) floorf(bounds.top + offsetY + 0.5f); - dirtyLayer(x, y, x + bounds.getWidth(), y + bounds.getHeight()); - } else { - dirtyLayer(left + bounds.left, top + bounds.top, - left + bounds.right, top + bounds.bottom, *currentTransform()); - } - } - } + if (!mesh || !mesh->verticesCount || quickRejectSetupScissor(left, top, right, bottom)) { + return; + } - bool ignoreTransform = false; - if (CC_LIKELY(pureTranslate)) { - const float x = (int) floorf(left + currentTransform()->getTranslateX() + 0.5f); - const float y = (int) floorf(top + currentTransform()->getTranslateY() + 0.5f); + Texture* texture = entry ? entry->texture : mCaches.textureCache.get(bitmap); + if (!texture) return; - right = x + right - left; - bottom = y + bottom - top; - left = x; - top = y; - ignoreTransform = true; - } - drawIndexedTextureMesh(left, top, right, bottom, texture->id, paint, - texture->blend, (GLvoid*) mesh->offset, (GLvoid*) mesh->textureOffset, - GL_TRIANGLES, mesh->indexCount, false, ignoreTransform, - mCaches.patchCache.getMeshBuffer(), kModelViewMode_Translate, !mesh->hasEmptyQuads); + // 9 patches are built for stretching - always filter + int textureFillFlags = static_cast<int>(TextureFillFlags::kForceFilter); + if (bitmap->colorType() == kAlpha_8_SkColorType) { + textureFillFlags |= TextureFillFlags::kIsAlphaMaskTexture; } - - return DrawGlInfo::kStatusDrew; + Glop glop; + GlopBuilder(mRenderState, mCaches, &glop) + .setMeshPatchQuads(*mesh) + .setFillTexturePaint(*texture, textureFillFlags, paint, currentSnapshot()->alpha) + .setTransform(currentSnapshot()->getOrthoMatrix(), *currentTransform(), false) + .setModelViewOffsetRectSnap(left, top, Rect(0, 0, right - left, bottom - top)) // TODO: get minimal bounds from patch + .setRoundRectClipState(currentSnapshot()->roundRectClipState) + .build(); + renderGlop(glop); } /** @@ -2307,87 +1700,49 @@ status_t OpenGLRenderer::drawPatch(const SkBitmap* bitmap, const Patch* mesh, * will not set the scissor enable or dirty the current layer, if any. * The caller is responsible for properly dirtying the current layer. */ -status_t OpenGLRenderer::drawPatches(const SkBitmap* bitmap, AssetAtlas::Entry* entry, - TextureVertex* vertices, uint32_t indexCount, const SkPaint* paint) { - mCaches.activeTexture(0); +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 DrawGlInfo::kStatusDone; + if (!texture) return; const AutoTexture autoCleanup(texture); - texture->setWrap(GL_CLAMP_TO_EDGE, true); - texture->setFilter(GL_LINEAR, true); - - drawIndexedTextureMesh(0.0f, 0.0f, 1.0f, 1.0f, texture->id, paint, - texture->blend, &vertices[0].x, &vertices[0].u, - GL_TRIANGLES, indexCount, false, true, 0, kModelViewMode_Translate, false); - - return DrawGlInfo::kStatusDrew; + // TODO: get correct bounds from caller + // 9 patches are built for stretching - always filter + int textureFillFlags = static_cast<int>(TextureFillFlags::kForceFilter); + if (bitmap->colorType() == kAlpha_8_SkColorType) { + textureFillFlags |= TextureFillFlags::kIsAlphaMaskTexture; + } + Glop glop; + GlopBuilder(mRenderState, mCaches, &glop) + .setMeshTexturedIndexedQuads(vertices, elementCount) + .setFillTexturePaint(*texture, textureFillFlags, paint, currentSnapshot()->alpha) + .setTransform(currentSnapshot()->getOrthoMatrix(), Matrix4::identity(), false) + .setModelViewOffsetRect(0, 0, Rect(0, 0, 0, 0)) + .setRoundRectClipState(currentSnapshot()->roundRectClipState) + .build(); + renderGlop(glop); } -status_t OpenGLRenderer::drawVertexBuffer(float translateX, float translateY, +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 DrawGlInfo::kStatusDone; - } - - Rect bounds(vertexBuffer.getBounds()); - bounds.translate(translateX, translateY); - dirtyLayer(bounds.left, bounds.top, bounds.right, bounds.bottom, *currentTransform()); - - int color = paint->getColor(); - bool isAA = paint->isAntiAlias(); - - setupDraw(); - setupDrawNoTexture(); - if (isAA) setupDrawVertexAlpha((displayFlags & kVertexBuffer_ShadowInterp)); - setupDrawColor(color, ((color >> 24) & 0xFF) * mSnapshot->alpha); - setupDrawColorFilter(getColorFilter(paint)); - setupDrawShader(getShader(paint)); - setupDrawBlending(paint, isAA); - setupDrawProgram(); - setupDrawModelView(kModelViewMode_Translate, (displayFlags & kVertexBuffer_Offset), - translateX, translateY, 0, 0); - setupDrawColorUniforms(getShader(paint)); - setupDrawColorFilterUniforms(getColorFilter(paint)); - setupDrawShaderUniforms(getShader(paint)); - - const void* vertices = vertexBuffer.getBuffer(); - bool force = mCaches.unbindMeshBuffer(); - mCaches.bindPositionVertexPointer(true, vertices, isAA ? gAlphaVertexStride : gVertexStride); - mCaches.resetTexCoordsVertexPointer(); - - int alphaSlot = -1; - if (isAA) { - void* alphaCoords = ((GLbyte*) vertices) + gVertexAlphaOffset; - alphaSlot = mCaches.currentProgram->getAttrib("vtxAlpha"); - // TODO: avoid enable/disable in back to back uses of the alpha attribute - glEnableVertexAttribArray(alphaSlot); - glVertexAttribPointer(alphaSlot, 1, GL_FLOAT, GL_FALSE, gAlphaVertexStride, alphaCoords); - } - - const VertexBuffer::Mode mode = vertexBuffer.getMode(); - if (mode == VertexBuffer::kStandard) { - mCaches.unbindIndicesBuffer(); - glDrawArrays(GL_TRIANGLE_STRIP, 0, vertexBuffer.getVertexCount()); - } else if (mode == VertexBuffer::kOnePolyRingShadow) { - mCaches.bindShadowIndicesBuffer(); - glDrawElements(GL_TRIANGLE_STRIP, ONE_POLY_RING_SHADOW_INDEX_COUNT, GL_UNSIGNED_SHORT, 0); - } else if (mode == VertexBuffer::kTwoPolyRingShadow) { - mCaches.bindShadowIndicesBuffer(); - glDrawElements(GL_TRIANGLE_STRIP, TWO_POLY_RING_SHADOW_INDEX_COUNT, GL_UNSIGNED_SHORT, 0); - } else if (mode == VertexBuffer::kIndices) { - mCaches.unbindIndicesBuffer(); - glDrawElements(GL_TRIANGLE_STRIP, vertexBuffer.getIndexCount(), GL_UNSIGNED_SHORT, - vertexBuffer.getIndices()); - } - - if (isAA) { - glDisableVertexAttribArray(alphaSlot); - } - - return DrawGlInfo::kStatusDrew; + return; + } + + bool fudgeOffset = displayFlags & kVertexBuffer_Offset; + bool shadowInterp = displayFlags & kVertexBuffer_ShadowInterp; + Glop glop; + GlopBuilder(mRenderState, mCaches, &glop) + .setMeshVertexBuffer(vertexBuffer, shadowInterp) + .setFillPaint(*paint, currentSnapshot()->alpha) + .setTransform(currentSnapshot()->getOrthoMatrix(), *currentTransform(), fudgeOffset) + .setModelViewOffsetRect(translateX, translateY, vertexBuffer.getBounds()) + .setRoundRectClipState(currentSnapshot()->roundRectClipState) + .build(); + renderGlop(glop); } /** @@ -2399,11 +1754,11 @@ status_t OpenGLRenderer::drawVertexBuffer(float translateX, float translateY, * * Doesn't yet support joins, caps, or path effects. */ -status_t OpenGLRenderer::drawConvexPath(const SkPath& path, const SkPaint* paint) { +void OpenGLRenderer::drawConvexPath(const SkPath& path, const SkPaint* paint) { VertexBuffer vertexBuffer; // TODO: try clipping large paths to viewport PathTessellator::tessellatePath(path, paint, *currentTransform(), vertexBuffer); - return drawVertexBuffer(vertexBuffer, paint); + drawVertexBuffer(vertexBuffer, paint); } /** @@ -2417,8 +1772,8 @@ status_t OpenGLRenderer::drawConvexPath(const SkPath& path, const SkPaint* paint * 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. */ -status_t OpenGLRenderer::drawLines(const float* points, int count, const SkPaint* paint) { - if (currentSnapshot()->isIgnored() || count < 4) return DrawGlInfo::kStatusDone; +void OpenGLRenderer::drawLines(const float* points, int count, const SkPaint* paint) { + if (mState.currentlyIgnored() || count < 4) return; count &= ~0x3; // round down to nearest four @@ -2427,15 +1782,15 @@ status_t OpenGLRenderer::drawLines(const float* points, int count, const SkPaint const Rect& bounds = buffer.getBounds(); if (quickRejectSetupScissor(bounds.left, bounds.top, bounds.right, bounds.bottom)) { - return DrawGlInfo::kStatusDone; + return; } int displayFlags = paint->isAntiAlias() ? 0 : kVertexBuffer_Offset; - return drawVertexBuffer(buffer, paint, displayFlags); + drawVertexBuffer(buffer, paint, displayFlags); } -status_t OpenGLRenderer::drawPoints(const float* points, int count, const SkPaint* paint) { - if (currentSnapshot()->isIgnored() || count < 2) return DrawGlInfo::kStatusDone; +void OpenGLRenderer::drawPoints(const float* points, int count, const SkPaint* paint) { + if (mState.currentlyIgnored() || count < 2) return; count &= ~0x1; // round down to nearest two @@ -2444,18 +1799,20 @@ status_t OpenGLRenderer::drawPoints(const float* points, int count, const SkPain const Rect& bounds = buffer.getBounds(); if (quickRejectSetupScissor(bounds.left, bounds.top, bounds.right, bounds.bottom)) { - return DrawGlInfo::kStatusDone; + return; } int displayFlags = paint->isAntiAlias() ? 0 : kVertexBuffer_Offset; - return drawVertexBuffer(buffer, paint, displayFlags); + drawVertexBuffer(buffer, paint, displayFlags); + + mDirty = true; } -status_t OpenGLRenderer::drawColor(int color, SkXfermode::Mode mode) { +void OpenGLRenderer::drawColor(int color, SkXfermode::Mode mode) { // No need to check against the clip, we fill the clip region - if (currentSnapshot()->isIgnored()) return DrawGlInfo::kStatusDone; + if (mState.currentlyIgnored()) return; - Rect clip(*currentClipRect()); + Rect clip(mState.currentClipRect()); clip.snapToPixelBoundaries(); SkPaint paint; @@ -2464,12 +1821,12 @@ status_t OpenGLRenderer::drawColor(int color, SkXfermode::Mode mode) { drawColorRect(clip.left, clip.top, clip.right, clip.bottom, &paint, true); - return DrawGlInfo::kStatusDrew; + mDirty = true; } -status_t OpenGLRenderer::drawShape(float left, float top, const PathTexture* texture, +void OpenGLRenderer::drawShape(float left, float top, PathTexture* texture, const SkPaint* paint) { - if (!texture) return DrawGlInfo::kStatusDone; + if (!texture) return; const AutoTexture autoCleanup(texture); const float x = left + texture->left - texture->offset; @@ -2477,89 +1834,89 @@ status_t OpenGLRenderer::drawShape(float left, float top, const PathTexture* tex drawPathTexture(texture, x, y, paint); - return DrawGlInfo::kStatusDrew; + mDirty = true; } -status_t OpenGLRenderer::drawRoundRect(float left, float top, float right, float bottom, +void OpenGLRenderer::drawRoundRect(float left, float top, float right, float bottom, float rx, float ry, const SkPaint* p) { - if (currentSnapshot()->isIgnored() + if (mState.currentlyIgnored() || quickRejectSetupScissor(left, top, right, bottom, p) - || paintWillNotDraw(*p)) { - return DrawGlInfo::kStatusDone; + || PaintUtils::paintWillNotDraw(*p)) { + return; } - if (p->getPathEffect() != 0) { - mCaches.activeTexture(0); - const PathTexture* texture = mCaches.pathCache.getRoundRect( + if (p->getPathEffect() != nullptr) { + mCaches.textureState().activateTexture(0); + PathTexture* texture = mCaches.pathCache.getRoundRect( right - left, bottom - top, rx, ry, p); - return drawShape(left, top, texture, 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); } - - const VertexBuffer* vertexBuffer = mCaches.tessellationCache.getRoundRect( - *currentTransform(), *p, right - left, bottom - top, rx, ry); - return drawVertexBuffer(left, top, *vertexBuffer, p); } -status_t OpenGLRenderer::drawCircle(float x, float y, float radius, const SkPaint* p) { - if (currentSnapshot()->isIgnored() +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) - || paintWillNotDraw(*p)) { - return DrawGlInfo::kStatusDone; - } - if (p->getPathEffect() != 0) { - mCaches.activeTexture(0); - const PathTexture* texture = mCaches.pathCache.getCircle(radius, p); - return drawShape(x - radius, y - radius, texture, p); + || PaintUtils::paintWillNotDraw(*p)) { + return; } - - SkPath path; - if (p->getStyle() == SkPaint::kStrokeAndFill_Style) { - path.addCircle(x, y, radius + p->getStrokeWidth() / 2); + if (p->getPathEffect() != nullptr) { + mCaches.textureState().activateTexture(0); + PathTexture* texture = mCaches.pathCache.getCircle(radius, p); + drawShape(x - radius, y - radius, texture, p); } else { - path.addCircle(x, y, radius); + SkPath path; + if (p->getStyle() == SkPaint::kStrokeAndFill_Style) { + path.addCircle(x, y, radius + p->getStrokeWidth() / 2); + } else { + path.addCircle(x, y, radius); + } + drawConvexPath(path, p); } - return drawConvexPath(path, p); } -status_t OpenGLRenderer::drawOval(float left, float top, float right, float bottom, +void OpenGLRenderer::drawOval(float left, float top, float right, float bottom, const SkPaint* p) { - if (currentSnapshot()->isIgnored() + if (mState.currentlyIgnored() || quickRejectSetupScissor(left, top, right, bottom, p) - || paintWillNotDraw(*p)) { - return DrawGlInfo::kStatusDone; - } - - if (p->getPathEffect() != 0) { - mCaches.activeTexture(0); - const PathTexture* texture = mCaches.pathCache.getOval(right - left, bottom - top, p); - return drawShape(left, top, texture, p); + || PaintUtils::paintWillNotDraw(*p)) { + return; } - SkPath path; - SkRect rect = SkRect::MakeLTRB(left, top, right, bottom); - if (p->getStyle() == SkPaint::kStrokeAndFill_Style) { - rect.outset(p->getStrokeWidth() / 2, p->getStrokeWidth() / 2); + 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); } - path.addOval(rect); - return drawConvexPath(path, p); } -status_t OpenGLRenderer::drawArc(float left, float top, float right, float bottom, +void OpenGLRenderer::drawArc(float left, float top, float right, float bottom, float startAngle, float sweepAngle, bool useCenter, const SkPaint* p) { - if (currentSnapshot()->isIgnored() + if (mState.currentlyIgnored() || quickRejectSetupScissor(left, top, right, bottom, p) - || paintWillNotDraw(*p)) { - return DrawGlInfo::kStatusDone; + || PaintUtils::paintWillNotDraw(*p)) { + return; } // TODO: support fills (accounting for concavity if useCenter && sweepAngle > 180) - if (p->getStyle() != SkPaint::kStroke_Style || p->getPathEffect() != 0 || useCenter) { - mCaches.activeTexture(0); - const PathTexture* texture = mCaches.pathCache.getArc(right - left, bottom - top, + 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); - return drawShape(left, top, texture, 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); @@ -2573,53 +1930,54 @@ status_t OpenGLRenderer::drawArc(float left, float top, float right, float botto if (useCenter) { path.close(); } - return drawConvexPath(path, p); + drawConvexPath(path, p); } // See SkPaintDefaults.h #define SkPaintDefaults_MiterLimit SkIntToScalar(4) -status_t OpenGLRenderer::drawRect(float left, float top, float right, float bottom, +void OpenGLRenderer::drawRect(float left, float top, float right, float bottom, const SkPaint* p) { - if (currentSnapshot()->isIgnored() + if (mState.currentlyIgnored() || quickRejectSetupScissor(left, top, right, bottom, p) - || paintWillNotDraw(*p)) { - return DrawGlInfo::kStatusDone; + || PaintUtils::paintWillNotDraw(*p)) { + return; } if (p->getStyle() != SkPaint::kFill_Style) { // only fill style is supported by drawConvexPath, since others have to handle joins - if (p->getPathEffect() != 0 || p->getStrokeJoin() != SkPaint::kMiter_Join || + if (p->getPathEffect() != nullptr || p->getStrokeJoin() != SkPaint::kMiter_Join || p->getStrokeMiter() != SkPaintDefaults_MiterLimit) { - mCaches.activeTexture(0); - const PathTexture* texture = + mCaches.textureState().activateTexture(0); + PathTexture* texture = mCaches.pathCache.getRect(right - left, bottom - top, p); - return drawShape(left, top, texture, 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); - SkPath path; - SkRect rect = SkRect::MakeLTRB(left, top, right, bottom); - if (p->getStyle() == SkPaint::kStrokeAndFill_Style) { - rect.outset(p->getStrokeWidth() / 2, p->getStrokeWidth() / 2); + mDirty = true; } - path.addRect(rect); - return drawConvexPath(path, p); - } - - if (p->isAntiAlias() && !currentTransform()->isSimple()) { - SkPath path; - path.addRect(left, top, right, bottom); - return drawConvexPath(path, p); - } else { - drawColorRect(left, top, right, bottom, p); - return DrawGlInfo::kStatusDrew; } } void OpenGLRenderer::drawTextShadow(const SkPaint* paint, const char* text, int bytesCount, int count, const float* positions, FontRenderer& fontRenderer, int alpha, float x, float y) { - mCaches.activeTexture(0); + mCaches.textureState().activateTexture(0); TextShadow textShadow; if (!getTextShadow(paint, &textShadow)) { @@ -2629,63 +1987,52 @@ void OpenGLRenderer::drawTextShadow(const SkPaint* paint, const char* text, // NOTE: The drop shadow will not perform gamma correction // if shader-based correction is enabled mCaches.dropShadowCache.setFontRenderer(fontRenderer); - const ShadowTexture* shadow = mCaches.dropShadowCache.get( + ShadowTexture* texture = mCaches.dropShadowCache.get( paint, text, bytesCount, count, textShadow.radius, positions); // If the drop shadow exceeds the max texture size or couldn't be // allocated, skip drawing - if (!shadow) return; - const AutoTexture autoCleanup(shadow); - - const float sx = x - shadow->left + textShadow.dx; - const float sy = y - shadow->top + textShadow.dy; - - const int shadowAlpha = ((textShadow.color >> 24) & 0xFF) * mSnapshot->alpha; - if (getShader(paint)) { - textShadow.color = SK_ColorWHITE; - } + if (!texture) return; + const AutoTexture autoCleanup(texture); - setupDraw(); - setupDrawWithTexture(true); - setupDrawAlpha8Color(textShadow.color, shadowAlpha < 255 ? shadowAlpha : alpha); - setupDrawColorFilter(getColorFilter(paint)); - setupDrawShader(getShader(paint)); - setupDrawBlending(paint, true); - setupDrawProgram(); - setupDrawModelView(kModelViewMode_TranslateAndScale, false, - sx, sy, sx + shadow->width, sy + shadow->height); - setupDrawTexture(shadow->id); - setupDrawPureColorUniforms(); - setupDrawColorFilterUniforms(getColorFilter(paint)); - setupDrawShaderUniforms(getShader(paint)); - setupDrawMesh(NULL, (GLvoid*) gMeshTextureOffset); + const float sx = x - texture->left + textShadow.dx; + const float sy = y - texture->top + textShadow.dy; - glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount); + Glop glop; + GlopBuilder(mRenderState, mCaches, &glop) + .setMeshTexturedUnitQuad(nullptr) + .setFillShadowTexturePaint(*texture, textShadow.color, *paint, currentSnapshot()->alpha) + .setTransform(currentSnapshot()->getOrthoMatrix(), *currentTransform(), false) + .setModelViewMapUnitToRect(Rect(sx, sy, sx + texture->width, sy + texture->height)) + .setRoundRectClipState(currentSnapshot()->roundRectClipState) + .build(); + renderGlop(glop); } bool OpenGLRenderer::canSkipText(const SkPaint* paint) const { - float alpha = (hasTextShadow(paint) ? 1.0f : paint->getAlpha()) * mSnapshot->alpha; - return alpha == 0.0f && getXfermode(paint->getXfermode()) == SkXfermode::kSrcOver_Mode; + float alpha = (hasTextShadow(paint) ? 1.0f : paint->getAlpha()) * currentSnapshot()->alpha; + return MathUtils::isZero(alpha) + && PaintUtils::getXfermode(paint->getXfermode()) == SkXfermode::kSrcOver_Mode; } -status_t OpenGLRenderer::drawPosText(const char* text, int bytesCount, int count, +void OpenGLRenderer::drawPosText(const char* text, int bytesCount, int count, const float* positions, const SkPaint* paint) { - if (text == NULL || count == 0 || currentSnapshot()->isIgnored() || canSkipText(paint)) { - return DrawGlInfo::kStatusDone; + if (text == nullptr || count == 0 || mState.currentlyIgnored() || canSkipText(paint)) { + return; } // NOTE: Skia does not support perspective transform on drawPosText yet if (!currentTransform()->isSimple()) { - return DrawGlInfo::kStatusDone; + return; } - mCaches.enableScissor(); + mRenderState.scissor().setEnabled(true); float x = 0.0f; float y = 0.0f; const bool pureTranslate = currentTransform()->isPureTranslate(); if (pureTranslate) { - x = (int) floorf(x + currentTransform()->getTranslateX() + 0.5f); - y = (int) floorf(y + currentTransform()->getTranslateY() + 0.5f); + x = floorf(x + currentTransform()->getTranslateX() + 0.5f); + y = floorf(y + currentTransform()->getTranslateY() + 0.5f); } FontRenderer& fontRenderer = mCaches.fontRenderer->getFontRenderer(paint); @@ -2707,23 +2054,16 @@ status_t OpenGLRenderer::drawPosText(const char* text, int bytesCount, int count } fontRenderer.setTextureFiltering(linearFilter); - const Rect* clip = pureTranslate ? mSnapshot->clipRect : &mSnapshot->getLocalClip(); + const Rect& clip(pureTranslate ? writableSnapshot()->getClipRect() : writableSnapshot()->getLocalClip()); Rect bounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f); - const bool hasActiveLayer = hasLayer(); - - TextSetupFunctor functor(this, x, y, pureTranslate, alpha, mode, paint); - if (fontRenderer.renderPosText(paint, clip, text, 0, bytesCount, count, x, y, - positions, hasActiveLayer ? &bounds : NULL, &functor)) { - if (hasActiveLayer) { - if (!pureTranslate) { - currentTransform()->mapRect(bounds); - } - dirtyLayerUnchecked(bounds, getRegion()); - } + TextDrawFunctor functor(this, x, y, pureTranslate, alpha, mode, paint); + if (fontRenderer.renderPosText(paint, &clip, text, 0, bytesCount, count, x, y, + positions, hasLayer() ? &bounds : nullptr, &functor)) { + dirtyLayer(bounds.left, bounds.top, bounds.right, bounds.bottom, *currentTransform()); + mDirty = true; } - return DrawGlInfo::kStatusDrew; } bool OpenGLRenderer::findBestFontTransform(const mat4& transform, SkMatrix* outMatrix) const { @@ -2747,16 +2087,77 @@ bool OpenGLRenderer::findBestFontTransform(const mat4& transform, SkMatrix* outM return true; } -status_t OpenGLRenderer::drawText(const char* text, int bytesCount, int count, float x, float y, +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::setMatrix(const Matrix4& matrix) { + mState.setMatrix(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::drawText(const char* text, int bytesCount, int count, float x, float y, const float* positions, const SkPaint* paint, float totalAdvance, const Rect& bounds, DrawOpMode drawOpMode) { - if (drawOpMode == kDrawOpMode_Immediate) { + 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 (text == NULL || count == 0 || currentSnapshot()->isIgnored() || canSkipText(paint) || + if (text == nullptr || count == 0 || mState.currentlyIgnored() || canSkipText(paint) || quickRejectSetupScissor(bounds)) { - return DrawGlInfo::kStatusDone; + return; } } @@ -2767,8 +2168,8 @@ status_t OpenGLRenderer::drawText(const char* text, int bytesCount, int count, f const bool pureTranslate = transform.isPureTranslate(); if (CC_LIKELY(pureTranslate)) { - x = (int) floorf(x + transform.getTranslateX() + 0.5f); - y = (int) floorf(y + transform.getTranslateY() + 0.5f); + x = floorf(x + transform.getTranslateX() + 0.5f); + y = floorf(y + transform.getTranslateY() + 0.5f); } int alpha; @@ -2804,25 +2205,25 @@ status_t OpenGLRenderer::drawText(const char* text, int bytesCount, int count, f fontRenderer.setTextureFiltering(linearFilter); // TODO: Implement better clipping for scaled/rotated text - const Rect* clip = !pureTranslate ? NULL : currentClipRect(); + const Rect* clip = !pureTranslate ? nullptr : &mState.currentClipRect(); Rect layerBounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f); bool status; - TextSetupFunctor functor(this, x, y, pureTranslate, alpha, mode, paint); + TextDrawFunctor functor(this, x, y, pureTranslate, alpha, mode, paint); // don't call issuedrawcommand, do it at end of batch - bool forceFinish = (drawOpMode != kDrawOpMode_Defer); + 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, text, 0, bytesCount, count, x, y, - positions, hasActiveLayer ? &layerBounds : NULL, &functor, forceFinish); + positions, hasActiveLayer ? &layerBounds : nullptr, &functor, forceFinish); } else { status = fontRenderer.renderPosText(paint, clip, text, 0, bytesCount, count, x, y, - positions, hasActiveLayer ? &layerBounds : NULL, &functor, forceFinish); + positions, hasActiveLayer ? &layerBounds : nullptr, &functor, forceFinish); } - if ((status || drawOpMode != kDrawOpMode_Immediate) && hasActiveLayer) { + if ((status || drawOpMode != DrawOpMode::kImmediate) && hasActiveLayer) { if (!pureTranslate) { transform.mapRect(layerBounds); } @@ -2831,17 +2232,17 @@ status_t OpenGLRenderer::drawText(const char* text, int bytesCount, int count, f drawTextDecorations(totalAdvance, oldX, oldY, paint); - return DrawGlInfo::kStatusDrew; + mDirty = true; } -status_t OpenGLRenderer::drawTextOnPath(const char* text, int bytesCount, int count, +void OpenGLRenderer::drawTextOnPath(const char* text, int bytesCount, int count, const SkPath* path, float hOffset, float vOffset, const SkPaint* paint) { - if (text == NULL || count == 0 || currentSnapshot()->isIgnored() || canSkipText(paint)) { - return DrawGlInfo::kStatusDone; + if (text == nullptr || count == 0 || mState.currentlyIgnored() || canSkipText(paint)) { + return; } // TODO: avoid scissor by calculating maximum bounds using path bounds + font metrics - mCaches.enableScissor(); + mRenderState.scissor().setEnabled(true); FontRenderer& fontRenderer = mCaches.fontRenderer->getFontRenderer(paint); fontRenderer.setFont(paint, SkMatrix::I()); @@ -2850,47 +2251,40 @@ status_t OpenGLRenderer::drawTextOnPath(const char* text, int bytesCount, int co int alpha; SkXfermode::Mode mode; getAlphaAndMode(paint, &alpha, &mode); - TextSetupFunctor functor(this, 0.0f, 0.0f, false, alpha, mode, paint); + TextDrawFunctor functor(this, 0.0f, 0.0f, false, alpha, mode, paint); - const Rect* clip = &mSnapshot->getLocalClip(); + const Rect* clip = &writableSnapshot()->getLocalClip(); Rect bounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f); - const bool hasActiveLayer = hasLayer(); - if (fontRenderer.renderTextOnPath(paint, clip, text, 0, bytesCount, count, path, - hOffset, vOffset, hasActiveLayer ? &bounds : NULL, &functor)) { - if (hasActiveLayer) { - currentTransform()->mapRect(bounds); - dirtyLayerUnchecked(bounds, getRegion()); - } + hOffset, vOffset, hasLayer() ? &bounds : nullptr, &functor)) { + dirtyLayer(bounds.left, bounds.top, bounds.right, bounds.bottom, *currentTransform()); + mDirty = true; } - - return DrawGlInfo::kStatusDrew; } -status_t OpenGLRenderer::drawPath(const SkPath* path, const SkPaint* paint) { - if (currentSnapshot()->isIgnored()) return DrawGlInfo::kStatusDone; +void OpenGLRenderer::drawPath(const SkPath* path, const SkPaint* paint) { + if (mState.currentlyIgnored()) return; - mCaches.activeTexture(0); + mCaches.textureState().activateTexture(0); - const PathTexture* texture = mCaches.pathCache.get(path, paint); - if (!texture) return DrawGlInfo::kStatusDone; + PathTexture* texture = mCaches.pathCache.get(path, paint); + if (!texture) return; const AutoTexture autoCleanup(texture); const float x = texture->left - texture->offset; const float y = texture->top - texture->offset; drawPathTexture(texture, x, y, paint); - - return DrawGlInfo::kStatusDrew; + mDirty = true; } -status_t OpenGLRenderer::drawLayer(Layer* layer, float x, float y) { +void OpenGLRenderer::drawLayer(Layer* layer, float x, float y) { if (!layer) { - return DrawGlInfo::kStatusDone; + return; } - mat4* transform = NULL; + mat4* transform = nullptr; if (layer->isTextureLayer()) { transform = &layer->getTransform(); if (!transform->isIdentity()) { @@ -2900,14 +2294,15 @@ status_t OpenGLRenderer::drawLayer(Layer* layer, float x, float y) { } bool clipRequired = false; - const bool rejected = calculateQuickRejectForScissor(x, y, - x + layer->layer.getWidth(), y + layer->layer.getHeight(), &clipRequired, NULL, false); + const bool rejected = mState.calculateQuickRejectForScissor( + x, y, x + layer->layer.getWidth(), y + layer->layer.getHeight(), + &clipRequired, nullptr, false); if (rejected) { if (transform && !transform->isIdentity()) { restore(); } - return DrawGlInfo::kStatusDone; + return; } EVENT_LOGD("drawLayer," RECT_STRING ", clipRequired %d", x, y, @@ -2915,54 +2310,23 @@ status_t OpenGLRenderer::drawLayer(Layer* layer, float x, float y) { updateLayer(layer, true); - mCaches.setScissorEnabled(mScissorOptimizationDisabled || clipRequired); - mCaches.activeTexture(0); + 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) { - - const float a = getLayerAlpha(layer); - setupDraw(); - setupDrawWithTexture(); - setupDrawColor(a, a, a, a); - setupDrawColorFilter(layer->getColorFilter()); - setupDrawBlending(layer); - setupDrawProgram(); - setupDrawPureColorUniforms(); - setupDrawColorFilterUniforms(layer->getColorFilter()); - setupDrawTexture(layer->getTexture()); - if (CC_LIKELY(currentTransform()->isPureTranslate())) { - int tx = (int) floorf(x + currentTransform()->getTranslateX() + 0.5f); - int ty = (int) floorf(y + currentTransform()->getTranslateY() + 0.5f); - - layer->setFilter(GL_NEAREST); - setupDrawModelView(kModelViewMode_Translate, false, tx, ty, - tx + layer->layer.getWidth(), ty + layer->layer.getHeight(), true); - } else { - layer->setFilter(GL_LINEAR); - setupDrawModelView(kModelViewMode_Translate, false, x, y, - x + layer->layer.getWidth(), y + layer->layer.getHeight()); - } - - TextureVertex* mesh = &layer->mesh[0]; - GLsizei elementsCount = layer->meshElementCount; - - while (elementsCount > 0) { - GLsizei drawCount = min(elementsCount, (GLsizei) gMaxNumberOfQuads * 6); - - setupDrawMeshIndices(&mesh[0].x, &mesh[0].u); - DRAW_DOUBLE_STENCIL_IF(!layer->hasDrawnSinceUpdate, - glDrawElements(GL_TRIANGLES, drawCount, GL_UNSIGNED_SHORT, NULL)); - - elementsCount -= drawCount; - // Though there are 4 vertices in a quad, we use 6 indices per - // quad to draw with GL_TRIANGLES - mesh += (drawCount / 6) * 4; - } - + Glop glop; + GlopBuilder(mRenderState, mCaches, &glop) + .setMeshTexturedIndexedQuads(layer->mesh, layer->meshElementCount) + .setFillLayer(layer->getTexture(), layer->getColorFilter(), getLayerAlpha(layer), layer->getMode(), Blend::ModeOrderSwap::NoSwap) + .setTransform(currentSnapshot()->getOrthoMatrix(), *currentTransform(), false) + .setModelViewOffsetRectSnap(x, y, Rect(0, 0, layer->layer.getWidth(), layer->layer.getHeight())) + .setRoundRectClipState(currentSnapshot()->roundRectClipState) + .build(); + DRAW_DOUBLE_STENCIL_IF(!layer->hasDrawnSinceUpdate, renderGlop(glop)); #if DEBUG_LAYERS_AS_REGIONS drawRegionRectsDebug(layer->region); #endif @@ -2982,53 +2346,16 @@ status_t OpenGLRenderer::drawLayer(Layer* layer, float x, float y) { restore(); } - return DrawGlInfo::kStatusDrew; + mDirty = true; } /////////////////////////////////////////////////////////////////////////////// // Draw filters /////////////////////////////////////////////////////////////////////////////// - -void OpenGLRenderer::resetPaintFilter() { - // when clearing the PaintFilter, the masks should also be cleared for simple DrawModifier - // comparison, see MergingDrawBatch::canMergeWith - mDrawModifiers.mHasDrawFilter = false; - mDrawModifiers.mPaintFilterClearBits = 0; - mDrawModifiers.mPaintFilterSetBits = 0; -} - -void OpenGLRenderer::setupPaintFilter(int clearBits, int setBits) { - // TODO: don't bother with boolean, it's redundant with clear/set bits - mDrawModifiers.mHasDrawFilter = true; - mDrawModifiers.mPaintFilterClearBits = clearBits & SkPaint::kAllFlags; - mDrawModifiers.mPaintFilterSetBits = setBits & SkPaint::kAllFlags; -} - -const SkPaint* OpenGLRenderer::filterPaint(const SkPaint* paint) { - // TODO: use CompatFlagsDrawFilter here, and combine logic with android/graphics/DrawFilter.cpp - // to avoid clobbering 0x02 paint flag - - // Equivalent to the Java Paint's FILTER_BITMAP_FLAG. - static const uint32_t sFilterBitmapFlag = 0x02; - - if (CC_LIKELY(!mDrawModifiers.mHasDrawFilter || !paint)) { - return paint; - } - - const uint32_t clearBits = mDrawModifiers.mPaintFilterClearBits; - const uint32_t setBits = mDrawModifiers.mPaintFilterSetBits; - - const uint32_t flags = (paint->getFlags() & ~clearBits) | setBits; - mFilteredPaint = *paint; - mFilteredPaint.setFlags(flags); - - // check if paint filter trying to override bitmap filter - if ((clearBits | setBits) & sFilterBitmapFlag) { - mFilteredPaint.setFilterLevel(flags & sFilterBitmapFlag - ? SkPaint::kLow_FilterLevel : SkPaint::kNone_FilterLevel); - } - - return &mFilteredPaint; +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"); } /////////////////////////////////////////////////////////////////////////////// @@ -3043,32 +2370,21 @@ Texture* OpenGLRenderer::getTexture(const SkBitmap* bitmap) { return texture; } -void OpenGLRenderer::drawPathTexture(const PathTexture* texture, - float x, float y, const SkPaint* paint) { +void OpenGLRenderer::drawPathTexture(PathTexture* texture, float x, float y, + const SkPaint* paint) { if (quickRejectSetupScissor(x, y, x + texture->width, y + texture->height)) { return; } - int alpha; - SkXfermode::Mode mode; - getAlphaAndMode(paint, &alpha, &mode); - - setupDraw(); - setupDrawWithTexture(true); - setupDrawAlpha8Color(paint->getColor(), alpha); - setupDrawColorFilter(getColorFilter(paint)); - setupDrawShader(getShader(paint)); - setupDrawBlending(paint, true); - setupDrawProgram(); - setupDrawModelView(kModelViewMode_TranslateAndScale, false, - x, y, x + texture->width, y + texture->height); - setupDrawTexture(texture->id); - setupDrawPureColorUniforms(); - setupDrawColorFilterUniforms(getColorFilter(paint)); - setupDrawShaderUniforms(getShader(paint)); - setupDrawMesh(NULL, (GLvoid*) gMeshTextureOffset); - - glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount); + Glop glop; + GlopBuilder(mRenderState, mCaches, &glop) + .setMeshTexturedUnitQuad(nullptr) + .setFillPathTexturePaint(*texture, *paint, currentSnapshot()->alpha) + .setTransform(currentSnapshot()->getOrthoMatrix(), *currentTransform(), false) + .setModelViewMapUnitToRect(Rect(x, y, x + texture->width, y + texture->height)) + .setRoundRectClipState(currentSnapshot()->roundRectClipState) + .build(); + renderGlop(glop); } // Same values used by Skia @@ -3121,28 +2437,20 @@ void OpenGLRenderer::drawTextDecorations(float underlineWidth, float x, float y, } } -status_t OpenGLRenderer::drawRects(const float* rects, int count, const SkPaint* paint) { - if (currentSnapshot()->isIgnored()) { - return DrawGlInfo::kStatusDone; +void OpenGLRenderer::drawRects(const float* rects, int count, const SkPaint* paint) { + if (mState.currentlyIgnored()) { + return; } - return drawColorRects(rects, count, paint, false, true, true); + drawColorRects(rects, count, paint, false, true, true); } -static void mapPointFakeZ(Vector3& point, const mat4& transformXY, const mat4& transformZ) { - // map z coordinate with true 3d matrix - point.z = transformZ.mapZ(point); - - // map x,y coordinates with draw/Skia matrix - transformXY.mapPoint(point.x, point.y); -} - -status_t OpenGLRenderer::drawShadow(float casterAlpha, +void OpenGLRenderer::drawShadow(float casterAlpha, const VertexBuffer* ambientShadowVertexBuffer, const VertexBuffer* spotShadowVertexBuffer) { - if (currentSnapshot()->isIgnored()) return DrawGlInfo::kStatusDone; + if (mState.currentlyIgnored()) return; // TODO: use quickRejectWithScissor. For now, always force enable scissor. - mCaches.enableScissor(); + mRenderState.scissor().setEnabled(true); SkPaint paint; paint.setAntiAlias(true); // want to use AlphaVertex @@ -3166,19 +2474,13 @@ status_t OpenGLRenderer::drawShadow(float casterAlpha, drawVertexBuffer(*spotShadowVertexBuffer, &paint, kVertexBuffer_ShadowInterp); } - return DrawGlInfo::kStatusDrew; + mDirty=true; } -status_t OpenGLRenderer::drawColorRects(const float* rects, int count, const SkPaint* paint, +void OpenGLRenderer::drawColorRects(const float* rects, int count, const SkPaint* paint, bool ignoreTransform, bool dirty, bool clip) { if (count == 0) { - return DrawGlInfo::kStatusDone; - } - - int color = paint->getColor(); - // If a shader is set, preserve only the alpha - if (getShader(paint)) { - color |= 0x00ffffff; + return; } float left = FLT_MAX; @@ -3207,247 +2509,37 @@ status_t OpenGLRenderer::drawColorRects(const float* rects, int count, const SkP } if (clip && quickRejectSetupScissor(left, top, right, bottom)) { - return DrawGlInfo::kStatusDone; - } - - setupDraw(); - setupDrawNoTexture(); - setupDrawColor(color, ((color >> 24) & 0xFF) * currentSnapshot()->alpha); - setupDrawShader(getShader(paint)); - setupDrawColorFilter(getColorFilter(paint)); - setupDrawBlending(paint); - setupDrawProgram(); - setupDrawDirtyRegionsDisabled(); - setupDrawModelView(kModelViewMode_Translate, false, - 0.0f, 0.0f, 0.0f, 0.0f, ignoreTransform); - setupDrawColorUniforms(getShader(paint)); - setupDrawShaderUniforms(getShader(paint)); - setupDrawColorFilterUniforms(getColorFilter(paint)); - - if (dirty && hasLayer()) { - dirtyLayer(left, top, right, bottom, *currentTransform()); + return; } - issueIndexedQuadDraw(&mesh[0], count / 4); - - return DrawGlInfo::kStatusDrew; + const Matrix4& transform = ignoreTransform ? Matrix4::identity() : *currentTransform(); + Glop glop; + GlopBuilder(mRenderState, mCaches, &glop) + .setMeshIndexedQuads(&mesh[0], count / 4) + .setFillPaint(*paint, currentSnapshot()->alpha) + .setTransform(currentSnapshot()->getOrthoMatrix(), transform, false) + .setModelViewOffsetRect(0, 0, Rect(left, top, right, bottom)) + .setRoundRectClipState(currentSnapshot()->roundRectClipState) + .build(); + renderGlop(glop); } void OpenGLRenderer::drawColorRect(float left, float top, float right, float bottom, const SkPaint* paint, bool ignoreTransform) { - int color = paint->getColor(); - // If a shader is set, preserve only the alpha - if (getShader(paint)) { - color |= 0x00ffffff; - } - - setupDraw(); - setupDrawNoTexture(); - setupDrawColor(color, ((color >> 24) & 0xFF) * currentSnapshot()->alpha); - setupDrawShader(getShader(paint)); - setupDrawColorFilter(getColorFilter(paint)); - setupDrawBlending(paint); - setupDrawProgram(); - setupDrawModelView(kModelViewMode_TranslateAndScale, false, - left, top, right, bottom, ignoreTransform); - setupDrawColorUniforms(getShader(paint)); - setupDrawShaderUniforms(getShader(paint), ignoreTransform); - setupDrawColorFilterUniforms(getColorFilter(paint)); - setupDrawSimpleMesh(); - - glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount); -} - -void OpenGLRenderer::drawTextureRect(float left, float top, float right, float bottom, - Texture* texture, const SkPaint* paint) { - texture->setWrap(GL_CLAMP_TO_EDGE, true); - - GLvoid* vertices = (GLvoid*) NULL; - GLvoid* texCoords = (GLvoid*) gMeshTextureOffset; - - if (texture->uvMapper) { - vertices = &mMeshVertices[0].x; - texCoords = &mMeshVertices[0].u; - - Rect uvs(0.0f, 0.0f, 1.0f, 1.0f); - texture->uvMapper->map(uvs); - - resetDrawTextureTexCoords(uvs.left, uvs.top, uvs.right, uvs.bottom); - } - - if (CC_LIKELY(currentTransform()->isPureTranslate())) { - const float x = (int) floorf(left + currentTransform()->getTranslateX() + 0.5f); - const float y = (int) floorf(top + currentTransform()->getTranslateY() + 0.5f); - - texture->setFilter(GL_NEAREST, true); - drawTextureMesh(x, y, x + texture->width, y + texture->height, texture->id, - paint, texture->blend, vertices, texCoords, - GL_TRIANGLE_STRIP, gMeshCount, false, true); - } else { - texture->setFilter(getFilter(paint), true); - drawTextureMesh(left, top, right, bottom, texture->id, paint, - texture->blend, vertices, texCoords, GL_TRIANGLE_STRIP, gMeshCount); - } - - if (texture->uvMapper) { - resetDrawTextureTexCoords(0.0f, 0.0f, 1.0f, 1.0f); - } -} - -void OpenGLRenderer::drawTextureMesh(float left, float top, float right, float bottom, - GLuint texture, const SkPaint* paint, bool blend, - GLvoid* vertices, GLvoid* texCoords, GLenum drawMode, GLsizei elementsCount, - bool swapSrcDst, bool ignoreTransform, GLuint vbo, - ModelViewMode modelViewMode, bool dirty) { - - int a; - SkXfermode::Mode mode; - getAlphaAndMode(paint, &a, &mode); - const float alpha = a / 255.0f; - - setupDraw(); - setupDrawWithTexture(); - setupDrawColor(alpha, alpha, alpha, alpha); - setupDrawColorFilter(getColorFilter(paint)); - setupDrawBlending(paint, blend, swapSrcDst); - setupDrawProgram(); - if (!dirty) setupDrawDirtyRegionsDisabled(); - setupDrawModelView(modelViewMode, false, left, top, right, bottom, ignoreTransform); - setupDrawTexture(texture); - setupDrawPureColorUniforms(); - setupDrawColorFilterUniforms(getColorFilter(paint)); - setupDrawMesh(vertices, texCoords, vbo); - - glDrawArrays(drawMode, 0, elementsCount); -} - -void OpenGLRenderer::drawIndexedTextureMesh(float left, float top, float right, float bottom, - GLuint texture, const SkPaint* paint, bool blend, - GLvoid* vertices, GLvoid* texCoords, GLenum drawMode, GLsizei elementsCount, - bool swapSrcDst, bool ignoreTransform, GLuint vbo, - ModelViewMode modelViewMode, bool dirty) { - - int a; - SkXfermode::Mode mode; - getAlphaAndMode(paint, &a, &mode); - const float alpha = a / 255.0f; - - setupDraw(); - setupDrawWithTexture(); - setupDrawColor(alpha, alpha, alpha, alpha); - setupDrawColorFilter(getColorFilter(paint)); - setupDrawBlending(paint, blend, swapSrcDst); - setupDrawProgram(); - if (!dirty) setupDrawDirtyRegionsDisabled(); - setupDrawModelView(modelViewMode, false, left, top, right, bottom, ignoreTransform); - setupDrawTexture(texture); - setupDrawPureColorUniforms(); - setupDrawColorFilterUniforms(getColorFilter(paint)); - setupDrawMeshIndices(vertices, texCoords, vbo); - - glDrawElements(drawMode, elementsCount, GL_UNSIGNED_SHORT, NULL); -} - -void OpenGLRenderer::drawAlpha8TextureMesh(float left, float top, float right, float bottom, - GLuint texture, const SkPaint* paint, - GLvoid* vertices, GLvoid* texCoords, GLenum drawMode, GLsizei elementsCount, - bool ignoreTransform, ModelViewMode modelViewMode, bool dirty) { - - int color = paint != NULL ? paint->getColor() : 0; - int alpha; - SkXfermode::Mode mode; - getAlphaAndMode(paint, &alpha, &mode); - - setupDraw(); - setupDrawWithTexture(true); - if (paint != NULL) { - setupDrawAlpha8Color(color, alpha); - } - setupDrawColorFilter(getColorFilter(paint)); - setupDrawShader(getShader(paint)); - setupDrawBlending(paint, true); - setupDrawProgram(); - if (!dirty) setupDrawDirtyRegionsDisabled(); - setupDrawModelView(modelViewMode, false, left, top, right, bottom, ignoreTransform); - setupDrawTexture(texture); - setupDrawPureColorUniforms(); - setupDrawColorFilterUniforms(getColorFilter(paint)); - setupDrawShaderUniforms(getShader(paint), ignoreTransform); - setupDrawMesh(vertices, texCoords); - - glDrawArrays(drawMode, 0, elementsCount); -} - -void OpenGLRenderer::chooseBlending(bool blend, SkXfermode::Mode mode, - ProgramDescription& description, bool swapSrcDst) { - - if (mSnapshot->roundRectClipState != NULL /*&& !mSkipOutlineClip*/) { - blend = true; - mDescription.hasRoundRectClip = true; - } - mSkipOutlineClip = true; - - blend = blend || mode != SkXfermode::kSrcOver_Mode; - - if (blend) { - // These blend modes are not supported by OpenGL directly and have - // to be implemented using shaders. Since the shader will perform - // the blending, turn blending off here - // If the blend mode cannot be implemented using shaders, fall - // back to the default SrcOver blend mode instead - if (CC_UNLIKELY(mode > SkXfermode::kScreen_Mode)) { - if (CC_UNLIKELY(mExtensions.hasFramebufferFetch())) { - description.framebufferMode = mode; - description.swapSrcDst = swapSrcDst; - - if (mCaches.blend) { - glDisable(GL_BLEND); - mCaches.blend = false; - } - - return; - } else { - mode = SkXfermode::kSrcOver_Mode; - } - } - - if (!mCaches.blend) { - glEnable(GL_BLEND); - } - - GLenum sourceMode = swapSrcDst ? gBlendsSwap[mode].src : gBlends[mode].src; - GLenum destMode = swapSrcDst ? gBlendsSwap[mode].dst : gBlends[mode].dst; - - if (sourceMode != mCaches.lastSrcMode || destMode != mCaches.lastDstMode) { - glBlendFunc(sourceMode, destMode); - mCaches.lastSrcMode = sourceMode; - mCaches.lastDstMode = destMode; - } - } else if (mCaches.blend) { - glDisable(GL_BLEND); - } - mCaches.blend = blend; -} - -bool OpenGLRenderer::useProgram(Program* program) { - if (!program->isInUse()) { - if (mCaches.currentProgram != NULL) mCaches.currentProgram->remove(); - program->use(); - mCaches.currentProgram = program; - return false; - } - return true; -} - -void OpenGLRenderer::resetDrawTextureTexCoords(float u1, float v1, float u2, float v2) { - TextureVertex* v = &mMeshVertices[0]; - TextureVertex::setUV(v++, u1, v1); - TextureVertex::setUV(v++, u2, v1); - TextureVertex::setUV(v++, u1, v2); - TextureVertex::setUV(v++, u2, v2); -} - -void OpenGLRenderer::getAlphaAndMode(const SkPaint* paint, int* alpha, SkXfermode::Mode* mode) const { + const Matrix4& transform = ignoreTransform ? Matrix4::identity() : *currentTransform(); + Glop glop; + GlopBuilder(mRenderState, mCaches, &glop) + .setMeshUnitQuad() + .setFillPaint(*paint, currentSnapshot()->alpha) + .setTransform(currentSnapshot()->getOrthoMatrix(), transform, false) + .setModelViewMapUnitToRect(Rect(left, top, right, bottom)) + .setRoundRectClipState(currentSnapshot()->roundRectClipState) + .build(); + renderGlop(glop); +} + +void OpenGLRenderer::getAlphaAndMode(const SkPaint* paint, int* alpha, + SkXfermode::Mode* mode) const { getAlphaAndModeDirect(paint, alpha, mode); if (mDrawModifiers.mOverrideLayerAlpha < 1.0f) { // if drawing a layer, ignore the paint's alpha diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h index 5eee2e2..5f8960a 100755 --- a/libs/hwui/OpenGLRenderer.h +++ b/libs/hwui/OpenGLRenderer.h @@ -17,6 +17,18 @@ #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> @@ -38,45 +50,33 @@ #include <androidfw/ResourceTypes.h> -#include "Debug.h" -#include "Extensions.h" -#include "Matrix.h" -#include "Program.h" -#include "Rect.h" -#include "Renderer.h" -#include "Snapshot.h" -#include "StatefulBaseRenderer.h" -#include "UvMapper.h" -#include "Vertex.h" -#include "Caches.h" -#include "CanvasProperty.h" - class SkShader; namespace android { namespace uirenderer { +enum class DrawOpMode { + kImmediate, + kDefer, + kFlush +}; + class DeferredDisplayState; +struct Glop; class RenderState; class RenderNode; -class TextSetupFunctor; +class TextDrawFunctor; class VertexBuffer; struct DrawModifiers { - DrawModifiers() { - reset(); - } + DrawModifiers() + : mOverrideLayerAlpha(0.0f) {} void reset() { - memset(this, 0, sizeof(DrawModifiers)); + mOverrideLayerAlpha = 0.0f; } float mOverrideLayerAlpha; - - // Draw filters - bool mHasDrawFilter; - int mPaintFilterClearBits; - int mPaintFilterSetBits; }; enum StateDeferFlags { @@ -123,20 +123,59 @@ enum ModelViewMode { /** * OpenGL Renderer implementation. */ -class OpenGLRenderer : public StatefulBaseRenderer { +class OpenGLRenderer : public CanvasStateClient { public: OpenGLRenderer(RenderState& renderState); virtual ~OpenGLRenderer(); + /** + * Sets the dimension of the underlying drawing surface. This method must + * be called at least once every time the drawing surface changes size. + * + * @param width The width in pixels of the underlysing surface + * @param height The height in pixels of the underlysing surface + */ + void setViewport(int width, int height) { mState.setViewport(width, height); } + void initProperties(); void initLight(const Vector3& lightCenter, float lightRadius, uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha); - virtual void onViewportInitialized(); - virtual status_t prepareDirty(float left, float top, float right, float bottom, bool opaque); - virtual void finish(); + /* + * 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(float left, float top, float right, float bottom, + bool opaque); - virtual status_t callDrawGLFunction(Functor* functor, Rect& dirty); + /** + * Prepares the renderer to draw a frame. This method must be invoked + * at the beginning of each frame. When this method is invoked, the + * entire drawing surface is assumed to be redrawn. + * + * @param opaque If true, the target surface is considered opaque + * and will not be cleared. If false, the target surface + * will be cleared + */ + void prepare(bool opaque) { + prepareDirty(0.0f, 0.0f, mState.getWidth(), mState.getHeight(), 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); @@ -145,7 +184,7 @@ public: virtual int saveLayer(float left, float top, float right, float bottom, const SkPaint* paint, int flags) { - return saveLayer(left, top, right, bottom, paint, flags, NULL); + return saveLayer(left, top, right, bottom, paint, flags, nullptr); } // Specialized saveLayer implementation, which will pass the convexMask to an FBO layer, if @@ -156,56 +195,50 @@ public: int saveLayerDeferred(float left, float top, float right, float bottom, const SkPaint* paint, int flags); - virtual status_t drawRenderNode(RenderNode* displayList, Rect& dirty, int32_t replayFlags = 1); - virtual status_t drawLayer(Layer* layer, float x, float y); - virtual status_t drawBitmap(const SkBitmap* bitmap, const SkPaint* paint); - status_t drawBitmaps(const SkBitmap* bitmap, AssetAtlas::Entry* entry, int bitmapCount, + void drawRenderNode(RenderNode* displayList, Rect& dirty, int32_t replayFlags = 1); + void drawLayer(Layer* layer, float x, float y); + 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); - virtual status_t drawBitmap(const SkBitmap* bitmap, float srcLeft, float srcTop, - float srcRight, float srcBottom, float dstLeft, float dstTop, - float dstRight, float dstBottom, const SkPaint* paint); - virtual status_t drawBitmapData(const SkBitmap* bitmap, const SkPaint* paint); - virtual status_t drawBitmapMesh(const SkBitmap* bitmap, int meshWidth, int meshHeight, + 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); - status_t drawPatches(const SkBitmap* bitmap, AssetAtlas::Entry* entry, + void drawPatches(const SkBitmap* bitmap, AssetAtlas::Entry* entry, TextureVertex* vertices, uint32_t indexCount, const SkPaint* paint); - virtual status_t drawPatch(const SkBitmap* bitmap, const Res_png_9patch* patch, + void drawPatch(const SkBitmap* bitmap, const Patch* mesh, AssetAtlas::Entry* entry, float left, float top, float right, float bottom, const SkPaint* paint); - status_t drawPatch(const SkBitmap* bitmap, const Patch* mesh, AssetAtlas::Entry* entry, - float left, float top, float right, float bottom, const SkPaint* paint); - virtual status_t drawColor(int color, SkXfermode::Mode mode); - virtual status_t drawRect(float left, float top, float right, float bottom, + void drawColor(int color, SkXfermode::Mode mode); + void drawRect(float left, float top, float right, float bottom, const SkPaint* paint); - virtual status_t drawRoundRect(float left, float top, float right, float bottom, + void drawRoundRect(float left, float top, float right, float bottom, float rx, float ry, const SkPaint* paint); - virtual status_t drawCircle(float x, float y, float radius, const SkPaint* paint); - virtual status_t drawOval(float left, float top, float right, float bottom, + void drawCircle(float x, float y, float radius, const SkPaint* paint); + void drawOval(float left, float top, float right, float bottom, const SkPaint* paint); - virtual status_t drawArc(float left, float top, float right, float bottom, + void drawArc(float left, float top, float right, float bottom, float startAngle, float sweepAngle, bool useCenter, const SkPaint* paint); - virtual status_t drawPath(const SkPath* path, const SkPaint* paint); - virtual status_t drawLines(const float* points, int count, const SkPaint* paint); - virtual status_t drawPoints(const float* points, int count, const SkPaint* paint); - virtual status_t drawTextOnPath(const char* text, int bytesCount, int count, const SkPath* path, + 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 char* text, int bytesCount, int count, const SkPath* path, float hOffset, float vOffset, const SkPaint* paint); - virtual status_t drawPosText(const char* text, int bytesCount, int count, + void drawPosText(const char* text, int bytesCount, int count, const float* positions, const SkPaint* paint); - virtual status_t drawText(const char* text, int bytesCount, int count, float x, float y, + void drawText(const char* text, int bytesCount, int count, float x, float y, const float* positions, const SkPaint* paint, float totalAdvance, const Rect& bounds, - DrawOpMode drawOpMode = kDrawOpMode_Immediate); - virtual status_t drawRects(const float* rects, int count, const SkPaint* paint); + DrawOpMode drawOpMode = DrawOpMode::kImmediate); + void drawRects(const float* rects, int count, const SkPaint* paint); - status_t drawShadow(float casterAlpha, - const VertexBuffer* ambientShadowVertexBuffer, const VertexBuffer* spotShadowVertexBuffer); + void drawShadow(float casterAlpha, + const VertexBuffer* ambientShadowVertexBuffer, + const VertexBuffer* spotShadowVertexBuffer); - virtual void resetPaintFilter(); - virtual void setupPaintFilter(int clearBits, int setBits); + void setDrawFilter(SkDrawFilter* filter); // If this value is set to < 1.0, it overrides alpha set on layer (see drawBitmap, drawLayer) void setOverrideLayerAlpha(float alpha) { mDrawModifiers.mOverrideLayerAlpha = alpha; } - const SkPaint* filterPaint(const SkPaint* paint); - /** * Store the current display state (most importantly, the current clip and transform), and * additionally map the state's bounds from local to window coordinates. @@ -227,21 +260,18 @@ public: return mCaches; } - // simple rect clip - bool isCurrentClipSimple() { - return mSnapshot->clipRegion->isEmpty(); + RenderState& renderState() { + return mRenderState; } - int getViewportWidth() { return currentSnapshot()->getViewportWidth(); } - int getViewportHeight() { return currentSnapshot()->getViewportHeight(); } + 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) { - mSnapshot->alpha *= alpha; - } + void scaleAlpha(float alpha) { mState.scaleAlpha(alpha); } /** * Inserts a named event marker in the stream of GL commands. @@ -275,14 +305,15 @@ public: * @param alpha Where to store the resulting alpha * @param mode Where to store the resulting xfermode */ - static inline void getAlphaAndModeDirect(const SkPaint* paint, int* alpha, SkXfermode::Mode* mode) { + static inline void getAlphaAndModeDirect(const SkPaint* paint, int* alpha, + SkXfermode::Mode* mode) { *mode = getXfermodeDirect(paint); *alpha = getAlphaDirect(paint); } static inline SkXfermode::Mode getXfermodeDirect(const SkPaint* paint) { if (!paint) return SkXfermode::kSrcOver_Mode; - return getXfermode(paint->getXfermode()); + return PaintUtils::getXfermode(paint->getXfermode()); } static inline int getAlphaDirect(const SkPaint* paint) { @@ -312,7 +343,7 @@ public: } static inline bool hasTextShadow(const SkPaint* paint) { - return getTextShadow(paint, NULL); + return getTextShadow(paint, nullptr); } /** @@ -334,18 +365,72 @@ public: drawColorRect(left, top, right, bottom, color, SkXfermode::kSrcOver_Mode, true); if (stencilWasEnabled) mCaches.stencil.enableTest(); + mDirty = true; } #endif - const Vector3& getLightCenter() const { return currentSnapshot()->getRelativeLightCenter(); } + 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 getMatrix(SkMatrix* outMatrix) const { mState.getMatrix(outMatrix); } + void setMatrix(const SkMatrix& matrix) { mState.setMatrix(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); + + 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() { - SkPath* path = new SkPath(); - mTempPaths.push_back(path); - return path; + std::unique_ptr<SkPath> path(new SkPath()); + SkPath* returnPath = path.get(); + mTempPaths.push_back(std::move(path)); + return returnPath; } protected: @@ -359,12 +444,12 @@ protected: * Indicates the start of rendering. This method will setup the * initial OpenGL state (viewport, clearing the buffer, etc.) */ - status_t startFrame(); + void startFrame(); /** * Clears the underlying surface if needed. */ - virtual status_t clear(float left, float top, float right, float bottom, bool opaque); + virtual void clear(float left, float top, float right, float bottom, bool opaque); /** * Call this method after updating a layer during a drawing pass. @@ -384,9 +469,16 @@ protected: */ 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 = NULL); - bool quickRejectSetupScissor(const Rect& bounds, const SkPaint* paint = NULL) { + const SkPaint* paint = nullptr); + bool quickRejectSetupScissor(const Rect& bounds, const SkPaint* paint = nullptr) { return quickRejectSetupScissor(bounds.left, bounds.top, bounds.right, bounds.bottom, paint); } @@ -411,21 +503,14 @@ protected: * Returns the region of the current layer. */ virtual Region* getRegion() const { - return mSnapshot->region; + return mState.currentRegion(); } /** * Indicates whether rendering is currently targeted at a layer. */ virtual bool hasLayer() const { - return (mSnapshot->flags & Snapshot::kFlagFboTarget) && mSnapshot->region; - } - - /** - * Returns the name of the FBO this renderer is rendering into. - */ - virtual GLuint getTargetFbo() const { - return 0; + return (mState.currentFlags() & Snapshot::kFlagFboTarget) && mState.currentRegion(); } /** @@ -459,7 +544,7 @@ protected: * null then null is returned. */ static inline SkColorFilter* getColorFilter(const SkPaint* paint) { - return paint ? paint->getColorFilter() : NULL; + return paint ? paint->getColorFilter() : nullptr; } /** @@ -467,7 +552,7 @@ protected: * null then null is returned. */ static inline const SkShader* getShader(const SkPaint* paint) { - return paint ? paint->getShader() : NULL; + return paint ? paint->getShader() : nullptr; } /** @@ -477,9 +562,13 @@ protected: return false; } - inline RenderState& renderState() { return mRenderState; } + CanvasState mState; + Caches& mCaches; + RenderState& mRenderState; private: + void renderGlop(const Glop& glop, bool clearLayer = true); + /** * Discards the content of the framebuffer if supported by the driver. * This method should be called at the beginning of a frame to optimize @@ -488,12 +577,6 @@ private: void discardFramebuffer(float left, float top, float right, float bottom); /** - * Ensures the state of the renderer is the same as the state of - * the GL context. - */ - void syncState(); - - /** * Tells the GPU what part of the screen is about to be redrawn. * This method will use the current layer space clip rect. * This method needs to be invoked every time getTargetFbo() is @@ -514,8 +597,6 @@ private: */ void endTiling(); - void onSnapshotRestored(const Snapshot& removed, const Snapshot& restored); - /** * Sets the clipping rectangle using glScissor. The clip is defined by * the current snapshot's clipRect member. @@ -594,7 +675,7 @@ private: * are transformed with the supplied matrix. */ void dirtyLayer(const float left, const float top, - const float right, const float bottom, const mat4 transform); + const float right, const float bottom, const Matrix4& transform); /** * Mark the layer as dirty at the specified coordinates. @@ -629,7 +710,7 @@ private: * @param dirty True if calling this method should dirty the current layer * @param clip True if the rects should be clipped, false otherwise */ - status_t drawColorRects(const float* rects, int count, const SkPaint* paint, + void drawColorRects(const float* rects, int count, const SkPaint* paint, bool ignoreTransform = false, bool dirty = true, bool clip = true); /** @@ -643,18 +724,7 @@ private: * @param texture The texture reprsenting the shape * @param paint The paint to draw the shape with */ - status_t drawShape(float left, float top, const PathTexture* texture, const SkPaint* paint); - - /** - * Draws the specified texture as an alpha bitmap. Alpha bitmaps obey - * different compositing rules. - * - * @param texture The texture to draw with - * @param left The x coordinate of the bitmap - * @param top The y coordinate of the bitmap - * @param paint The paint to render with - */ - void drawAlphaBitmap(Texture* texture, float left, float top, const SkPaint* paint); + void drawShape(float left, float top, PathTexture* texture, const SkPaint* paint); /** * Renders a strip of polygons with the specified paint, used for tessellated geometry. @@ -663,15 +733,15 @@ private: * @param paint The paint to render with * @param flags flags with which to draw */ - status_t drawVertexBuffer(float translateX, float translateY, const VertexBuffer& vertexBuffer, + void drawVertexBuffer(float translateX, float translateY, const VertexBuffer& vertexBuffer, const SkPaint* paint, int flags = 0); /** * Convenience for translating method */ - status_t drawVertexBuffer(const VertexBuffer& vertexBuffer, + void drawVertexBuffer(const VertexBuffer& vertexBuffer, const SkPaint* paint, int flags = 0) { - return drawVertexBuffer(0.0f, 0.0f, vertexBuffer, paint, flags); + drawVertexBuffer(0.0f, 0.0f, vertexBuffer, paint, flags); } /** @@ -680,67 +750,7 @@ private: * @param path The hull of the path to draw * @param paint The paint to render with */ - status_t drawConvexPath(const SkPath& path, const SkPaint* paint); - - /** - * Draws a textured rectangle with the specified texture. The specified coordinates - * are transformed by the current snapshot's transform matrix. - * - * @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 texture The texture to use - * @param paint The paint containing the alpha, blending mode, etc. - */ - void drawTextureRect(float left, float top, float right, float bottom, - Texture* texture, const SkPaint* paint); - - /** - * Draws a textured mesh with the specified texture. If the indices are omitted, - * the mesh is drawn as a simple quad. The mesh pointers become offsets when a - * VBO is bound. - * - * @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 texture The texture name to map onto the rectangle - * @param paint The paint containing the alpha, blending mode, colorFilter, etc. - * @param blend True if the texture contains an alpha channel - * @param vertices The vertices that define the mesh - * @param texCoords The texture coordinates of each vertex - * @param elementsCount The number of elements in the mesh, required by indices - * @param swapSrcDst Whether or not the src and dst blending operations should be swapped - * @param ignoreTransform True if the current transform should be ignored - * @param vbo The VBO used to draw the mesh - * @param modelViewMode Defines whether the model view matrix should be scaled - * @param dirty True if calling this method should dirty the current layer - */ - void drawTextureMesh(float left, float top, float right, float bottom, GLuint texture, - const SkPaint* paint, bool blend, - GLvoid* vertices, GLvoid* texCoords, GLenum drawMode, GLsizei elementsCount, - bool swapSrcDst = false, bool ignoreTransform = false, GLuint vbo = 0, - ModelViewMode modelViewMode = kModelViewMode_TranslateAndScale, bool dirty = true); - - void drawIndexedTextureMesh(float left, float top, float right, float bottom, GLuint texture, - const SkPaint* paint, bool blend, - GLvoid* vertices, GLvoid* texCoords, GLenum drawMode, GLsizei elementsCount, - bool swapSrcDst = false, bool ignoreTransform = false, GLuint vbo = 0, - ModelViewMode modelViewMode = kModelViewMode_TranslateAndScale, bool dirty = true); - - void drawAlpha8TextureMesh(float left, float top, float right, float bottom, - GLuint texture, const SkPaint* paint, - GLvoid* vertices, GLvoid* texCoords, GLenum drawMode, GLsizei elementsCount, - bool ignoreTransform, ModelViewMode modelViewMode = kModelViewMode_TranslateAndScale, - bool dirty = true); - - /** - * Draws the specified list of vertices as quads using indexed GL_TRIANGLES. - * If the number of vertices to draw exceeds the number of indices we have - * pre-allocated, this method will generate several glDrawElements() calls. - */ - void issueIndexedQuadDraw(Vertex* mesh, GLsizei quadsCount); + void drawConvexPath(const SkPath& path, const SkPaint* paint); /** * Draws text underline and strike-through if needed. @@ -780,7 +790,7 @@ private: * @param y The y coordinate where the texture will be drawn * @param paint The paint to draw the texture with */ - void drawPathTexture(const PathTexture* texture, float x, float y, const SkPaint* paint); + void drawPathTexture(PathTexture* texture, float x, float y, const SkPaint* paint); /** * Resets the texture coordinates stored in mMeshVertices. Setting the values @@ -800,106 +810,6 @@ private: */ bool canSkipText(const SkPaint* paint) const; - /** - * Binds the specified texture. The texture unit must have been selected - * prior to calling this method. - */ - inline void bindTexture(GLuint texture) { - mCaches.bindTexture(texture); - } - - /** - * Binds the specified EGLImage texture. The texture unit must have been selected - * prior to calling this method. - */ - inline void bindExternalTexture(GLuint texture) { - mCaches.bindTexture(GL_TEXTURE_EXTERNAL_OES, texture); - } - - /** - * Enable or disable blending as necessary. This function sets the appropriate - * blend function based on the specified xfermode. - */ - inline void chooseBlending(bool blend, SkXfermode::Mode mode, ProgramDescription& description, - bool swapSrcDst = false); - - /** - * Use the specified program with the current GL context. If the program is already - * in use, it will not be bound again. If it is not in use, the current program is - * marked unused and the specified program becomes used and becomes the new - * current program. - * - * @param program The program to use - * - * @return true If the specified program was already in use, false otherwise. - */ - inline bool useProgram(Program* program); - - /** - * Invoked before any drawing operation. This sets required state. - */ - void setupDraw(bool clear = true); - - /** - * Various methods to setup OpenGL rendering. - */ - void setupDrawWithTexture(bool isAlpha8 = false); - void setupDrawWithTextureAndColor(bool isAlpha8 = false); - void setupDrawWithExternalTexture(); - void setupDrawNoTexture(); - void setupDrawVertexAlpha(bool useShadowAlphaInterp); - void setupDrawColor(int color, int alpha); - void setupDrawColor(float r, float g, float b, float a); - void setupDrawAlpha8Color(int color, int alpha); - void setupDrawTextGamma(const SkPaint* paint); - void setupDrawShader(const SkShader* shader); - void setupDrawColorFilter(const SkColorFilter* filter); - void setupDrawBlending(const Layer* layer, bool swapSrcDst = false); - void setupDrawBlending(const SkPaint* paint, bool blend = true, bool swapSrcDst = false); - void setupDrawProgram(); - void setupDrawDirtyRegionsDisabled(); - - /** - * Setup the current program matrices based upon the nature of the geometry. - * - * @param mode If kModelViewMode_Translate, the geometry must be translated by the left and top - * parameters. If kModelViewMode_TranslateAndScale, the geometry that exists in the (0,0, 1,1) - * space must be scaled up and translated to fill the quad provided in (l,t,r,b). These - * transformations are stored in the modelView matrix and uploaded to the shader. - * - * @param offset Set to true if the the matrix should be fudged (translated) slightly to disambiguate - * geometry pixel positioning. See Vertex::GeometryFudgeFactor(). - * - * @param ignoreTransform Set to true if l,t,r,b coordinates already in layer space, - * currentTransform() will be ignored. (e.g. when drawing clip in layer coordinates to stencil, - * or when simple translation has been extracted) - */ - void setupDrawModelView(ModelViewMode mode, bool offset, - float left, float top, float right, float bottom, bool ignoreTransform = false); - void setupDrawColorUniforms(bool hasShader); - void setupDrawPureColorUniforms(); - - /** - * Setup uniforms for the current shader. - * - * @param shader SkShader on the current paint. - * - * @param ignoreTransform Set to true to ignore the transform in shader. - */ - void setupDrawShaderUniforms(const SkShader* shader, bool ignoreTransform = false); - void setupDrawColorFilterUniforms(const SkColorFilter* paint); - void setupDrawSimpleMesh(); - void setupDrawTexture(GLuint texture); - void setupDrawExternalTexture(GLuint texture); - void setupDrawTextureTransform(); - void setupDrawTextureTransformUniforms(mat4& transform); - void setupDrawTextGammaUniforms(); - void setupDrawMesh(const GLvoid* vertices, const GLvoid* texCoords = NULL, GLuint vbo = 0); - void setupDrawMesh(const GLvoid* vertices, const GLvoid* texCoords, const GLvoid* colors); - void setupDrawMeshIndices(const GLvoid* vertices, const GLvoid* texCoords, GLuint vbo = 0); - void setupDrawIndexedVertices(GLvoid* vertices); - void accountForClear(SkXfermode::Mode mode); - bool updateLayer(Layer* layer, bool inFrame); void updateLayers(); void flushLayers(); @@ -931,9 +841,7 @@ private: /** * Should be invoked every time the glScissor is modified. */ - inline void dirtyClip() { - mDirtyClip = true; - } + inline void dirtyClip() { mState.setDirtyClip(true); } inline const UvMapper& getMapper(const Texture* texture) { return texture && texture->uvMapper ? *texture->uvMapper : mUvMapper; @@ -946,21 +854,9 @@ private: */ Texture* getTexture(const SkBitmap* bitmap); - /** - * Model-view matrix used to position/size objects - * - * Stores operation-local modifications to the draw matrix that aren't incorporated into the - * currentTransform(). - * - * If generated with kModelViewMode_Translate, mModelViewMatrix will reflect an x/y offset, - * e.g. the offset in drawLayer(). If generated with kModelViewMode_TranslateAndScale, - * mModelViewMatrix will reflect a translation and scale, e.g. the translation and scale - * required to make VBO 0 (a rect of (0,0,1,1)) scaled to match the x,y offset, and width/height - * of a bitmap. - * - * Used as input to SkiaShader transformation. - */ - mat4 mModelViewMatrix; + 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; @@ -969,9 +865,6 @@ private: // Is a frame currently being rendered bool mFrameStarted; - // Used to draw textured quads - TextureVertex mMeshVertices[4]; - // Default UV mapper const UvMapper mUvMapper; @@ -979,31 +872,11 @@ private: DrawModifiers mDrawModifiers; SkPaint mFilteredPaint; - // Various caches - Caches& mCaches; - Extensions& mExtensions; - RenderState& mRenderState; - // List of rectangles to clear after saveLayer() is invoked - Vector<Rect*> mLayers; + std::vector<Rect> mLayers; // List of layers to update at the beginning of a frame Vector< sp<Layer> > mLayerUpdates; - // The following fields are used to setup drawing - // Used to describe the shaders to generate - ProgramDescription mDescription; - // Color description - bool mColorSet; - float mColorA, mColorR, mColorG, mColorB; - // Indicates that the shader should get a color - bool mSetShaderColor; - // Current texture unit - GLuint mTextureUnit; - // Track dirty regions, true by default - bool mTrackDirtyRegions; - // Indicate whether we are drawing an opaque frame - bool mOpaqueFrame; - // See PROPERTY_DISABLE_SCISSOR_OPTIMIZATION in // Properties.h bool mScissorOptimizationDisabled; @@ -1014,6 +887,10 @@ private: bool mSkipOutlineClip; + // True if anything has been drawn since the last call to + // reportAndClearDirty() + bool mDirty; + // Lighting + shadows Vector3 mLightCenter; float mLightRadius; @@ -1021,10 +898,10 @@ private: uint8_t mSpotShadowAlpha; // Paths kept alive for the duration of the frame - std::vector<SkPath*> mTempPaths; + std::vector<std::unique_ptr<SkPath>> mTempPaths; friend class Layer; - friend class TextSetupFunctor; + friend class TextDrawFunctor; friend class DrawBitmapOp; friend class DrawPatchOp; diff --git a/libs/hwui/Outline.h b/libs/hwui/Outline.h index 6dacd5e..5e9b213 100644 --- a/libs/hwui/Outline.h +++ b/libs/hwui/Outline.h @@ -95,7 +95,7 @@ public: } const SkPath* getPath() const { - if (mType == kOutlineType_None || mType == kOutlineType_Empty) return NULL; + if (mType == kOutlineType_None || mType == kOutlineType_Empty) return nullptr; return &mPath; } diff --git a/libs/hwui/Patch.cpp b/libs/hwui/Patch.cpp index 442e9ba..f673c6a 100644 --- a/libs/hwui/Patch.cpp +++ b/libs/hwui/Patch.cpp @@ -24,22 +24,12 @@ #include "Patch.h" #include "Properties.h" #include "UvMapper.h" +#include "utils/MathUtils.h" namespace android { namespace uirenderer { /////////////////////////////////////////////////////////////////////////////// -// Constructors/destructor -/////////////////////////////////////////////////////////////////////////////// - -Patch::Patch(): vertices(NULL), verticesCount(0), indexCount(0), hasEmptyQuads(false) { -} - -Patch::~Patch() { - delete[] vertices; -} - -/////////////////////////////////////////////////////////////////////////////// // Vertices management /////////////////////////////////////////////////////////////////////////////// @@ -47,19 +37,11 @@ uint32_t Patch::getSize() const { return verticesCount * sizeof(TextureVertex); } -TextureVertex* Patch::createMesh(const float bitmapWidth, const float bitmapHeight, - float width, float height, const Res_png_9patch* patch) { - UvMapper mapper; - return createMesh(bitmapWidth, bitmapHeight, width, height, mapper, patch); -} - -TextureVertex* Patch::createMesh(const float bitmapWidth, const float bitmapHeight, - float width, float height, const UvMapper& mapper, const Res_png_9patch* patch) { - if (vertices) return vertices; +Patch::Patch(const float bitmapWidth, const float bitmapHeight, + float width, float height, const UvMapper& mapper, const Res_png_9patch* patch) + : mColors(patch->getColors()) { int8_t emptyQuads = 0; - mColors = patch->getColors(); - const int8_t numColors = patch->numColors; if (uint8_t(numColors) < sizeof(uint32_t) * 4) { for (int8_t i = 0; i < numColors; i++) { @@ -75,10 +57,10 @@ TextureVertex* Patch::createMesh(const float bitmapWidth, const float bitmapHeig uint32_t yCount = patch->numYDivs; uint32_t maxVertices = ((xCount + 1) * (yCount + 1) - emptyQuads) * 4; - if (maxVertices == 0) return NULL; + if (maxVertices == 0) return; - TextureVertex* tempVertices = new TextureVertex[maxVertices]; - TextureVertex* vertex = tempVertices; + vertices.reset(new TextureVertex[maxVertices]); + TextureVertex* vertex = vertices.get(); const int32_t* xDivs = patch->getXDivs(); const int32_t* yDivs = patch->getYDivs(); @@ -157,15 +139,11 @@ TextureVertex* Patch::createMesh(const float bitmapWidth, const float bitmapHeig width, bitmapWidth, quadCount); } - if (verticesCount == maxVertices) { - vertices = tempVertices; - } else { - vertices = new TextureVertex[verticesCount]; - memcpy(vertices, tempVertices, verticesCount * sizeof(TextureVertex)); - delete[] tempVertices; + if (verticesCount != maxVertices) { + std::unique_ptr<TextureVertex[]> reducedVertices(new TextureVertex[verticesCount]); + memcpy(reducedVertices.get(), vertices.get(), verticesCount * sizeof(TextureVertex)); + vertices = std::move(reducedVertices); } - - return vertices; } void Patch::generateRow(const int32_t* xDivs, uint32_t xCount, TextureVertex*& vertex, @@ -213,10 +191,10 @@ void Patch::generateQuad(TextureVertex*& vertex, float x1, float y1, float x2, f const uint32_t oldQuadCount = quadCount; quadCount++; - if (x1 < 0.0f) x1 = 0.0f; - if (x2 < 0.0f) x2 = 0.0f; - if (y1 < 0.0f) y1 = 0.0f; - if (y2 < 0.0f) y2 = 0.0f; + x1 = MathUtils::max(x1, 0.0f); + x2 = MathUtils::max(x2, 0.0f); + y1 = MathUtils::max(y1, 0.0f); + y2 = MathUtils::max(y2, 0.0f); // Skip degenerate and transparent (empty) quads if ((mColors[oldQuadCount] == 0) || x1 >= x2 || y1 >= y2) { diff --git a/libs/hwui/Patch.h b/libs/hwui/Patch.h index 1ba045d..b63bd24 100644 --- a/libs/hwui/Patch.h +++ b/libs/hwui/Patch.h @@ -27,38 +27,35 @@ #include "Rect.h" #include "UvMapper.h" -#include "Vertex.h" namespace android { namespace uirenderer { +struct TextureVertex; + /////////////////////////////////////////////////////////////////////////////// // 9-patch structures /////////////////////////////////////////////////////////////////////////////// class Patch { public: - Patch(); - ~Patch(); + Patch(const float bitmapWidth, const float bitmapHeight, + float width, float height, + const UvMapper& mapper, const Res_png_9patch* patch); /** * Returns the size of this patch's mesh in bytes. */ uint32_t getSize() const; - TextureVertex* vertices; - uint32_t verticesCount; - uint32_t indexCount; - bool hasEmptyQuads; + std::unique_ptr<TextureVertex[]> vertices; + uint32_t verticesCount = 0; + uint32_t indexCount = 0; + bool hasEmptyQuads = false; Vector<Rect> quads; - GLintptr offset; - GLintptr textureOffset; - - TextureVertex* createMesh(const float bitmapWidth, const float bitmapHeight, - float width, float height, const Res_png_9patch* patch); - TextureVertex* createMesh(const float bitmapWidth, const float bitmapHeight, - float width, float height, const UvMapper& mapper, const Res_png_9patch* patch); + GLintptr positionOffset = 0; + GLintptr textureOffset = 0; private: void generateRow(const int32_t* xDivs, uint32_t xCount, TextureVertex*& vertex, diff --git a/libs/hwui/PatchCache.cpp b/libs/hwui/PatchCache.cpp index 2f2debc..2765262 100644 --- a/libs/hwui/PatchCache.cpp +++ b/libs/hwui/PatchCache.cpp @@ -20,8 +20,10 @@ #include <utils/Log.h> #include "Caches.h" +#include "Patch.h" #include "PatchCache.h" #include "Properties.h" +#include "renderstate/RenderState.h" namespace android { namespace uirenderer { @@ -30,11 +32,15 @@ namespace uirenderer { // Constructors/destructor /////////////////////////////////////////////////////////////////////////////// -PatchCache::PatchCache(): - mSize(0), mCache(LruCache<PatchDescription, Patch*>::kUnlimitedCapacity), - mMeshBuffer(0), mFreeBlocks(NULL), mGenerationId(0) { +PatchCache::PatchCache(RenderState& renderState) + : mRenderState(renderState) + , mSize(0) + , mCache(LruCache<PatchDescription, Patch*>::kUnlimitedCapacity) + , mMeshBuffer(0) + , mFreeBlocks(nullptr) + , mGenerationId(0) { char property[PROPERTY_VALUE_MAX]; - if (property_get(PROPERTY_PATCH_CACHE_SIZE, property, NULL) > 0) { + if (property_get(PROPERTY_PATCH_CACHE_SIZE, property, nullptr) > 0) { INIT_LOGD(" Setting patch cache size to %skB", property); mMaxSize = KB(atoi(property)); } else { @@ -47,15 +53,15 @@ PatchCache::~PatchCache() { clear(); } -void PatchCache::init(Caches& caches) { +void PatchCache::init() { bool created = false; if (!mMeshBuffer) { glGenBuffers(1, &mMeshBuffer); created = true; } - caches.bindMeshBuffer(mMeshBuffer); - caches.resetVertexPointers(); + mRenderState.meshState().bindMeshBuffer(mMeshBuffer); + mRenderState.meshState().resetVertexPointers(); if (created) { createVertexBuffer(); @@ -84,7 +90,7 @@ void PatchCache::clear() { clearCache(); if (mMeshBuffer) { - Caches::getInstance().unbindMeshBuffer(); + mRenderState.meshState().unbindMeshBuffer(); glDeleteBuffers(1, &mMeshBuffer); mMeshBuffer = 0; mSize = 0; @@ -104,7 +110,7 @@ void PatchCache::clearCache() { delete block; block = next; } - mFreeBlocks = NULL; + mFreeBlocks = nullptr; } void PatchCache::remove(Vector<patch_pair_t>& patchesToRemove, Res_png_9patch* patch) { @@ -124,11 +130,11 @@ void PatchCache::removeDeferred(Res_png_9patch* patch) { size_t count = mGarbage.size(); for (size_t i = 0; i < count; i++) { if (patch == mGarbage[i]) { - patch = NULL; + patch = nullptr; break; } } - LOG_ALWAYS_FATAL_IF(patch == NULL); + LOG_ALWAYS_FATAL_IF(patch == nullptr); mGarbage.push(patch); } @@ -156,7 +162,7 @@ void PatchCache::clearGarbage() { // Release the patch and mark the space in the free list Patch* patch = pair.getSecond(); - BufferBlock* block = new BufferBlock(patch->offset, patch->getSize()); + BufferBlock* block = new BufferBlock(patch->positionOffset, patch->getSize()); block->next = mFreeBlocks; mFreeBlocks = block; @@ -174,7 +180,7 @@ void PatchCache::clearGarbage() { } void PatchCache::createVertexBuffer() { - glBufferData(GL_ARRAY_BUFFER, mMaxSize, NULL, GL_DYNAMIC_DRAW); + glBufferData(GL_ARRAY_BUFFER, mMaxSize, nullptr, GL_DYNAMIC_DRAW); mSize = 0; mFreeBlocks = new BufferBlock(0, mMaxSize); mGenerationId++; @@ -184,9 +190,9 @@ void PatchCache::createVertexBuffer() { * Sets the mesh's offsets and copies its associated vertices into * the mesh buffer (VBO). */ -void PatchCache::setupMesh(Patch* newMesh, TextureVertex* vertices) { +void PatchCache::setupMesh(Patch* newMesh) { // This call ensures the VBO exists and that it is bound - init(Caches::getInstance()); + init(); // If we're running out of space, let's clear the entire cache uint32_t size = newMesh->getSize(); @@ -196,7 +202,7 @@ void PatchCache::setupMesh(Patch* newMesh, TextureVertex* vertices) { } // Find a block where we can fit the mesh - BufferBlock* previous = NULL; + BufferBlock* previous = nullptr; BufferBlock* block = mFreeBlocks; while (block) { // The mesh fits @@ -212,14 +218,14 @@ void PatchCache::setupMesh(Patch* newMesh, TextureVertex* vertices) { if (!block) { clearCache(); createVertexBuffer(); - previous = NULL; + previous = nullptr; block = mFreeBlocks; } // Copy the 9patch mesh in the VBO - newMesh->offset = (GLintptr) (block->offset); - newMesh->textureOffset = newMesh->offset + gMeshTextureOffset; - glBufferSubData(GL_ARRAY_BUFFER, newMesh->offset, size, vertices); + newMesh->positionOffset = (GLintptr) (block->offset); + newMesh->textureOffset = newMesh->positionOffset + kMeshTextureOffset; + glBufferSubData(GL_ARRAY_BUFFER, newMesh->positionOffset, size, newMesh->vertices.get()); // Remove the block since we've used it entirely if (block->size == size) { @@ -238,6 +244,8 @@ void PatchCache::setupMesh(Patch* newMesh, TextureVertex* vertices) { mSize += size; } +static const UvMapper sIdentity; + const Patch* PatchCache::get(const AssetAtlas::Entry* entry, const uint32_t bitmapWidth, const uint32_t bitmapHeight, const float pixelWidth, const float pixelHeight, const Res_png_9patch* patch) { @@ -246,20 +254,12 @@ const Patch* PatchCache::get(const AssetAtlas::Entry* entry, const Patch* mesh = mCache.get(description); if (!mesh) { - Patch* newMesh = new Patch(); - TextureVertex* vertices; - - if (entry) { - // An atlas entry has a UV mapper - vertices = newMesh->createMesh(bitmapWidth, bitmapHeight, - pixelWidth, pixelHeight, entry->uvMapper, patch); - } else { - vertices = newMesh->createMesh(bitmapWidth, bitmapHeight, - pixelWidth, pixelHeight, patch); - } + const UvMapper& mapper = entry ? entry->uvMapper : sIdentity; + Patch* newMesh = new Patch(bitmapWidth, bitmapHeight, + pixelWidth, pixelHeight, mapper, patch); - if (vertices) { - setupMesh(newMesh, vertices); + if (newMesh->vertices) { + setupMesh(newMesh); } #if DEBUG_PATCHES @@ -278,7 +278,7 @@ void PatchCache::dumpFreeBlocks(const char* prefix) { String8 dump; BufferBlock* block = mFreeBlocks; while (block) { - dump.appendFormat("->(%d, %d)", block->offset, block->size); + dump.appendFormat("->(%d, %d)", block->positionOffset, block->size); block = block->next; } ALOGD("%s: Free blocks%s", prefix, dump.string()); diff --git a/libs/hwui/PatchCache.h b/libs/hwui/PatchCache.h index 9f2c9a5..387f79a 100644 --- a/libs/hwui/PatchCache.h +++ b/libs/hwui/PatchCache.h @@ -25,12 +25,13 @@ #include "AssetAtlas.h" #include "Debug.h" -#include "Patch.h" #include "utils/Pair.h" namespace android { namespace uirenderer { +class Patch; + /////////////////////////////////////////////////////////////////////////////// // Defines /////////////////////////////////////////////////////////////////////////////// @@ -50,9 +51,9 @@ class Caches; class PatchCache { public: - PatchCache(); + PatchCache(RenderState& renderState); ~PatchCache(); - void init(Caches& caches); + void init(); const Patch* get(const AssetAtlas::Entry* entry, const uint32_t bitmapWidth, const uint32_t bitmapHeight, @@ -90,7 +91,7 @@ public: private: struct PatchDescription { - PatchDescription(): mPatch(NULL), mBitmapWidth(0), mBitmapHeight(0), + PatchDescription(): mPatch(nullptr), mBitmapWidth(0), mBitmapHeight(0), mPixelWidth(0), mPixelHeight(0) { } @@ -145,7 +146,7 @@ private: * to track available regions of memory in the VBO. */ struct BufferBlock { - BufferBlock(uint32_t offset, uint32_t size): offset(offset), size(size), next(NULL) { + BufferBlock(uint32_t offset, uint32_t size): offset(offset), size(size), next(nullptr) { } uint32_t offset; @@ -159,7 +160,7 @@ private: void clearCache(); void createVertexBuffer(); - void setupMesh(Patch* newMesh, TextureVertex* vertices); + void setupMesh(Patch* newMesh); void remove(Vector<patch_pair_t>& patchesToRemove, Res_png_9patch* patch); @@ -167,6 +168,7 @@ private: void dumpFreeBlocks(const char* prefix); #endif + RenderState& mRenderState; uint32_t mMaxSize; uint32_t mSize; diff --git a/libs/hwui/PathCache.cpp b/libs/hwui/PathCache.cpp index 6f48e4d..bdb44a6 100644 --- a/libs/hwui/PathCache.cpp +++ b/libs/hwui/PathCache.cpp @@ -31,7 +31,6 @@ #include "PathCache.h" #include "thread/Signal.h" -#include "thread/Task.h" #include "thread/TaskProcessor.h" namespace android { @@ -41,25 +40,25 @@ namespace uirenderer { // Cache entries /////////////////////////////////////////////////////////////////////////////// -PathDescription::PathDescription(): - type(kShapeNone), - join(SkPaint::kDefault_Join), - cap(SkPaint::kDefault_Cap), - style(SkPaint::kFill_Style), - miter(4.0f), - strokeWidth(1.0f), - pathEffect(NULL) { +PathDescription::PathDescription() + : type(kShapeNone) + , join(SkPaint::kDefault_Join) + , cap(SkPaint::kDefault_Cap) + , style(SkPaint::kFill_Style) + , miter(4.0f) + , strokeWidth(1.0f) + , pathEffect(nullptr) { memset(&shape, 0, sizeof(Shape)); } -PathDescription::PathDescription(ShapeType type, const SkPaint* paint): - type(type), - join(paint->getStrokeJoin()), - cap(paint->getStrokeCap()), - style(paint->getStyle()), - miter(paint->getStrokeMiter()), - strokeWidth(paint->getStrokeWidth()), - pathEffect(paint->getPathEffect()) { +PathDescription::PathDescription(ShapeType type, const SkPaint* paint) + : type(type) + , join(paint->getStrokeJoin()) + , cap(paint->getStrokeCap()) + , style(paint->getStyle()) + , miter(paint->getStrokeMiter()) + , strokeWidth(paint->getStrokeWidth()) + , pathEffect(paint->getPathEffect()) { memset(&shape, 0, sizeof(Shape)); } @@ -81,7 +80,7 @@ hash_t PathDescription::hash() const { bool PathCache::canDrawAsConvexPath(SkPath* path, const SkPaint* paint) { // NOTE: This should only be used after PathTessellator handles joins properly - return paint->getPathEffect() == NULL && path->getConvexity() == SkPath::kConvex_Convexity; + return paint->getPathEffect() == nullptr && path->getConvexity() == SkPath::kConvex_Convexity; } void PathCache::computePathBounds(const SkPath* path, const SkPaint* paint, @@ -114,9 +113,9 @@ static void initPaint(SkPaint& paint) { // will be applied later when compositing the alpha8 texture paint.setColor(SK_ColorBLACK); paint.setAlpha(255); - paint.setColorFilter(NULL); - paint.setMaskFilter(NULL); - paint.setShader(NULL); + paint.setColorFilter(nullptr); + paint.setMaskFilter(nullptr); + paint.setShader(nullptr); SkXfermode* mode = SkXfermode::Create(SkXfermode::kSrc_Mode); SkSafeUnref(paint.setXfermode(mode)); } @@ -133,18 +132,6 @@ static void drawPath(const SkPath *path, const SkPaint* paint, SkBitmap& bitmap, canvas.drawPath(*path, pathPaint); } -static PathTexture* createTexture(float left, float top, float offset, - uint32_t width, uint32_t height, uint32_t id) { - PathTexture* texture = new PathTexture(Caches::getInstance()); - texture->left = left; - texture->top = top; - texture->offset = offset; - texture->width = width; - texture->height = height; - texture->generation = id; - return texture; -} - /////////////////////////////////////////////////////////////////////////////// // Cache constructor/destructor /////////////////////////////////////////////////////////////////////////////// @@ -153,7 +140,7 @@ PathCache::PathCache(): mCache(LruCache<PathDescription, PathTexture*>::kUnlimitedCapacity), mSize(0), mMaxSize(MB(DEFAULT_PATH_CACHE_SIZE)) { char property[PROPERTY_VALUE_MAX]; - if (property_get(PROPERTY_PATH_CACHE_SIZE, property, NULL) > 0) { + if (property_get(PROPERTY_PATH_CACHE_SIZE, property, nullptr) > 0) { INIT_LOGD(" Setting %s cache size to %sMB", name, property); setMaxSize(MB(atof(property))); } else { @@ -211,8 +198,8 @@ 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(); - if (task != NULL) { - SkBitmap* bitmap = task->getResult(); + if (task != nullptr) { + task->getResult(); texture->clearTask(); } else { // If there is a pending task, the path was not added @@ -231,7 +218,7 @@ void PathCache::removeTexture(PathTexture* texture) { } if (texture->id) { - Caches::getInstance().deleteTexture(texture->id); + Caches::getInstance().textureState().deleteTexture(texture->id); } delete texture; } @@ -261,14 +248,15 @@ PathTexture* PathCache::addTexture(const PathDescription& entry, const SkPath *p uint32_t width, height; computePathBounds(path, paint, left, top, offset, width, height); - if (!checkTextureSize(width, height)) return NULL; + if (!checkTextureSize(width, height)) return nullptr; purgeCache(width, height); SkBitmap bitmap; drawPath(path, paint, bitmap, left, top, offset, width, height); - PathTexture* texture = createTexture(left, top, offset, width, height, + PathTexture* texture = new PathTexture(Caches::getInstance(), + left, top, offset, width, height, path->getGenerationID()); generateTexture(entry, &bitmap, texture); @@ -313,7 +301,7 @@ void PathCache::generateTexture(SkBitmap& bitmap, Texture* texture) { glGenTextures(1, &texture->id); - Caches::getInstance().bindTexture(texture->id); + Caches::getInstance().textureState().bindTexture(texture->id); // Textures are Alpha8 glPixelStorei(GL_UNPACK_ALIGNMENT, 1); @@ -355,7 +343,7 @@ void PathCache::PathProcessor::onProcess(const sp<Task<SkBitmap*> >& task) { } else { texture->width = 0; texture->height = 0; - t->setResult(NULL); + t->setResult(nullptr); } } @@ -363,22 +351,9 @@ void PathCache::PathProcessor::onProcess(const sp<Task<SkBitmap*> >& task) { // Paths /////////////////////////////////////////////////////////////////////////////// -void PathCache::remove(Vector<PathDescription>& pathsToRemove, const path_pair_t& pair) { - LruCache<PathDescription, PathTexture*>::Iterator i(mCache); - - while (i.next()) { - const PathDescription& key = i.key(); - if (key.type == kShapePath && - (key.shape.path.mPath == pair.getFirst() || - key.shape.path.mPath == pair.getSecond())) { - pathsToRemove.push(key); - } - } -} - -void PathCache::removeDeferred(SkPath* path) { +void PathCache::removeDeferred(const SkPath* path) { Mutex::Autolock l(mLock); - mGarbage.push(path_pair_t(path, const_cast<SkPath*>(path->getSourcePath()))); + mGarbage.push(path->getGenerationID()); } void PathCache::clearGarbage() { @@ -388,9 +363,15 @@ void PathCache::clearGarbage() { Mutex::Autolock l(mLock); size_t count = mGarbage.size(); for (size_t i = 0; i < count; i++) { - const path_pair_t& pair = mGarbage.itemAt(i); - remove(pathsToRemove, pair); - delete pair.getFirst(); + const uint32_t generationID = mGarbage.itemAt(i); + + LruCache<PathDescription, PathTexture*>::Iterator iter(mCache); + while (iter.next()) { + const PathDescription& key = iter.key(); + if (key.type == kShapePath && key.shape.path.mGenerationID == generationID) { + pathsToRemove.push(key); + } + } } mGarbage.clear(); } @@ -400,27 +381,9 @@ void PathCache::clearGarbage() { } } -/** - * To properly handle path mutations at draw time we always make a copy - * of paths objects when recording display lists. The source path points - * to the path we originally copied the path from. This ensures we use - * the original path as a cache key the first time a path is inserted - * in the cache. The source path is also used to reclaim garbage when a - * Dalvik Path object is collected. - */ -static const SkPath* getSourcePath(const SkPath* path) { - const SkPath* sourcePath = path->getSourcePath(); - if (sourcePath && sourcePath->getGenerationID() == path->getGenerationID()) { - return const_cast<SkPath*>(sourcePath); - } - return path; -} - PathTexture* PathCache::get(const SkPath* path, const SkPaint* paint) { - path = getSourcePath(path); - PathDescription entry(kShapePath, paint); - entry.shape.path.mPath = path; + entry.shape.path.mGenerationID = path->getGenerationID(); PathTexture* texture = mCache.get(entry); @@ -430,7 +393,7 @@ PathTexture* PathCache::get(const SkPath* path, const SkPaint* paint) { // 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(); - if (task != NULL) { + 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(); @@ -440,14 +403,9 @@ PathTexture* PathCache::get(const SkPath* path, const SkPaint* paint) { } else { ALOGW("Path too large to be rendered into a texture"); texture->clearTask(); - texture = NULL; + texture = nullptr; mCache.remove(entry); } - } else if (path->getGenerationID() != texture->generation) { - // The size of the path might have changed so we first - // remove the entry from the cache - mCache.remove(entry); - texture = addTexture(entry, path, paint); } } @@ -459,25 +417,20 @@ void PathCache::precache(const SkPath* path, const SkPaint* paint) { return; } - path = getSourcePath(path); - PathDescription entry(kShapePath, paint); - entry.shape.path.mPath = path; + entry.shape.path.mGenerationID = path->getGenerationID(); PathTexture* texture = mCache.get(entry); bool generate = false; if (!texture) { generate = true; - } else if (path->getGenerationID() != texture->generation) { - mCache.remove(entry); - generate = true; } if (generate) { // It is important to specify the generation ID so we do not // attempt to precache the same path several times - texture = createTexture(0.0f, 0.0f, 0.0f, 0, 0, path->getGenerationID()); + texture = new PathTexture(Caches::getInstance(), path->getGenerationID()); sp<PathTask> task = new PathTask(path, paint, texture); texture->setTask(task); @@ -490,7 +443,7 @@ void PathCache::precache(const SkPath* path, const SkPaint* paint) { // be enforced. mCache.put(entry, texture); - if (mProcessor == NULL) { + if (mProcessor == nullptr) { mProcessor = new PathProcessor(Caches::getInstance()); } mProcessor->add(task); diff --git a/libs/hwui/PathCache.h b/libs/hwui/PathCache.h index bc34188..4297693 100644 --- a/libs/hwui/PathCache.h +++ b/libs/hwui/PathCache.h @@ -17,22 +17,22 @@ #ifndef ANDROID_HWUI_PATH_CACHE_H #define ANDROID_HWUI_PATH_CACHE_H -#include <GLES2/gl2.h> - -#include <utils/LruCache.h> -#include <utils/Mutex.h> -#include <utils/Vector.h> - #include "Debug.h" -#include "Properties.h" #include "Texture.h" +#include "thread/Task.h" +#include "thread/TaskProcessor.h" #include "utils/Macros.h" #include "utils/Pair.h" +#include <GLES2/gl2.h> +#include <SkPath.h> +#include <utils/LruCache.h> +#include <utils/Mutex.h> +#include <utils/Vector.h> + class SkBitmap; class SkCanvas; class SkPaint; -class SkPath; struct SkRect; namespace android { @@ -59,7 +59,19 @@ class Caches; * Alpha texture used to represent a path. */ struct PathTexture: public Texture { - PathTexture(Caches& caches): Texture(caches) { + PathTexture(Caches& caches, float left, float top, + float offset, int width, int height, int generation) + : Texture(caches) + , left(left) + , top(top) + , offset(offset) { + this->width = width; + this->height = height; + this->generation = generation; + } + PathTexture(Caches& caches, int generation) + : Texture(caches) { + this->generation = generation; } ~PathTexture() { @@ -69,15 +81,15 @@ struct PathTexture: public Texture { /** * Left coordinate of the path bounds. */ - float left; + float left = 0; /** * Top coordinate of the path bounds. */ - float top; + float top = 0; /** * Offset to draw the path at the correct origin. */ - float offset; + float offset = 0; sp<Task<SkBitmap*> > task() const { return mTask; @@ -88,7 +100,7 @@ struct PathTexture: public Texture { } void clearTask() { - if (mTask != NULL) { + if (mTask != nullptr) { mTask.clear(); } } @@ -118,7 +130,7 @@ struct PathDescription { SkPathEffect* pathEffect; union Shape { struct Path { - const SkPath* mPath; + uint32_t mGenerationID; } path; struct RoundRect { float mWidth; @@ -166,7 +178,7 @@ public: * Used as a callback when an entry is removed from the cache. * Do not invoke directly. */ - void operator()(PathDescription& path, PathTexture*& texture); + void operator()(PathDescription& path, PathTexture*& texture) override; /** * Clears the cache. This causes all textures to be deleted. @@ -198,7 +210,7 @@ public: * Removes the specified path. This is meant to be called from threads * that are not the EGL context thread. */ - void removeDeferred(SkPath* path); + ANDROID_API void removeDeferred(const SkPath* path); /** * Process deferred removals. */ @@ -227,8 +239,6 @@ public: float& left, float& top, float& offset, uint32_t& width, uint32_t& height); private: - typedef Pair<SkPath*, SkPath*> path_pair_t; - PathTexture* addTexture(const PathDescription& entry, const SkPath *path, const SkPaint* paint); PathTexture* addTexture(const PathDescription& entry, SkBitmap* bitmap); @@ -245,12 +255,6 @@ private: } /** - * Removes an entry. - * The pair must define first=path, second=sourcePath - */ - void remove(Vector<PathDescription>& pathsToRemove, const path_pair_t& pair); - - /** * Ensures there is enough space in the cache for a texture of the specified * dimensions. */ @@ -279,8 +283,7 @@ private: delete future()->get(); } - // copied, since input path not refcounted / guaranteed to survive for duration of task - // TODO: avoid deep copy with refcounting + // copied, since input path not guaranteed to survive for duration of task const SkPath path; // copied, since input paint may not be immutable @@ -293,7 +296,7 @@ private: PathProcessor(Caches& caches); ~PathProcessor() { } - virtual void onProcess(const sp<Task<SkBitmap*> >& task); + virtual void onProcess(const sp<Task<SkBitmap*> >& task) override; private: uint32_t mMaxTextureSize; @@ -308,7 +311,7 @@ private: sp<PathProcessor> mProcessor; - Vector<path_pair_t> mGarbage; + Vector<uint32_t> mGarbage; mutable Mutex mLock; }; // class PathCache diff --git a/libs/hwui/PathTessellator.cpp b/libs/hwui/PathTessellator.cpp index 27ef06f..c1f61d6 100644 --- a/libs/hwui/PathTessellator.cpp +++ b/libs/hwui/PathTessellator.cpp @@ -37,6 +37,7 @@ #include <SkPath.h> #include <SkPaint.h> +#include <SkGeometry.h> // WARNING: Internal Skia Header #include <stdlib.h> #include <stdint.h> @@ -229,7 +230,6 @@ void getStrokeVerticesFromPerimeter(const PaintInfo& paintInfo, const Vector<Ver current->x - totalOffset.x, current->y - totalOffset.y); - last = current; current = next; lastNormal = nextNormal; } @@ -279,7 +279,6 @@ void getStrokeVerticesFromUnclosedVertices(const PaintInfo& paintInfo, - (vertices[lastIndex].x - vertices[lastIndex - 1].x), vertices[lastIndex].y - vertices[lastIndex - 1].y); const float dTheta = PI / (extra + 1); - const float radialScale = 2.0f / (1 + cos(dTheta)); int capOffset; for (int i = 0; i < extra; i++) { @@ -290,14 +289,14 @@ void getStrokeVerticesFromUnclosedVertices(const PaintInfo& paintInfo, } beginTheta += dTheta; - Vector2 beginRadialOffset = {cos(beginTheta), sin(beginTheta)}; + Vector2 beginRadialOffset = {cosf(beginTheta), sinf(beginTheta)}; paintInfo.scaleOffsetForStrokeWidth(beginRadialOffset); Vertex::set(&buffer[capOffset], vertices[0].x + beginRadialOffset.x, vertices[0].y + beginRadialOffset.y); endTheta += dTheta; - Vector2 endRadialOffset = {cos(endTheta), sin(endTheta)}; + Vector2 endRadialOffset = {cosf(endTheta), sinf(endTheta)}; paintInfo.scaleOffsetForStrokeWidth(endRadialOffset); Vertex::set(&buffer[allocSize - 1 - capOffset], vertices[lastIndex].x + endRadialOffset.x, @@ -373,7 +372,6 @@ void getFillVerticesFromPerimeterAA(const PaintInfo& paintInfo, const Vector<Ver current->y - totalOffset.y, maxAlpha); - last = current; current = next; lastNormal = nextNormal; } @@ -468,7 +466,7 @@ inline static void storeCapAA(const PaintInfo& paintInfo, const Vector<Vertex>& for (int i = 0; i < extra; i++) { theta += dTheta; - Vector2 radialOffset = {cos(theta), sin(theta)}; + Vector2 radialOffset = {cosf(theta), sinf(theta)}; // scale to compensate for pinching at sharp angles, see totalOffsetFromNormals() radialOffset *= radialScale; @@ -701,7 +699,6 @@ void getStrokeVerticesFromPerimeterAA(const PaintInfo& paintInfo, const Vector<V current->y - outerOffset.y, 0.0f); - last = current; current = next; lastNormal = nextNormal; } @@ -787,6 +784,7 @@ void PathTessellator::tessellatePath(const SkPath &path, const SkPaint* paint, Rect bounds(path.getBounds()); paintInfo.expandBoundsForStroke(&bounds); vertexBuffer.setBounds(bounds); + vertexBuffer.setMeshFeatureFlags(paintInfo.isAA ? VertexBuffer::kAlpha : VertexBuffer::kNone); } template <class TYPE> @@ -831,7 +829,6 @@ void PathTessellator::tessellatePoints(const float* points, int count, const SkP Rect bounds; // tessellate, then duplicate outline across points - int numPoints = count / 2; VertexBuffer tempBuffer; if (!paintInfo.isAA) { getFillVerticesFromPerimeter(outlineVertices, tempBuffer); @@ -845,6 +842,7 @@ void PathTessellator::tessellatePoints(const float* points, int count, const SkP // expand bounds from vertex coords to pixel data paintInfo.expandBoundsForStroke(&bounds); vertexBuffer.setBounds(bounds); + vertexBuffer.setMeshFeatureFlags(paintInfo.isAA ? VertexBuffer::kAlpha : VertexBuffer::kNone); } void PathTessellator::tessellateLines(const float* points, int count, const SkPaint* paint, @@ -895,6 +893,7 @@ void PathTessellator::tessellateLines(const float* points, int count, const SkPa // expand bounds from vertex coords to pixel data paintInfo.expandBoundsForStroke(&bounds); vertexBuffer.setBounds(bounds); + vertexBuffer.setMeshFeatureFlags(paintInfo.isAA ? VertexBuffer::kAlpha : VertexBuffer::kNone); } /////////////////////////////////////////////////////////////////////////////// @@ -953,6 +952,21 @@ bool PathTessellator::approximatePathOutlineVertices(const SkPath& path, bool fo pts[2].x(), pts[2].y(), sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices); break; + case SkPath::kConic_Verb: { + ALOGV("kConic_Verb"); + SkAutoConicToQuads converter; + const SkPoint* quads = converter.computeQuads(pts, iter.conicWeight(), + thresholdSquared); + for (int i = 0; i < converter.countQuads(); ++i) { + const int offset = 2 * i; + recursiveQuadraticBezierVertices( + quads[offset].x(), quads[offset].y(), + quads[offset+2].x(), quads[offset+2].y(), + quads[offset+1].x(), quads[offset+1].y(), + sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices); + } + break; + } default: break; } diff --git a/libs/hwui/PixelBuffer.cpp b/libs/hwui/PixelBuffer.cpp index 5b642b9..9665a68 100644 --- a/libs/hwui/PixelBuffer.cpp +++ b/libs/hwui/PixelBuffer.cpp @@ -16,13 +16,14 @@ #define LOG_TAG "OpenGLRenderer" -#include <utils/Log.h> +#include "PixelBuffer.h" -#include "Caches.h" #include "Debug.h" #include "Extensions.h" -#include "PixelBuffer.h" #include "Properties.h" +#include "renderstate/RenderState.h" + +#include <utils/Log.h> namespace android { namespace uirenderer { @@ -34,33 +35,28 @@ namespace uirenderer { class CpuPixelBuffer: public PixelBuffer { public: CpuPixelBuffer(GLenum format, uint32_t width, uint32_t height); - ~CpuPixelBuffer(); - uint8_t* map(AccessMode mode = kAccessMode_ReadWrite); - void unmap(); + uint8_t* map(AccessMode mode = kAccessMode_ReadWrite) override; + void unmap() override; - uint8_t* getMappedPointer() const; + uint8_t* getMappedPointer() const override; - void upload(uint32_t x, uint32_t y, uint32_t width, uint32_t height, int offset); + void upload(uint32_t x, uint32_t y, uint32_t width, uint32_t height, int offset) override; private: - uint8_t* mBuffer; + std::unique_ptr<uint8_t[]> mBuffer; }; -CpuPixelBuffer::CpuPixelBuffer(GLenum format, uint32_t width, uint32_t height): - PixelBuffer(format, width, height) { - mBuffer = new uint8_t[width * height * formatSize(format)]; -} - -CpuPixelBuffer::~CpuPixelBuffer() { - delete[] mBuffer; +CpuPixelBuffer::CpuPixelBuffer(GLenum format, uint32_t width, uint32_t height) + : PixelBuffer(format, width, height) + , mBuffer(new uint8_t[width * height * formatSize(format)]) { } uint8_t* CpuPixelBuffer::map(AccessMode mode) { if (mAccessMode == kAccessMode_None) { mAccessMode = mode; } - return mBuffer; + return mBuffer.get(); } void CpuPixelBuffer::unmap() { @@ -68,12 +64,12 @@ void CpuPixelBuffer::unmap() { } uint8_t* CpuPixelBuffer::getMappedPointer() const { - return mAccessMode == kAccessMode_None ? NULL : mBuffer; + 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); + mFormat, GL_UNSIGNED_BYTE, &mBuffer[offset]); } /////////////////////////////////////////////////////////////////////////////// @@ -85,12 +81,12 @@ public: GpuPixelBuffer(GLenum format, uint32_t width, uint32_t height); ~GpuPixelBuffer(); - uint8_t* map(AccessMode mode = kAccessMode_ReadWrite); - void unmap(); + uint8_t* map(AccessMode mode = kAccessMode_ReadWrite) override; + void unmap() override; - uint8_t* getMappedPointer() const; + uint8_t* getMappedPointer() const override; - void upload(uint32_t x, uint32_t y, uint32_t width, uint32_t height, int offset); + void upload(uint32_t x, uint32_t y, uint32_t width, uint32_t height, int offset) override; private: GLuint mBuffer; @@ -98,12 +94,16 @@ private: Caches& mCaches; }; -GpuPixelBuffer::GpuPixelBuffer(GLenum format, uint32_t width, uint32_t height): - PixelBuffer(format, width, height), mMappedPointer(0), mCaches(Caches::getInstance()) { +GpuPixelBuffer::GpuPixelBuffer(GLenum format, + uint32_t width, uint32_t height) + : PixelBuffer(format, width, height) + , mMappedPointer(nullptr) + , mCaches(Caches::getInstance()){ glGenBuffers(1, &mBuffer); - mCaches.bindPixelBuffer(mBuffer); - glBufferData(GL_PIXEL_UNPACK_BUFFER, getSize(), NULL, GL_DYNAMIC_DRAW); - mCaches.unbindPixelBuffer(); + + mCaches.pixelBufferState().bind(mBuffer); + glBufferData(GL_PIXEL_UNPACK_BUFFER, getSize(), nullptr, GL_DYNAMIC_DRAW); + mCaches.pixelBufferState().unbind(); } GpuPixelBuffer::~GpuPixelBuffer() { @@ -112,7 +112,7 @@ GpuPixelBuffer::~GpuPixelBuffer() { uint8_t* GpuPixelBuffer::map(AccessMode mode) { if (mAccessMode == kAccessMode_None) { - mCaches.bindPixelBuffer(mBuffer); + mCaches.pixelBufferState().bind(mBuffer); mMappedPointer = (uint8_t*) glMapBufferRange(GL_PIXEL_UNPACK_BUFFER, 0, getSize(), mode); #if DEBUG_OPENGL if (!mMappedPointer) { @@ -131,14 +131,14 @@ uint8_t* GpuPixelBuffer::map(AccessMode mode) { void GpuPixelBuffer::unmap() { if (mAccessMode != kAccessMode_None) { if (mMappedPointer) { - mCaches.bindPixelBuffer(mBuffer); + mCaches.pixelBufferState().bind(mBuffer); GLboolean status = glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER); if (status == GL_FALSE) { ALOGE("Corrupted GPU pixel buffer"); } } mAccessMode = kAccessMode_None; - mMappedPointer = NULL; + mMappedPointer = nullptr; } } @@ -148,7 +148,7 @@ uint8_t* GpuPixelBuffer::getMappedPointer() const { 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.bindPixelBuffer(mBuffer); + mCaches.pixelBufferState().bind(mBuffer); unmap(); glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height, mFormat, GL_UNSIGNED_BYTE, reinterpret_cast<void*>(offset)); @@ -158,7 +158,8 @@ void GpuPixelBuffer::upload(uint32_t x, uint32_t y, uint32_t width, uint32_t hei // Factory /////////////////////////////////////////////////////////////////////////////// -PixelBuffer* PixelBuffer::create(GLenum format, uint32_t width, uint32_t height, BufferType type) { +PixelBuffer* PixelBuffer::create(GLenum format, + uint32_t width, uint32_t height, BufferType type) { if (type == kBufferType_Auto && Caches::getInstance().gpuPixelBuffersEnabled) { return new GpuPixelBuffer(format, width, height); } diff --git a/libs/hwui/PixelBuffer.h b/libs/hwui/PixelBuffer.h index 04225a2..aac5ec4 100644 --- a/libs/hwui/PixelBuffer.h +++ b/libs/hwui/PixelBuffer.h @@ -18,6 +18,7 @@ #define ANDROID_HWUI_PIXEL_BUFFER_H #include <GLES3/gl3.h> +#include <cutils/log.h> namespace android { namespace uirenderer { diff --git a/libs/hwui/Program.cpp b/libs/hwui/Program.cpp index e6fd2dc..32713e9 100644 --- a/libs/hwui/Program.cpp +++ b/libs/hwui/Program.cpp @@ -46,7 +46,7 @@ Program::Program(const ProgramDescription& description, const char* vertex, cons glAttachShader(mProgramId, mVertexShader); glAttachShader(mProgramId, mFragmentShader); - position = bindAttrib("position", kBindingPosition); + bindAttrib("position", kBindingPosition); if (description.hasTexture || description.hasExternalTexture) { texCoords = bindAttrib("texCoords", kBindingTexCoords); } else { @@ -64,7 +64,7 @@ Program::Program(const ProgramDescription& description, const char* vertex, cons glGetProgramiv(mProgramId, GL_INFO_LOG_LENGTH, &infoLen); if (infoLen > 1) { GLchar log[infoLen]; - glGetProgramInfoLog(mProgramId, infoLen, 0, &log[0]); + glGetProgramInfoLog(mProgramId, infoLen, nullptr, &log[0]); ALOGE("%s", log); } LOG_ALWAYS_FATAL("Error while linking shaders"); @@ -135,7 +135,7 @@ GLuint Program::buildShader(const char* source, GLenum type) { ATRACE_NAME("Build GL Shader"); GLuint shader = glCreateShader(type); - glShaderSource(shader, 1, &source, 0); + glShaderSource(shader, 1, &source, nullptr); glCompileShader(shader); GLint status; @@ -145,7 +145,7 @@ GLuint Program::buildShader(const char* source, GLenum type) { // Some drivers return wrong values for GL_INFO_LOG_LENGTH // use a fixed size instead GLchar log[512]; - glGetShaderInfoLog(shader, sizeof(log), 0, &log[0]); + glGetShaderInfoLog(shader, sizeof(log), nullptr, &log[0]); LOG_ALWAYS_FATAL("Shader info log: %s", log); return 0; } @@ -177,12 +177,12 @@ void Program::set(const mat4& projectionMatrix, const mat4& modelViewMatrix, glUniformMatrix4fv(transform, 1, GL_FALSE, &t.data[0]); } -void Program::setColor(const float r, const float g, const float b, const float a) { +void Program::setColor(FloatColor color) { if (!mHasColorUniform) { mColorUniform = getUniform("color"); mHasColorUniform = true; } - glUniform4f(mColorUniform, r, g, b, a); + glUniform4f(mColorUniform, color.r, color.g, color.b, color.a); } void Program::use() { diff --git a/libs/hwui/Program.h b/libs/hwui/Program.h index d05b331..af1e4a7 100644 --- a/libs/hwui/Program.h +++ b/libs/hwui/Program.h @@ -25,6 +25,7 @@ #include <SkXfermode.h> #include "Debug.h" +#include "FloatColor.h" #include "Matrix.h" #include "Properties.h" @@ -102,7 +103,7 @@ typedef uint64_t programid; * A ProgramDescription must be used in conjunction with a ProgramCache. */ struct ProgramDescription { - enum ColorModifier { + enum ColorFilterMode { kColorNone = 0, kColorMatrix, kColorBlend @@ -148,7 +149,7 @@ struct ProgramDescription { GLenum bitmapWrapT; // Color operations - ColorModifier colorOp; + ColorFilterMode colorOp; SkXfermode::Mode colorMode; // Framebuffer blending (requires Extensions.hasFramebufferFetch()) @@ -363,15 +364,10 @@ public: /** * Sets the color associated with this shader. */ - void setColor(const float r, const float g, const float b, const float a); + void setColor(FloatColor color); /** - * Name of the position attribute. - */ - int position; - - /** - * Name of the texCoords attribute if it exists, -1 otherwise. + * Name of the texCoords attribute if it exists (kBindingTexCoords), -1 otherwise. */ int texCoords; diff --git a/libs/hwui/ProgramCache.cpp b/libs/hwui/ProgramCache.cpp index 62835e0..41adda1 100644 --- a/libs/hwui/ProgramCache.cpp +++ b/libs/hwui/ProgramCache.cpp @@ -240,8 +240,9 @@ const char* gFS_Main_ModulateColor = const char* gFS_Main_ApplyVertexAlphaLinearInterp = " fragColor *= alpha;\n"; const char* gFS_Main_ApplyVertexAlphaShadowInterp = - " fragColor *= (1.0 - cos(alpha)) / 2.0;\n"; - + // Use a gaussian function for the shadow fall off. Note that alpha here + // is actually (1.0 - alpha) for saving computation. + " fragColor *= exp(- alpha * alpha * 4.0) - 0.018;\n"; const char* gFS_Main_FetchTexture[2] = { // Don't modulate " fragColor = texture2D(baseSampler, outTexCoords);\n", @@ -380,9 +381,9 @@ const char* gBlendOps[18] = { // Xor "return vec4(src.rgb * (1.0 - dst.a) + (1.0 - src.a) * dst.rgb, " "src.a + dst.a - 2.0 * src.a * dst.a);\n", - // Add + // Plus "return min(src + dst, 1.0);\n", - // Multiply + // Modulate "return src * dst;\n", // Screen "return src + dst - src * dst;\n", @@ -404,7 +405,8 @@ const char* gBlendOps[18] = { // Constructors/destructors /////////////////////////////////////////////////////////////////////////////// -ProgramCache::ProgramCache(): mHasES3(Extensions::getInstance().getMajorGlVersion() >= 3) { +ProgramCache::ProgramCache(Extensions& extensions) + : mHasES3(extensions.getMajorGlVersion() >= 3) { } ProgramCache::~ProgramCache() { @@ -417,11 +419,6 @@ ProgramCache::~ProgramCache() { void ProgramCache::clear() { PROGRAM_LOGD("Clearing program cache"); - - size_t count = mCache.size(); - for (size_t i = 0; i < count; i++) { - delete mCache.valueAt(i); - } mCache.clear(); } @@ -433,14 +430,14 @@ Program* ProgramCache::get(const ProgramDescription& description) { key = PROGRAM_KEY_TEXTURE; } - ssize_t index = mCache.indexOfKey(key); - Program* program = NULL; - if (index < 0) { + auto iter = mCache.find(key); + Program* program = nullptr; + if (iter == mCache.end()) { description.log("Could not find program"); program = generateProgram(description, key); - mCache.add(key, program); + mCache[key] = std::unique_ptr<Program>(program); } else { - program = mCache.valueAt(index); + program = iter->second.get(); } return program; } @@ -834,7 +831,7 @@ void ProgramCache::printLongString(const String8& shader) const { while ((index = shader.find("\n", index)) > -1) { String8 line(str, index - lastIndex); if (line.length() == 0) line.append("\n"); - PROGRAM_LOGD("%s", line.string()); + ALOGD("%s", line.string()); index++; str += (index - lastIndex); lastIndex = index; diff --git a/libs/hwui/ProgramCache.h b/libs/hwui/ProgramCache.h index 38f6f99..1dadda1 100644 --- a/libs/hwui/ProgramCache.h +++ b/libs/hwui/ProgramCache.h @@ -20,12 +20,12 @@ #include <utils/KeyedVector.h> #include <utils/Log.h> #include <utils/String8.h> +#include <map> #include <GLES2/gl2.h> #include "Debug.h" #include "Program.h" -#include "Properties.h" namespace android { namespace uirenderer { @@ -40,7 +40,7 @@ namespace uirenderer { */ class ProgramCache { public: - ProgramCache(); + ProgramCache(Extensions& extensions); ~ProgramCache(); Program* get(const ProgramDescription& description); @@ -56,7 +56,7 @@ private: void printLongString(const String8& shader) const; - KeyedVector<programid, Program*> mCache; + std::map<programid, std::unique_ptr<Program>> mCache; const bool mHasES3; }; // class ProgramCache diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h index befed16..a0312e1 100644 --- a/libs/hwui/Properties.h +++ b/libs/hwui/Properties.h @@ -253,9 +253,9 @@ enum DebugLevel { // Converts a number of kilo-bytes into bytes #define KB(s) s * 1024 -static DebugLevel readDebugLevel() { +static inline DebugLevel readDebugLevel() { char property[PROPERTY_VALUE_MAX]; - if (property_get(PROPERTY_DEBUG, property, NULL) > 0) { + if (property_get(PROPERTY_DEBUG, property, nullptr) > 0) { return (DebugLevel) atoi(property); } return kDebugDisabled; diff --git a/libs/hwui/Query.h b/libs/hwui/Query.h deleted file mode 100644 index e25b16b..0000000 --- a/libs/hwui/Query.h +++ /dev/null @@ -1,152 +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_QUERY_H -#define ANDROID_HWUI_QUERY_H - -#include <GLES3/gl3.h> - -#include "Extensions.h" - -namespace android { -namespace uirenderer { - -/** - * A Query instance can be used to perform occlusion queries. If the device - * does not support occlusion queries, the result of a query will always be - * 0 and the result will always be marked available. - * - * To run an occlusion query successfully, you must start end end the query: - * - * Query query; - * query.begin(); - * // execute OpenGL calls - * query.end(); - * GLuint result = query.getResult(); - */ -class Query { -public: - /** - * Possible query targets. - */ - enum Target { - /** - * Indicates if any sample passed the depth & stencil tests. - */ - kTargetSamples = GL_ANY_SAMPLES_PASSED, - /** - * Indicates if any sample passed the depth & stencil tests. - * The implementation may choose to use a less precise version - * of the test, potentially resulting in false positives. - */ - kTargetConservativeSamples = GL_ANY_SAMPLES_PASSED_CONSERVATIVE, - }; - - /** - * Creates a new query with the specified target. The default - * target is kTargetSamples (of GL_ANY_SAMPLES_PASSED in OpenGL.) - */ - Query(Target target = kTargetSamples): mActive(false), mTarget(target), - mCanQuery(Extensions::getInstance().hasOcclusionQueries()), - mQuery(0) { - } - - ~Query() { - if (mQuery) { - glDeleteQueries(1, &mQuery); - } - } - - /** - * Begins the query. If the query has already begun or if the device - * does not support occlusion queries, calling this method as no effect. - * After calling this method successfully, the query is marked active. - */ - void begin() { - if (!mActive && mCanQuery) { - if (!mQuery) { - glGenQueries(1, &mQuery); - } - - glBeginQuery(mTarget, mQuery); - mActive = true; - } - } - - /** - * Ends the query. If the query has already begun or if the device - * does not support occlusion queries, calling this method as no effect. - * After calling this method successfully, the query is marked inactive. - */ - void end() { - if (mQuery && mActive) { - glEndQuery(mTarget); - mActive = false; - } - } - - /** - * Returns true if the query is active, false otherwise. - */ - bool isActive() { - return mActive; - } - - /** - * Returns true if the result of the query is available, - * false otherwise. Calling getResult() before the result - * is available may result in the calling thread being blocked. - * If the device does not support queries, this method always - * returns true. - */ - bool isResultAvailable() { - if (!mQuery) return true; - - GLuint result; - glGetQueryObjectuiv(mQuery, GL_QUERY_RESULT_AVAILABLE, &result); - return result == GL_TRUE; - } - - /** - * Returns the result of the query. If the device does not - * support queries this method will return 0. - * - * Calling this method implicitely calls end() if the query - * is currently active. - */ - GLuint getResult() { - if (!mQuery) return 0; - - end(); - - GLuint result; - glGetQueryObjectuiv(mQuery, GL_QUERY_RESULT, &result); - return result; - } - - -private: - bool mActive; - GLenum mTarget; - bool mCanQuery; - GLuint mQuery; - -}; // class Query - -}; // namespace uirenderer -}; // namespace android - -#endif // ANDROID_HWUI_QUERY_H diff --git a/libs/hwui/Rect.h b/libs/hwui/Rect.h index 13265a9..c82082f 100644 --- a/libs/hwui/Rect.h +++ b/libs/hwui/Rect.h @@ -111,6 +111,10 @@ public: set(r.left, r.top, r.right, r.bottom); } + inline void set(const SkIRect& r) { + set(r.left(), r.top(), r.right(), r.bottom()); + } + inline float getWidth() const { return right - left; } @@ -248,7 +252,22 @@ public: bottom = fmaxf(bottom, y); } - void dump(const char* label = NULL) const { + void expandToCoverRect(float otherLeft, float otherTop, float otherRight, float otherBottom) { + left = fminf(left, otherLeft); + top = fminf(top, otherTop); + right = fmaxf(right, otherRight); + bottom = fmaxf(bottom, otherBottom); + } + + SkRect toSkRect() const { + return SkRect::MakeLTRB(left, top, right, bottom); + } + + SkIRect toSkIRect() const { + return SkIRect::MakeLTRB(left, top, right, bottom); + } + + void dump(const char* label = nullptr) const { ALOGD("%s[l=%f t=%f r=%f b=%f]", label ? label : "Rect", left, top, right, bottom); } diff --git a/libs/hwui/RenderBufferCache.cpp b/libs/hwui/RenderBufferCache.cpp index 830a13a..d0812c9 100644 --- a/libs/hwui/RenderBufferCache.cpp +++ b/libs/hwui/RenderBufferCache.cpp @@ -42,7 +42,7 @@ namespace uirenderer { RenderBufferCache::RenderBufferCache(): mSize(0), mMaxSize(MB(DEFAULT_RENDER_BUFFER_CACHE_SIZE)) { char property[PROPERTY_VALUE_MAX]; - if (property_get(PROPERTY_RENDER_BUFFER_CACHE_SIZE, property, NULL) > 0) { + if (property_get(PROPERTY_RENDER_BUFFER_CACHE_SIZE, property, nullptr) > 0) { INIT_LOGD(" Setting render buffer cache size to %sMB", property); setMaxSize(MB(atof(property))); } else { @@ -108,7 +108,7 @@ void RenderBufferCache::clear() { } RenderBuffer* RenderBufferCache::get(GLenum format, const uint32_t width, const uint32_t height) { - RenderBuffer* buffer = NULL; + RenderBuffer* buffer = nullptr; RenderBufferEntry entry(format, width, height); ssize_t index = mCache.indexOf(entry); @@ -158,6 +158,11 @@ bool RenderBufferCache::put(RenderBuffer* buffer) { buffer->getWidth(), buffer->getHeight()); return true; + } else { + RENDER_BUFFER_LOGD("Deleted %s render buffer (%dx%d) Size=%d, MaxSize=%d", + RenderBuffer::formatName(buffer->getFormat()), + buffer->getWidth(), buffer->getHeight(), size, mMaxSize); + delete buffer; } return false; } diff --git a/libs/hwui/RenderBufferCache.h b/libs/hwui/RenderBufferCache.h index af8060f..6c668b0 100644 --- a/libs/hwui/RenderBufferCache.h +++ b/libs/hwui/RenderBufferCache.h @@ -78,11 +78,11 @@ public: private: struct RenderBufferEntry { RenderBufferEntry(): - mBuffer(NULL), mWidth(0), mHeight(0) { + mBuffer(nullptr), mWidth(0), mHeight(0) { } RenderBufferEntry(GLenum format, const uint32_t width, const uint32_t height): - mBuffer(NULL), mFormat(format), mWidth(width), mHeight(height) { + mBuffer(nullptr), mFormat(format), mWidth(width), mHeight(height) { } RenderBufferEntry(RenderBuffer* buffer): diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp index 787ee62..e009451 100644 --- a/libs/hwui/RenderNode.cpp +++ b/libs/hwui/RenderNode.cpp @@ -29,9 +29,9 @@ #include "DamageAccumulator.h" #include "Debug.h" #include "DisplayListOp.h" -#include "DisplayListLogBuffer.h" #include "LayerRenderer.h" #include "OpenGLRenderer.h" +#include "TreeInfo.h" #include "utils/MathUtils.h" #include "utils/TraceUtils.h" #include "renderthread/CanvasContext.h" @@ -39,28 +39,6 @@ namespace android { namespace uirenderer { -void RenderNode::outputLogBuffer(int fd) { - DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance(); - if (logBuffer.isEmpty()) { - return; - } - - FILE *file = fdopen(fd, "a"); - - fprintf(file, "\nRecent DisplayList operations\n"); - logBuffer.outputCommands(file); - - if (Caches::hasInstance()) { - String8 cachesLog; - Caches::getInstance().dumpMemoryUsage(cachesLog); - fprintf(file, "\nCaches:\n%s\n", cachesLog.string()); - } else { - fprintf(file, "\nNo caches instance.\n"); - } - - fflush(file); -} - void RenderNode::debugDumpLayers(const char* prefix) { if (mLayer) { ALOGD("%sNode %p (%s) has layer %p (fbo = %u, wasBuildLayered = %s)", @@ -77,10 +55,10 @@ void RenderNode::debugDumpLayers(const char* prefix) { RenderNode::RenderNode() : mDirtyPropertyFields(0) , mNeedsDisplayListDataSync(false) - , mDisplayListData(0) - , mStagingDisplayListData(0) + , mDisplayListData(nullptr) + , mStagingDisplayListData(nullptr) , mAnimatorManager(*this) - , mLayer(0) + , mLayer(nullptr) , mParentCount(0) { } @@ -90,7 +68,7 @@ RenderNode::~RenderNode() { if (mLayer) { ALOGW("Memory Warning: Layer %p missed its detachment, held on to for far too long!", mLayer); mLayer->postDecStrong(); - mLayer = 0; + mLayer = nullptr; } } @@ -109,7 +87,7 @@ void RenderNode::output(uint32_t level) { getName(), (properties().hasShadow() ? ", casting shadow" : ""), (isRenderable() ? "" : ", empty"), - (mLayer != NULL ? ", on HW Layer" : "")); + (mLayer != nullptr ? ", on HW Layer" : "")); ALOGD("%*s%s %d", level * 2, "", "Save", SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag); @@ -161,7 +139,7 @@ void RenderNode::damageSelf(TreeInfo& info) { void RenderNode::prepareLayer(TreeInfo& info, uint32_t dirtyMask) { LayerType layerType = properties().layerProperties().type(); - if (CC_UNLIKELY(layerType == kLayerTypeRenderLayer)) { + if (CC_UNLIKELY(layerType == LayerType::RenderLayer)) { // Damage applied so far needs to affect our parent, but does not require // the layer to be updated. So we pop/push here to clear out the current // damage and get a clean state for display list or children updates to @@ -178,10 +156,10 @@ void RenderNode::pushLayerUpdate(TreeInfo& info) { LayerType layerType = properties().layerProperties().type(); // If we are not a layer OR we cannot be rendered (eg, view was detached) // we need to destroy any Layers we may have had previously - if (CC_LIKELY(layerType != kLayerTypeRenderLayer) || CC_UNLIKELY(!isRenderable())) { + if (CC_LIKELY(layerType != LayerType::RenderLayer) || CC_UNLIKELY(!isRenderable())) { if (CC_UNLIKELY(mLayer)) { LayerRenderer::destroyLayer(mLayer); - mLayer = NULL; + mLayer = nullptr; } return; } @@ -195,7 +173,7 @@ void RenderNode::pushLayerUpdate(TreeInfo& info) { } else if (mLayer->layer.getWidth() != getWidth() || mLayer->layer.getHeight() != getHeight()) { if (!LayerRenderer::resizeLayer(mLayer, getWidth(), getHeight())) { LayerRenderer::destroyLayer(mLayer); - mLayer = 0; + mLayer = nullptr; } damageSelf(info); transformUpdateNeeded = true; @@ -222,7 +200,7 @@ void RenderNode::pushLayerUpdate(TreeInfo& info) { } if (dirty.intersect(0, 0, getWidth(), getHeight())) { - dirty.roundOut(); + 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 @@ -310,10 +288,10 @@ void RenderNode::pushStagingDisplayListChanges(TreeInfo& info) { Caches::getInstance().registerFunctors(mStagingDisplayListData->functors.size()); } mDisplayListData = mStagingDisplayListData; - mStagingDisplayListData = NULL; + mStagingDisplayListData = nullptr; if (mDisplayListData) { for (size_t i = 0; i < mDisplayListData->functors.size(); i++) { - (*mDisplayListData->functors[i])(DrawGlInfo::kModeSync, NULL); + (*mDisplayListData->functors[i])(DrawGlInfo::kModeSync, nullptr); } } damageSelf(info); @@ -330,18 +308,13 @@ void RenderNode::deleteDisplayListData() { } } delete mDisplayListData; - mDisplayListData = NULL; + mDisplayListData = nullptr; } void RenderNode::prepareSubTree(TreeInfo& info, DisplayListData* subtree) { if (subtree) { TextureCache& cache = Caches::getInstance().textureCache; info.out.hasFunctors |= subtree->functors.size(); - // TODO: Fix ownedBitmapResources to not require disabling prepareTextures - // and thus falling out of async drawing path. - if (subtree->ownedBitmapResources.size()) { - info.prepareTextures = false; - } for (size_t i = 0; info.prepareTextures && i < subtree->bitmapResources.size(); i++) { info.prepareTextures = cache.prefetchAndMarkInUse(subtree->bitmapResources[i]); } @@ -358,7 +331,7 @@ void RenderNode::prepareSubTree(TreeInfo& info, DisplayListData* subtree) { void RenderNode::destroyHardwareResources() { if (mLayer) { LayerRenderer::destroyLayer(mLayer); - mLayer = NULL; + mLayer = nullptr; } if (mDisplayListData) { for (size_t i = 0; i < mDisplayListData->children().size(); i++) { @@ -411,7 +384,7 @@ void RenderNode::setViewProperties(OpenGLRenderer& renderer, T& handler) { renderer.concatMatrix(*properties().getTransformMatrix()); } } - const bool isLayer = properties().layerProperties().type() != kLayerTypeNone; + const bool isLayer = properties().layerProperties().type() != LayerType::None; int clipFlags = properties().getClippingFlags(); if (properties().getAlpha() < 1) { if (isLayer) { @@ -429,9 +402,10 @@ void RenderNode::setViewProperties(OpenGLRenderer& renderer, T& handler) { clipFlags = 0; // all clipping done by saveLayer } - ATRACE_FORMAT("%s alpha caused %ssaveLayer %dx%d", - getName(), clipFlags ? "" : "unclipped ", - (int)layerBounds.getWidth(), (int)layerBounds.getHeight()); + ATRACE_FORMAT("%s alpha caused %ssaveLayer %dx%d", getName(), + (saveFlags & SkCanvas::kClipToLayer_SaveFlag) ? "" : "unclipped ", + static_cast<int>(layerBounds.getWidth()), + static_cast<int>(layerBounds.getHeight())); SaveLayerOp* op = new (handler.allocator()) SaveLayerOp( layerBounds.left, layerBounds.top, layerBounds.right, layerBounds.bottom, @@ -516,7 +490,7 @@ void RenderNode::computeOrdering() { // TODO: create temporary DDLOp and call computeOrderingImpl on top DisplayList so that // transform properties are applied correctly to top level children - if (mDisplayListData == NULL) return; + if (mDisplayListData == nullptr) return; for (unsigned int i = 0; i < mDisplayListData->children().size(); i++) { DrawRenderNodeOp* childOp = mDisplayListData->children()[i]; childOp->mRenderNode->computeOrderingImpl(childOp, @@ -530,7 +504,7 @@ void RenderNode::computeOrderingImpl( Vector<DrawRenderNodeOp*>* compositedChildrenOfProjectionSurface, const mat4* transformFromProjectionSurface) { mProjectedNodes.clear(); - if (mDisplayListData == NULL || mDisplayListData->isEmpty()) return; + if (mDisplayListData == nullptr || mDisplayListData->isEmpty()) return; // TODO: should avoid this calculation in most cases // TODO: just calculate single matrix, down to all leaf composited elements @@ -554,9 +528,9 @@ void RenderNode::computeOrderingImpl( DrawRenderNodeOp* childOp = mDisplayListData->children()[i]; RenderNode* child = childOp->mRenderNode; - const SkPath* projectionOutline = NULL; - Vector<DrawRenderNodeOp*>* projectionChildren = NULL; - const mat4* projectionTransform = NULL; + const SkPath* projectionOutline = nullptr; + Vector<DrawRenderNodeOp*>* projectionChildren = nullptr; + const mat4* projectionTransform = nullptr; if (isProjectionReceiver && !child->properties().getProjectBackwards()) { // if receiving projections, collect projecting descendent @@ -682,7 +656,7 @@ void RenderNode::issueDrawShadowOperation(const Matrix4& transformFromParent, T& // holds temporary SkPath to store the result of intersections - SkPath* frameAllocatedPath = NULL; + SkPath* frameAllocatedPath = nullptr; const SkPath* outlinePath = casterOutlinePath; // intersect the outline with the reveal clip, if present @@ -777,7 +751,6 @@ void RenderNode::issueOperationsOf3dChildren(ChildrenSelectMode mode, int restoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag); DrawRenderNodeOp* childOp = zTranslatedNodes[drawIndex].value; - RenderNode* child = childOp->mRenderNode; renderer.concatMatrix(childOp->mTransformFromParent); childOp->mSkipInOrderDraw = false; // this is horrible, I'm so sorry everyone @@ -810,9 +783,9 @@ void RenderNode::issueOperationsOfProjectedChildren(OpenGLRenderer& renderer, T& // If the projection reciever has an outline, we mask each of the projected rendernodes to it // Either with clipRect, or special saveLayer masking - if (projectionReceiverOutline != NULL) { + if (projectionReceiverOutline != nullptr) { const SkRect& outlineBounds = projectionReceiverOutline->getBounds(); - if (projectionReceiverOutline->isRect(NULL)) { + if (projectionReceiverOutline->isRect(nullptr)) { // mask to the rect outline simply with clipRect ClipRectOp* clipOp = new (alloc) ClipRectOp( outlineBounds.left(), outlineBounds.top(), @@ -847,7 +820,7 @@ void RenderNode::issueOperationsOfProjectedChildren(OpenGLRenderer& renderer, T& renderer.restoreToCount(restoreTo); } - if (projectionReceiverOutline != NULL) { + if (projectionReceiverOutline != nullptr) { handler(new (alloc) RestoreToCountOp(restoreTo), PROPERTY_SAVECOUNT, properties().getClipToBounds()); } @@ -864,13 +837,12 @@ void RenderNode::issueOperationsOfProjectedChildren(OpenGLRenderer& renderer, T& */ template <class T> void RenderNode::issueOperations(OpenGLRenderer& renderer, T& handler) { - const int level = handler.level(); if (mDisplayListData->isEmpty()) { DISPLAY_LIST_LOGD("%*sEmpty display list (%p, %s)", level * 2, "", this, getName()); return; } - const bool drawLayer = (mLayer && (&renderer != mLayer->renderer)); + const bool drawLayer = (mLayer && (&renderer != mLayer->renderer.get())); // 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. @@ -888,7 +860,7 @@ void RenderNode::issueOperations(OpenGLRenderer& renderer, T& handler) { #if DEBUG_DISPLAY_LIST const Rect& clipRect = renderer.getLocalClipBounds(); DISPLAY_LIST_LOGD("%*sStart display list (%p, %s), localClipBounds: %.0f, %.0f, %.0f, %.0f", - level * 2, "", this, getName(), + handler.level() * 2, "", this, getName(), clipRect.left, clipRect.top, clipRect.right, clipRect.bottom); #endif @@ -915,7 +887,6 @@ void RenderNode::issueOperations(OpenGLRenderer& renderer, T& handler) { } else { const int saveCountOffset = renderer.getSaveCount() - 1; const int projectionReceiveIndex = mDisplayListData->projectionReceiveIndex; - DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance(); for (size_t chunkIndex = 0; chunkIndex < mDisplayListData->getChunks().size(); chunkIndex++) { const DisplayListData::Chunk& chunk = mDisplayListData->getChunks()[chunkIndex]; @@ -926,15 +897,15 @@ void RenderNode::issueOperations(OpenGLRenderer& renderer, T& handler) { initialTransform, zTranslatedNodes, renderer, handler); - for (int opIndex = chunk.beginOpIndex; opIndex < chunk.endOpIndex; opIndex++) { + for (size_t opIndex = chunk.beginOpIndex; opIndex < chunk.endOpIndex; opIndex++) { DisplayListOp *op = mDisplayListData->displayListOps[opIndex]; #if DEBUG_DISPLAY_LIST - op->output(level + 1); + op->output(handler.level() + 1); #endif - logBuffer.writeCommand(level, op->name()); handler(op, saveCountOffset, properties().getClipToBounds()); - if (CC_UNLIKELY(!mProjectedNodes.isEmpty() && opIndex == projectionReceiveIndex)) { + if (CC_UNLIKELY(!mProjectedNodes.isEmpty() && projectionReceiveIndex >= 0 && + opIndex == static_cast<size_t>(projectionReceiveIndex))) { issueOperationsOfProjectedChildren(renderer, handler); } } diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h index 2ce7cb7..d0d81d9 100644 --- a/libs/hwui/RenderNode.h +++ b/libs/hwui/RenderNode.h @@ -33,13 +33,10 @@ #include <androidfw/ResourceTypes.h> #include "AnimatorManager.h" -#include "DamageAccumulator.h" #include "Debug.h" #include "Matrix.h" -#include "DeferredDisplayList.h" #include "DisplayList.h" #include "RenderProperties.h" -#include "TreeInfo.h" class SkBitmap; class SkPaint; @@ -50,7 +47,7 @@ namespace android { namespace uirenderer { class DisplayListOp; -class DisplayListRenderer; +class DisplayListCanvas; class OpenGLRenderer; class Rect; class Layer; @@ -61,12 +58,13 @@ class SaveLayerOp; class SaveOp; class RestoreToCountOp; class DrawRenderNodeOp; +class TreeInfo; /** * 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 DisplayListRenderer (which manages the recording), DisplayListData + * functionality is split between DisplayListCanvas (which manages the recording), DisplayListData * (which holds the actual data), and DisplayList (which holds properties and performs playback onto * a renderer). * diff --git a/libs/hwui/RenderProperties.cpp b/libs/hwui/RenderProperties.cpp index 250cadc..9f1ceed 100644 --- a/libs/hwui/RenderProperties.cpp +++ b/libs/hwui/RenderProperties.cpp @@ -33,19 +33,17 @@ namespace android { namespace uirenderer { -LayerProperties::LayerProperties() - : mType(kLayerTypeNone) - , mColorFilter(NULL) { +LayerProperties::LayerProperties() { reset(); } LayerProperties::~LayerProperties() { - setType(kLayerTypeNone); + setType(LayerType::None); } void LayerProperties::reset() { mOpaque = false; - setFromPaint(NULL); + setFromPaint(nullptr); } bool LayerProperties::setColorFilter(SkColorFilter* filter) { @@ -61,7 +59,7 @@ bool LayerProperties::setFromPaint(const SkPaint* paint) { OpenGLRenderer::getAlphaAndModeDirect(paint, &alpha, &mode); changed |= setAlpha(static_cast<uint8_t>(alpha)); changed |= setXferMode(mode); - changed |= setColorFilter(paint ? paint->getColorFilter() : NULL); + changed |= setColorFilter(paint ? paint->getColorFilter() : nullptr); return changed; } @@ -92,7 +90,7 @@ RenderProperties::PrimitiveFields::PrimitiveFields() } RenderProperties::ComputedFields::ComputedFields() - : mTransformMatrix(NULL) { + : mTransformMatrix(nullptr) { } RenderProperties::ComputedFields::~ComputedFields() { @@ -100,8 +98,8 @@ RenderProperties::ComputedFields::~ComputedFields() { } RenderProperties::RenderProperties() - : mStaticMatrix(NULL) - , mAnimationMatrix(NULL) { + : mStaticMatrix(nullptr) + , mAnimationMatrix(nullptr) { } RenderProperties::~RenderProperties() { @@ -146,7 +144,7 @@ void RenderProperties::debugOutputProperties(const int level) const { } } - const bool isLayer = layerProperties().type() != kLayerTypeNone; + const bool isLayer = layerProperties().type() != LayerType::None; int clipFlags = getClippingFlags(); if (mPrimitiveFields.mAlpha < 1) { if (isLayer) { diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h index 31c4f22..61e98d2 100644 --- a/libs/hwui/RenderProperties.h +++ b/libs/hwui/RenderProperties.h @@ -26,8 +26,8 @@ #include <SkCamera.h> #include <SkMatrix.h> #include <SkRegion.h> +#include <SkXfermode.h> -#include "Animator.h" #include "Rect.h" #include "RevealClip.h" #include "Outline.h" @@ -49,12 +49,12 @@ class RenderProperties; #define RP_SET_AND_DIRTY(a, b) RP_SET(a, b, mPrimitiveFields.mMatrixOrPivotDirty = true) // Keep in sync with View.java:LAYER_TYPE_* -enum LayerType { - kLayerTypeNone = 0, +enum class LayerType { + None = 0, // Although we cannot build the software layer directly (must be done at // record time), this information is used when applying alpha. - kLayerTypeSoftware = 1, - kLayerTypeRenderLayer = 2, + Software = 1, + RenderLayer = 2, // TODO: LayerTypeSurfaceTexture? Maybe? }; @@ -124,12 +124,12 @@ private: friend class RenderProperties; - LayerType mType; + LayerType mType = LayerType::None; // Whether or not that Layer's content is opaque, doesn't include alpha bool mOpaque; uint8_t mAlpha; SkXfermode::Mode mMode; - SkColorFilter* mColorFilter; + SkColorFilter* mColorFilter = nullptr; }; /* @@ -188,7 +188,7 @@ public: if (matrix) { mStaticMatrix = new SkMatrix(*matrix); } else { - mStaticMatrix = NULL; + mStaticMatrix = nullptr; } return true; } @@ -203,7 +203,7 @@ public: if (matrix) { mAnimationMatrix = new SkMatrix(*matrix); } else { - mAnimationMatrix = NULL; + mAnimationMatrix = nullptr; } return true; } @@ -571,7 +571,7 @@ public: bool hasShadow() const { return getZ() > 0.0f - && getOutline().getPath() != NULL + && getOutline().getPath() != nullptr && getOutline().getAlpha() != 0.0f; } diff --git a/libs/hwui/RenderState.cpp b/libs/hwui/RenderState.cpp deleted file mode 100644 index ca640e5..0000000 --- a/libs/hwui/RenderState.cpp +++ /dev/null @@ -1,188 +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. - */ -#include "RenderState.h" - -#include "renderthread/CanvasContext.h" -#include "renderthread/EglManager.h" - -namespace android { -namespace uirenderer { - -RenderState::RenderState(renderthread::RenderThread& thread) - : mRenderThread(thread) - , mCaches(NULL) - , mViewportWidth(0) - , mViewportHeight(0) - , mFramebuffer(0) { - mThreadId = pthread_self(); -} - -RenderState::~RenderState() { -} - -void RenderState::onGLContextCreated() { - // This is delayed because the first access of Caches makes GL calls - mCaches = &Caches::getInstance(); - mCaches->init(); - mCaches->setRenderState(this); - mCaches->textureCache.setAssetAtlas(&mAssetAtlas); -} - -static void layerLostGlContext(Layer* layer) { - layer->onGlContextLost(); -} - -void RenderState::onGLContextDestroyed() { -/* - size_t size = mActiveLayers.size(); - if (CC_UNLIKELY(size != 0)) { - ALOGE("Crashing, have %d contexts and %d layers at context destruction. isempty %d", - mRegisteredContexts.size(), size, mActiveLayers.empty()); - mCaches->dumpMemoryUsage(); - for (std::set<renderthread::CanvasContext*>::iterator cit = mRegisteredContexts.begin(); - cit != mRegisteredContexts.end(); cit++) { - renderthread::CanvasContext* context = *cit; - ALOGE("Context: %p (root = %p)", context, context->mRootRenderNode.get()); - ALOGE(" Prefeteched layers: %zu", context->mPrefetechedLayers.size()); - for (std::set<RenderNode*>::iterator pit = context->mPrefetechedLayers.begin(); - pit != context->mPrefetechedLayers.end(); pit++) { - (*pit)->debugDumpLayers(" "); - } - context->mRootRenderNode->debugDumpLayers(" "); - } - - - if (mActiveLayers.begin() == mActiveLayers.end()) { - ALOGE("set has become empty. wat."); - } - for (std::set<const Layer*>::iterator lit = mActiveLayers.begin(); - lit != mActiveLayers.end(); lit++) { - const Layer* layer = *(lit); - ALOGE("Layer %p, state %d, texlayer %d, fbo %d, buildlayered %d", - layer, layer->state, layer->isTextureLayer(), layer->getFbo(), layer->wasBuildLayered); - } - LOG_ALWAYS_FATAL("%d layers have survived gl context destruction", size); - } -*/ - std::for_each(mActiveLayers.begin(), mActiveLayers.end(), layerLostGlContext); - mAssetAtlas.terminate(); -} - -void RenderState::setViewport(GLsizei width, GLsizei height) { - mViewportWidth = width; - mViewportHeight = height; - glViewport(0, 0, mViewportWidth, mViewportHeight); -} - - -void RenderState::getViewport(GLsizei* outWidth, GLsizei* outHeight) { - *outWidth = mViewportWidth; - *outHeight = mViewportHeight; -} - -void RenderState::bindFramebuffer(GLuint fbo) { - if (mFramebuffer != fbo) { - mFramebuffer = fbo; - glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer); - } -} - -void RenderState::invokeFunctor(Functor* functor, DrawGlInfo::Mode mode, DrawGlInfo* info) { - interruptForFunctorInvoke(); - (*functor)(mode, info); - resumeFromFunctorInvoke(); -} - -void RenderState::interruptForFunctorInvoke() { - if (mCaches->currentProgram) { - if (mCaches->currentProgram->isInUse()) { - mCaches->currentProgram->remove(); - mCaches->currentProgram = NULL; - } - } - mCaches->resetActiveTexture(); - mCaches->unbindMeshBuffer(); - mCaches->unbindIndicesBuffer(); - mCaches->resetVertexPointers(); - mCaches->disableTexCoordsVertexArray(); - debugOverdraw(false, false); -} - -void RenderState::resumeFromFunctorInvoke() { - glViewport(0, 0, mViewportWidth, mViewportHeight); - glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer); - debugOverdraw(false, false); - - glClearColor(0.0f, 0.0f, 0.0f, 0.0f); - - mCaches->scissorEnabled = glIsEnabled(GL_SCISSOR_TEST); - mCaches->enableScissor(); - mCaches->resetScissor(); - - mCaches->activeTexture(0); - mCaches->resetBoundTextures(); - - mCaches->blend = true; - glEnable(GL_BLEND); - glBlendFunc(mCaches->lastSrcMode, mCaches->lastDstMode); - glBlendEquation(GL_FUNC_ADD); -} - -void RenderState::debugOverdraw(bool enable, bool clear) { - if (mCaches->debugOverdraw && mFramebuffer == 0) { - if (clear) { - mCaches->disableScissor(); - mCaches->stencil.clear(); - } - if (enable) { - mCaches->stencil.enableDebugWrite(); - } else { - mCaches->stencil.disable(); - } - } -} - -void RenderState::requireGLContext() { - assertOnGLThread(); - mRenderThread.eglManager().requireGlContext(); -} - -void RenderState::assertOnGLThread() { - pthread_t curr = pthread_self(); - LOG_ALWAYS_FATAL_IF(!pthread_equal(mThreadId, curr), "Wrong thread!"); -} - - -class DecStrongTask : public renderthread::RenderTask { -public: - DecStrongTask(VirtualLightRefBase* object) : mObject(object) {} - - virtual void run() { - mObject->decStrong(0); - mObject = 0; - delete this; - } - -private: - VirtualLightRefBase* mObject; -}; - -void RenderState::postDecStrong(VirtualLightRefBase* object) { - mRenderThread.queue(new DecStrongTask(object)); -} - -} /* namespace uirenderer */ -} /* namespace android */ diff --git a/libs/hwui/Renderer.h b/libs/hwui/Renderer.h deleted file mode 100644 index 3159d1e..0000000 --- a/libs/hwui/Renderer.h +++ /dev/null @@ -1,233 +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_RENDERER_H -#define ANDROID_HWUI_RENDERER_H - -#include <SkColorFilter.h> -#include <SkPaint.h> -#include <SkRegion.h> - -#include <utils/String8.h> - -#include "AssetAtlas.h" - -namespace android { - -class Functor; -struct Res_png_9patch; - -namespace uirenderer { - -class RenderNode; -class Layer; -class Matrix4; -class SkiaColorFilter; -class Patch; - -enum DrawOpMode { - kDrawOpMode_Immediate, - kDrawOpMode_Defer, - kDrawOpMode_Flush -}; - -/** - * Hwui's abstract version of Canvas. - * - * Provides methods for frame state operations, as well as the SkCanvas style transform/clip state, - * and varied drawing operations. - * - * Should at some point interact with native SkCanvas. - */ -class ANDROID_API Renderer { -public: - virtual ~Renderer() {} - - /** - * 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; - } - - // TODO: move to a method on android:Paint - static inline bool paintWillNotDraw(const SkPaint& paint) { - return paint.getAlpha() == 0 - && !paint.getColorFilter() - && getXfermode(paint.getXfermode()) == SkXfermode::kSrcOver_Mode; - } - - // TODO: move to a method on android:Paint - static inline bool paintWillNotDrawText(const SkPaint& paint) { - return paint.getAlpha() == 0 - && paint.getLooper() == NULL - && !paint.getColorFilter() - && getXfermode(paint.getXfermode()) == SkXfermode::kSrcOver_Mode; - } - - static bool isBlendedColorFilter(const SkColorFilter* filter) { - if (filter == NULL) { - return false; - } - return (filter->getFlags() & SkColorFilter::kAlphaUnchanged_Flag) == 0; - } - -// ---------------------------------------------------------------------------- -// Frame state operations -// ---------------------------------------------------------------------------- - /** - * Sets the dimension of the underlying drawing surface. This method must - * be called at least once every time the drawing surface changes size. - * - * @param width The width in pixels of the underlysing surface - * @param height The height in pixels of the underlysing surface - */ - virtual void setViewport(int width, int height) = 0; - - /** - * Prepares the renderer to draw a frame. This method must be invoked - * at the beginning of each frame. When this method is invoked, the - * entire drawing surface is assumed to be redrawn. - * - * @param opaque If true, the target surface is considered opaque - * and will not be cleared. If false, the target surface - * will be cleared - */ - virtual status_t prepare(bool opaque) = 0; - - /** - * 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 left The left coordinate of the dirty rectangle - * @param top The top coordinate of the dirty rectangle - * @param right The right coordinate of the dirty rectangle - * @param bottom The bottom coordinate of the dirty rectangle - * @param opaque If true, the target surface is considered opaque - * and will not be cleared. If false, the target surface - * will be cleared in the specified dirty rectangle - */ - virtual status_t prepareDirty(float left, float top, float right, float bottom, - bool opaque) = 0; - - /** - * Indicates the end of a frame. This method must be invoked whenever - * the caller is done rendering a frame. - */ - virtual void finish() = 0; - -// ---------------------------------------------------------------------------- -// Canvas state operations -// ---------------------------------------------------------------------------- - // Save (layer) - virtual int getSaveCount() const = 0; - virtual int save(int flags) = 0; - virtual void restore() = 0; - virtual void restoreToCount(int saveCount) = 0; - - virtual int saveLayer(float left, float top, float right, float bottom, - const SkPaint* paint, int flags) = 0; - - int saveLayerAlpha(float left, float top, float right, float bottom, - int alpha, int flags) { - SkPaint paint; - paint.setAlpha(alpha); - return saveLayer(left, top, right, bottom, &paint, flags); - } - - // Matrix - virtual void getMatrix(SkMatrix* outMatrix) const = 0; - virtual void translate(float dx, float dy, float dz = 0.0f) = 0; - virtual void rotate(float degrees) = 0; - virtual void scale(float sx, float sy) = 0; - virtual void skew(float sx, float sy) = 0; - - virtual void setMatrix(const SkMatrix& matrix) = 0; - virtual void concatMatrix(const SkMatrix& matrix) = 0; - - // clip - virtual const Rect& getLocalClipBounds() const = 0; - virtual bool quickRejectConservative(float left, float top, - float right, float bottom) const = 0; - virtual bool clipRect(float left, float top, float right, float bottom, SkRegion::Op op) = 0; - virtual bool clipPath(const SkPath* path, SkRegion::Op op) = 0; - virtual bool clipRegion(const SkRegion* region, SkRegion::Op op) = 0; - - // Misc - should be implemented with SkPaint inspection - virtual void resetPaintFilter() = 0; - virtual void setupPaintFilter(int clearBits, int setBits) = 0; - -// ---------------------------------------------------------------------------- -// Canvas draw operations -// ---------------------------------------------------------------------------- - virtual status_t drawColor(int color, SkXfermode::Mode mode) = 0; - - // Bitmap-based - virtual status_t drawBitmap(const SkBitmap* bitmap, const SkPaint* paint) = 0; - virtual status_t drawBitmap(const SkBitmap* bitmap, float srcLeft, float srcTop, - float srcRight, float srcBottom, float dstLeft, float dstTop, - float dstRight, float dstBottom, const SkPaint* paint) = 0; - virtual status_t drawBitmapData(const SkBitmap* bitmap, const SkPaint* paint) = 0; - virtual status_t drawBitmapMesh(const SkBitmap* bitmap, int meshWidth, int meshHeight, - const float* vertices, const int* colors, const SkPaint* paint) = 0; - virtual status_t drawPatch(const SkBitmap* bitmap, const Res_png_9patch* patch, - float left, float top, float right, float bottom, const SkPaint* paint) = 0; - - // Shapes - virtual status_t drawRect(float left, float top, float right, float bottom, - const SkPaint* paint) = 0; - virtual status_t drawRects(const float* rects, int count, const SkPaint* paint) = 0; - virtual status_t drawRoundRect(float left, float top, float right, float bottom, - float rx, float ry, const SkPaint* paint) = 0; - virtual status_t drawCircle(float x, float y, float radius, const SkPaint* paint) = 0; - virtual status_t drawOval(float left, float top, float right, float bottom, - const SkPaint* paint) = 0; - virtual status_t drawArc(float left, float top, float right, float bottom, - float startAngle, float sweepAngle, bool useCenter, const SkPaint* paint) = 0; - virtual status_t drawPath(const SkPath* path, const SkPaint* paint) = 0; - virtual status_t drawLines(const float* points, int count, const SkPaint* paint) = 0; - virtual status_t drawPoints(const float* points, int count, const SkPaint* paint) = 0; - - // Text - virtual status_t drawText(const char* text, int bytesCount, int count, float x, float y, - const float* positions, const SkPaint* paint, float totalAdvance, const Rect& bounds, - DrawOpMode drawOpMode = kDrawOpMode_Immediate) = 0; - virtual status_t drawTextOnPath(const char* text, int bytesCount, int count, const SkPath* path, - float hOffset, float vOffset, const SkPaint* paint) = 0; - virtual status_t drawPosText(const char* text, int bytesCount, int count, - const float* positions, const SkPaint* paint) = 0; - -// ---------------------------------------------------------------------------- -// Canvas draw operations - special -// ---------------------------------------------------------------------------- - virtual status_t drawRenderNode(RenderNode* renderNode, Rect& dirty, - int32_t replayFlags) = 0; - - // TODO: rename for consistency - virtual status_t callDrawGLFunction(Functor* functor, Rect& dirty) = 0; -}; // class Renderer - -}; // namespace uirenderer -}; // namespace android - -#endif // ANDROID_HWUI_RENDERER_H diff --git a/libs/hwui/ResourceCache.cpp b/libs/hwui/ResourceCache.cpp index 31bd637..d3b8d70 100644 --- a/libs/hwui/ResourceCache.cpp +++ b/libs/hwui/ResourceCache.cpp @@ -16,16 +16,13 @@ #define LOG_TAG "OpenGLRenderer" -#include <SkPixelRef.h> #include "ResourceCache.h" #include "Caches.h" namespace android { -#ifdef USE_OPENGL_RENDERER using namespace uirenderer; ANDROID_SINGLETON_STATIC_INSTANCE(ResourceCache); -#endif namespace uirenderer { @@ -39,8 +36,8 @@ void ResourceCache::logCache() { ResourceReference* ref = mCache->valueAt(i); ALOGD(" ResourceCache: mCache(%zu): resource, ref = 0x%p, 0x%p", i, mCache->keyAt(i), mCache->valueAt(i)); - ALOGD(" ResourceCache: mCache(%zu): refCount, recycled, destroyed, type = %d, %d, %d, %d", - i, ref->refCount, ref->recycled, ref->destroyed, ref->resourceType); + ALOGD(" ResourceCache: mCache(%zu): refCount, destroyed, type = %d, %d, %d", + i, ref->refCount, ref->destroyed, ref->resourceType); } } @@ -62,17 +59,24 @@ void ResourceCache::unlock() { mLock.unlock(); } -void ResourceCache::incrementRefcount(void* resource, ResourceType resourceType) { +const SkBitmap* ResourceCache::insert(const SkBitmap* bitmapResource) { Mutex::Autolock _l(mLock); - incrementRefcountLocked(resource, resourceType); -} -void ResourceCache::incrementRefcount(const SkBitmap* bitmapResource) { - incrementRefcount((void*) bitmapResource, kBitmap); + BitmapKey bitmapKey(bitmapResource); + ssize_t index = mBitmapCache.indexOfKey(bitmapKey); + if (index == NAME_NOT_FOUND) { + SkBitmap* cachedBitmap = new SkBitmap(*bitmapResource); + index = mBitmapCache.add(bitmapKey, cachedBitmap); + return cachedBitmap; + } + + mBitmapCache.keyAt(index).mRefCount++; + return mBitmapCache.valueAt(index); } -void ResourceCache::incrementRefcount(const SkPath* pathResource) { - incrementRefcount((void*) pathResource, kPath); +void ResourceCache::incrementRefcount(void* resource, ResourceType resourceType) { + Mutex::Autolock _l(mLock); + incrementRefcountLocked(resource, resourceType); } void ResourceCache::incrementRefcount(const Res_png_9patch* patchResource) { @@ -81,37 +85,22 @@ void ResourceCache::incrementRefcount(const Res_png_9patch* patchResource) { void ResourceCache::incrementRefcountLocked(void* resource, ResourceType resourceType) { ssize_t index = mCache->indexOfKey(resource); - ResourceReference* ref = index >= 0 ? mCache->valueAt(index) : NULL; - if (ref == NULL || mCache->size() == 0) { + ResourceReference* ref = index >= 0 ? mCache->valueAt(index) : nullptr; + if (ref == nullptr || mCache->size() == 0) { ref = new ResourceReference(resourceType); mCache->add(resource, ref); } ref->refCount++; } -void ResourceCache::incrementRefcountLocked(const SkBitmap* bitmapResource) { - incrementRefcountLocked((void*) bitmapResource, kBitmap); -} - -void ResourceCache::incrementRefcountLocked(const SkPath* pathResource) { - incrementRefcountLocked((void*) pathResource, kPath); -} - -void ResourceCache::incrementRefcountLocked(const Res_png_9patch* patchResource) { - incrementRefcountLocked((void*) patchResource, kNinePatch); -} - void ResourceCache::decrementRefcount(void* resource) { Mutex::Autolock _l(mLock); decrementRefcountLocked(resource); } void ResourceCache::decrementRefcount(const SkBitmap* bitmapResource) { - decrementRefcount((void*) bitmapResource); -} - -void ResourceCache::decrementRefcount(const SkPath* pathResource) { - decrementRefcount((void*) pathResource); + Mutex::Autolock _l(mLock); + decrementRefcountLocked(bitmapResource); } void ResourceCache::decrementRefcount(const Res_png_9patch* patchResource) { @@ -120,8 +109,8 @@ void ResourceCache::decrementRefcount(const Res_png_9patch* patchResource) { void ResourceCache::decrementRefcountLocked(void* resource) { ssize_t index = mCache->indexOfKey(resource); - ResourceReference* ref = index >= 0 ? mCache->valueAt(index) : NULL; - if (ref == NULL) { + ResourceReference* ref = index >= 0 ? mCache->valueAt(index) : nullptr; + if (ref == nullptr) { // Should not get here - shouldn't get a call to decrement if we're not yet tracking it return; } @@ -132,62 +121,26 @@ void ResourceCache::decrementRefcountLocked(void* resource) { } void ResourceCache::decrementRefcountLocked(const SkBitmap* bitmapResource) { - decrementRefcountLocked((void*) bitmapResource); -} - -void ResourceCache::decrementRefcountLocked(const SkPath* pathResource) { - decrementRefcountLocked((void*) pathResource); + BitmapKey bitmapKey(bitmapResource); + ssize_t index = mBitmapCache.indexOfKey(bitmapKey); + + LOG_ALWAYS_FATAL_IF(index == NAME_NOT_FOUND, + "Decrementing the reference of an untracked Bitmap"); + + const BitmapKey& cacheEntry = mBitmapCache.keyAt(index); + if (cacheEntry.mRefCount == 1) { + // delete the bitmap and remove it from the cache + delete mBitmapCache.valueAt(index); + mBitmapCache.removeItemsAt(index); + } else { + cacheEntry.mRefCount--; + } } void ResourceCache::decrementRefcountLocked(const Res_png_9patch* patchResource) { decrementRefcountLocked((void*) patchResource); } -void ResourceCache::destructor(SkPath* resource) { - Mutex::Autolock _l(mLock); - destructorLocked(resource); -} - -void ResourceCache::destructorLocked(SkPath* resource) { - ssize_t index = mCache->indexOfKey(resource); - ResourceReference* ref = index >= 0 ? mCache->valueAt(index) : NULL; - if (ref == NULL) { - // If we're not tracking this resource, just delete it - if (Caches::hasInstance()) { - Caches::getInstance().pathCache.removeDeferred(resource); - } else { - delete resource; - } - return; - } - ref->destroyed = true; - if (ref->refCount == 0) { - deleteResourceReferenceLocked(resource, ref); - } -} - -void ResourceCache::destructor(const SkBitmap* resource) { - Mutex::Autolock _l(mLock); - destructorLocked(resource); -} - -void ResourceCache::destructorLocked(const SkBitmap* resource) { - ssize_t index = mCache->indexOfKey(resource); - ResourceReference* ref = index >= 0 ? mCache->valueAt(index) : NULL; - if (ref == NULL) { - // If we're not tracking this resource, just delete it - if (Caches::hasInstance()) { - Caches::getInstance().textureCache.releaseTexture(resource); - } - delete resource; - return; - } - ref->destroyed = true; - if (ref->refCount == 0) { - deleteResourceReferenceLocked(resource, ref); - } -} - void ResourceCache::destructor(Res_png_9patch* resource) { Mutex::Autolock _l(mLock); destructorLocked(resource); @@ -195,8 +148,8 @@ void ResourceCache::destructor(Res_png_9patch* resource) { void ResourceCache::destructorLocked(Res_png_9patch* resource) { ssize_t index = mCache->indexOfKey(resource); - ResourceReference* ref = index >= 0 ? mCache->valueAt(index) : NULL; - if (ref == NULL) { + ResourceReference* ref = index >= 0 ? mCache->valueAt(index) : nullptr; + if (ref == nullptr) { // If we're not tracking this resource, just delete it if (Caches::hasInstance()) { Caches::getInstance().patchCache.removeDeferred(resource); @@ -214,73 +167,12 @@ void ResourceCache::destructorLocked(Res_png_9patch* resource) { } /** - * Return value indicates whether resource was actually recycled, which happens when RefCnt - * reaches 0. - */ -bool ResourceCache::recycle(SkBitmap* resource) { - Mutex::Autolock _l(mLock); - return recycleLocked(resource); -} - -/** - * Return value indicates whether resource was actually recycled, which happens when RefCnt - * reaches 0. - */ -bool ResourceCache::recycleLocked(SkBitmap* resource) { - ssize_t index = mCache->indexOfKey(resource); - if (index < 0) { - if (Caches::hasInstance()) { - Caches::getInstance().textureCache.releaseTexture(resource); - } - // not tracking this resource; just recycle the pixel data - resource->setPixels(NULL, NULL); - return true; - } - ResourceReference* ref = mCache->valueAt(index); - if (ref == NULL) { - // Should not get here - shouldn't get a call to recycle if we're not yet tracking it - return true; - } - ref->recycled = true; - if (ref->refCount == 0) { - deleteResourceReferenceLocked(resource, ref); - return true; - } - // Still referring to resource, don't recycle yet - return false; -} - -/** * This method should only be called while the mLock mutex is held (that mutex is grabbed * by the various destructor() and recycle() methods which call this method). */ void ResourceCache::deleteResourceReferenceLocked(const void* resource, ResourceReference* ref) { - if (ref->recycled && ref->resourceType == kBitmap) { - SkBitmap* bitmap = (SkBitmap*) resource; - if (Caches::hasInstance()) { - Caches::getInstance().textureCache.releaseTexture(bitmap); - } - bitmap->setPixels(NULL, NULL); - } if (ref->destroyed) { switch (ref->resourceType) { - case kBitmap: { - SkBitmap* bitmap = (SkBitmap*) resource; - if (Caches::hasInstance()) { - Caches::getInstance().textureCache.releaseTexture(bitmap); - } - delete bitmap; - } - break; - case kPath: { - SkPath* path = (SkPath*) resource; - if (Caches::hasInstance()) { - Caches::getInstance().pathCache.removeDeferred(path); - } else { - delete path; - } - } - break; case kNinePatch: { if (Caches::hasInstance()) { Caches::getInstance().patchCache.removeDeferred((Res_png_9patch*) resource); @@ -298,5 +190,38 @@ void ResourceCache::deleteResourceReferenceLocked(const void* resource, Resource delete ref; } +/////////////////////////////////////////////////////////////////////////////// +// Bitmap Key +/////////////////////////////////////////////////////////////////////////////// + +void BitmapKey::operator=(const BitmapKey& other) { + this->mRefCount = other.mRefCount; + this->mBitmapDimensions = other.mBitmapDimensions; + this->mPixelRefOrigin = other.mPixelRefOrigin; + this->mPixelRefStableID = other.mPixelRefStableID; +} + +bool BitmapKey::operator==(const BitmapKey& other) const { + return mPixelRefStableID == other.mPixelRefStableID && + mPixelRefOrigin == other.mPixelRefOrigin && + mBitmapDimensions == other.mBitmapDimensions; +} + +bool BitmapKey::operator<(const BitmapKey& other) const { + if (mPixelRefStableID != other.mPixelRefStableID) { + return mPixelRefStableID < other.mPixelRefStableID; + } + if (mPixelRefOrigin.x() != other.mPixelRefOrigin.x()) { + return mPixelRefOrigin.x() < other.mPixelRefOrigin.x(); + } + if (mPixelRefOrigin.y() != other.mPixelRefOrigin.y()) { + return mPixelRefOrigin.y() < other.mPixelRefOrigin.y(); + } + if (mBitmapDimensions.width() != other.mBitmapDimensions.width()) { + return mBitmapDimensions.width() < other.mBitmapDimensions.width(); + } + return mBitmapDimensions.height() < other.mBitmapDimensions.height(); +} + }; // namespace uirenderer }; // namespace android diff --git a/libs/hwui/ResourceCache.h b/libs/hwui/ResourceCache.h index f12f2a4..fae55d1 100644 --- a/libs/hwui/ResourceCache.h +++ b/libs/hwui/ResourceCache.h @@ -20,39 +20,68 @@ #include <cutils/compiler.h> #include <SkBitmap.h> +#include <SkPixelRef.h> #include <utils/KeyedVector.h> #include <utils/Singleton.h> #include <androidfw/ResourceTypes.h> -#include "Layer.h" - namespace android { namespace uirenderer { +class Layer; + /** * Type of Resource being cached */ enum ResourceType { - kBitmap, kNinePatch, - kPath }; class ResourceReference { public: ResourceReference(ResourceType type) { - refCount = 0; recycled = false; destroyed = false; resourceType = type; + refCount = 0; destroyed = false; resourceType = type; } int refCount; - bool recycled; bool destroyed; ResourceType resourceType; }; +class BitmapKey { +public: + BitmapKey(const SkBitmap* bitmap) + : mRefCount(1) + , mBitmapDimensions(bitmap->dimensions()) + , mPixelRefOrigin(bitmap->pixelRefOrigin()) + , mPixelRefStableID(bitmap->pixelRef()->getStableID()) { } + + void operator=(const BitmapKey& other); + bool operator==(const BitmapKey& other) const; + bool operator<(const BitmapKey& other) const; + +private: + // This constructor is only used by the KeyedVector implementation + BitmapKey() + : mRefCount(-1) + , mBitmapDimensions(SkISize::Make(0,0)) + , mPixelRefOrigin(SkIPoint::Make(0,0)) + , mPixelRefStableID(0) { } + + // reference count of all HWUI object using this bitmap + mutable int mRefCount; + + SkISize mBitmapDimensions; + SkIPoint mPixelRefOrigin; + uint32_t mPixelRefStableID; + + friend class ResourceCache; + friend struct android::key_value_pair_t<BitmapKey, SkBitmap*>; +}; + class ANDROID_API ResourceCache: public Singleton<ResourceCache> { ResourceCache(); ~ResourceCache(); @@ -68,33 +97,24 @@ public: void lock(); void unlock(); - void incrementRefcount(const SkPath* resource); - void incrementRefcount(const SkBitmap* resource); - void incrementRefcount(const Res_png_9patch* resource); + /** + * The cache stores a copy of the provided resource or refs an existing resource + * if the bitmap has previously been inserted and returns the cached copy. + */ + const SkBitmap* insert(const SkBitmap* resource); - void incrementRefcountLocked(const SkPath* resource); - void incrementRefcountLocked(const SkBitmap* resource); - void incrementRefcountLocked(const Res_png_9patch* resource); + void incrementRefcount(const Res_png_9patch* resource); void decrementRefcount(const SkBitmap* resource); - void decrementRefcount(const SkPath* resource); void decrementRefcount(const Res_png_9patch* resource); void decrementRefcountLocked(const SkBitmap* resource); - void decrementRefcountLocked(const SkPath* resource); void decrementRefcountLocked(const Res_png_9patch* resource); - void destructor(SkPath* resource); - void destructor(const SkBitmap* resource); void destructor(Res_png_9patch* resource); - void destructorLocked(SkPath* resource); - void destructorLocked(const SkBitmap* resource); void destructorLocked(Res_png_9patch* resource); - bool recycle(SkBitmap* resource); - bool recycleLocked(SkBitmap* resource); - private: void deleteResourceReferenceLocked(const void* resource, ResourceReference* ref); @@ -114,6 +134,7 @@ private: mutable Mutex mLock; KeyedVector<const void*, ResourceReference*>* mCache; + KeyedVector<BitmapKey, SkBitmap*> mBitmapCache; }; }; // namespace uirenderer diff --git a/libs/hwui/RevealClip.h b/libs/hwui/RevealClip.h index a9600f1..0084a8e 100644 --- a/libs/hwui/RevealClip.h +++ b/libs/hwui/RevealClip.h @@ -54,7 +54,7 @@ public: float getRadius() const { return mRadius; } const SkPath* getPath() const { - if (!mShouldClip) return NULL; + if (!mShouldClip) return nullptr; return &mPath; } diff --git a/libs/hwui/ShadowTessellator.cpp b/libs/hwui/ShadowTessellator.cpp index c1ffa0a..30d3f41 100644 --- a/libs/hwui/ShadowTessellator.cpp +++ b/libs/hwui/ShadowTessellator.cpp @@ -87,9 +87,8 @@ void ShadowTessellator::tessellateSpotShadow(bool isCasterOpaque, reverseReceiverTransform.loadInverse(receiverTransform); reverseReceiverTransform.mapPoint3d(adjustedLightCenter); - const int lightVertexCount = 8; - if (CC_UNLIKELY(caches.propertyLightDiameter > 0)) { - lightRadius = caches.propertyLightDiameter; + if (CC_UNLIKELY(caches.propertyLightRadius > 0)) { + lightRadius = caches.propertyLightRadius; } // Now light and caster are both in local space, we will check whether @@ -114,33 +113,6 @@ void ShadowTessellator::tessellateSpotShadow(bool isCasterOpaque, #endif } -void ShadowTessellator::generateShadowIndices(uint16_t* shadowIndices) { - int currentIndex = 0; - const int rays = SHADOW_RAY_COUNT; - // For the penumbra area. - for (int layer = 0; layer < 2; layer ++) { - int baseIndex = layer * rays; - for (int i = 0; i < rays; i++) { - shadowIndices[currentIndex++] = i + baseIndex; - shadowIndices[currentIndex++] = rays + i + baseIndex; - } - // To close the loop, back to the ray 0. - shadowIndices[currentIndex++] = 0 + baseIndex; - // Note this is the same as the first index of next layer loop. - shadowIndices[currentIndex++] = rays + baseIndex; - } - -#if DEBUG_SHADOW - if (currentIndex != MAX_SHADOW_INDEX_COUNT) { - ALOGW("vertex index count is wrong. current %d, expected %d", - currentIndex, MAX_SHADOW_INDEX_COUNT); - } - for (int i = 0; i < MAX_SHADOW_INDEX_COUNT; i++) { - ALOGD("vertex index is (%d, %d)", i, shadowIndices[i]); - } -#endif -} - /** * Calculate the centroid of a 2d polygon. * @@ -194,7 +166,7 @@ Vector2 ShadowTessellator::calculateNormal(const Vector2& p1, const Vector2& p2) * @param len the number of points of the polygon */ bool ShadowTessellator::isClockwise(const Vector2* polygon, int len) { - if (len < 2 || polygon == NULL) { + if (len < 2 || polygon == nullptr) { return true; } double sum = 0; @@ -225,6 +197,7 @@ bool ShadowTessellator::isClockwisePath(const SkPath& path) { case SkPath::kLine_Verb: arrayForDirection.add((Vector2){pts[1].x(), pts[1].y()}); break; + case SkPath::kConic_Verb: case SkPath::kQuad_Verb: arrayForDirection.add((Vector2){pts[1].x(), pts[1].y()}); arrayForDirection.add((Vector2){pts[2].x(), pts[2].y()}); diff --git a/libs/hwui/ShadowTessellator.h b/libs/hwui/ShadowTessellator.h index 8f19b5c..c04d8ef 100644 --- a/libs/hwui/ShadowTessellator.h +++ b/libs/hwui/ShadowTessellator.h @@ -18,14 +18,16 @@ #ifndef ANDROID_HWUI_SHADOW_TESSELLATOR_H #define ANDROID_HWUI_SHADOW_TESSELLATOR_H +#include <SkPath.h> + #include "Debug.h" #include "Matrix.h" -#include "OpenGLRenderer.h" -#include "VertexBuffer.h" namespace android { namespace uirenderer { +class VertexBuffer; + // All SHADOW_* are used to define all the geometry property of shadows. // Use a simplified example to illustrate the geometry setup here. // Assuming we use 6 rays and only 1 layer, Then we will have 2 hexagons, which @@ -76,8 +78,6 @@ public: const mat4& receiverTransform, const Vector3& lightCenter, int lightRadius, const Rect& casterBounds, const Rect& localClip, VertexBuffer& shadowVertexBuffer); - static void generateShadowIndices(uint16_t* shadowIndices); - static Vector2 centroid2d(const Vector2* poly, int polyLength); static bool isClockwise(const Vector2* polygon, int len); diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp new file mode 100644 index 0000000..a323065 --- /dev/null +++ b/libs/hwui/SkiaCanvas.cpp @@ -0,0 +1,703 @@ +/* + * 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. + */ + +#include "Canvas.h" + +#include <SkCanvas.h> +#include <SkClipStack.h> +#include <SkDevice.h> +#include <SkDeque.h> +#include <SkDrawFilter.h> +#include <SkGraphics.h> +#include <SkShader.h> +#include <SkTArray.h> +#include <SkTemplates.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 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 setBitmap(const SkBitmap& bitmap) override; + + virtual bool isOpaque() override; + virtual int width() override; + virtual int height() override; + + virtual int getSaveCount() const override; + virtual int save(SkCanvas::SaveFlags 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, SkCanvas::SaveFlags flags) override; + virtual int saveLayerAlpha(float left, float top, float right, float bottom, + int alpha, SkCanvas::SaveFlags 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 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 drawText(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 drawPosText(const uint16_t* text, const float* positions, int count, + int posCount, const SkPaint& paint) override; + virtual void drawTextOnPath(const uint16_t* glyphs, int count, const SkPath& path, + float hOffset, float vOffset, const SkPaint& paint) override; + + virtual bool drawTextAbsolutePos() const override { return true; } + +private: + struct SaveRec { + int saveCount; + SkCanvas::SaveFlags saveFlags; + }; + + void recordPartialSave(SkCanvas::SaveFlags 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); + void drawTextDecorations(float x, float y, float length, const SkPaint& paint); + + SkAutoTUnref<SkCanvas> mCanvas; + SkAutoTDelete<SkDeque> mSaveStack; // lazily allocated, tracks partial saves. +}; + +Canvas* Canvas::create_canvas(const SkBitmap& bitmap) { + return new SkiaCanvas(bitmap); +} + +Canvas* Canvas::create_canvas(SkCanvas* skiaCanvas) { + return new SkiaCanvas(skiaCanvas); +} + +SkiaCanvas::SkiaCanvas(const SkBitmap& bitmap) { + mCanvas.reset(new SkCanvas(bitmap)); +} + +// ---------------------------------------------------------------------------- +// Canvas state operations: Replace Bitmap +// ---------------------------------------------------------------------------- + +class ClipCopier : public SkCanvas::ClipVisitor { +public: + ClipCopier(SkCanvas* dstCanvas) : m_dstCanvas(dstCanvas) {} + + virtual void clipRect(const SkRect& rect, SkRegion::Op op, bool antialias) { + m_dstCanvas->clipRect(rect, op, antialias); + } + virtual void clipRRect(const SkRRect& rrect, SkRegion::Op op, bool antialias) { + m_dstCanvas->clipRRect(rrect, op, antialias); + } + virtual void clipPath(const SkPath& path, SkRegion::Op op, bool antialias) { + m_dstCanvas->clipPath(path, op, antialias); + } + +private: + SkCanvas* m_dstCanvas; +}; + +void SkiaCanvas::setBitmap(const SkBitmap& bitmap) { + SkCanvas* newCanvas = new SkCanvas(bitmap); + SkASSERT(newCanvas); + + if (!bitmap.isNull()) { + // Copy the canvas matrix & clip state. + newCanvas->setMatrix(mCanvas->getTotalMatrix()); + if (NULL != mCanvas->getDevice() && NULL != newCanvas->getDevice()) { + ClipCopier copier(newCanvas); + mCanvas->replayClips(&copier); + } + } + + // unrefs the existing canvas + mCanvas.reset(newCanvas); + + // clean up the old save stack + mSaveStack.reset(NULL); +} + +// ---------------------------------------------------------------------------- +// Canvas state operations +// ---------------------------------------------------------------------------- + +bool SkiaCanvas::isOpaque() { + return mCanvas->getDevice()->accessBitmap(false).isOpaque(); +} + +int SkiaCanvas::width() { + return mCanvas->getBaseLayerSize().width(); +} + +int SkiaCanvas::height() { + return mCanvas->getBaseLayerSize().height(); +} + +// ---------------------------------------------------------------------------- +// Canvas state operations: Save (layer) +// ---------------------------------------------------------------------------- + +int SkiaCanvas::getSaveCount() const { + return mCanvas->getSaveCount(); +} + +int SkiaCanvas::save(SkCanvas::SaveFlags flags) { + int count = mCanvas->save(); + recordPartialSave(flags); + return count; +} + +void SkiaCanvas::restore() { + const SaveRec* rec = (NULL == mSaveStack.get()) + ? NULL + : static_cast<SaveRec*>(mSaveStack->back()); + int currentSaveCount = mCanvas->getSaveCount() - 1; + SkASSERT(NULL == rec || currentSaveCount >= rec->saveCount); + + if (NULL == rec || rec->saveCount != currentSaveCount) { + // Fast path - no record for this frame. + mCanvas->restore(); + return; + } + + bool preserveMatrix = !(rec->saveFlags & SkCanvas::kMatrix_SaveFlag); + bool preserveClip = !(rec->saveFlags & SkCanvas::kClip_SaveFlag); + + SkMatrix savedMatrix; + if (preserveMatrix) { + savedMatrix = mCanvas->getTotalMatrix(); + } + + SkTArray<SkClipStack::Element> savedClips; + if (preserveClip) { + saveClipsForFrame(savedClips, currentSaveCount); + } + + mCanvas->restore(); + + if (preserveMatrix) { + mCanvas->setMatrix(savedMatrix); + } + + if (preserveClip && !savedClips.empty()) { + applyClips(savedClips); + } + + mSaveStack->pop_back(); +} + +void SkiaCanvas::restoreToCount(int restoreCount) { + while (mCanvas->getSaveCount() > restoreCount) { + this->restore(); + } +} + +int SkiaCanvas::saveLayer(float left, float top, float right, float bottom, + const SkPaint* paint, SkCanvas::SaveFlags flags) { + SkRect bounds = SkRect::MakeLTRB(left, top, right, bottom); + int count = mCanvas->saveLayer(&bounds, paint, flags | SkCanvas::kMatrixClip_SaveFlag); + recordPartialSave(flags); + return count; +} + +int SkiaCanvas::saveLayerAlpha(float left, float top, float right, float bottom, + int alpha, SkCanvas::SaveFlags flags) { + SkRect bounds = SkRect::MakeLTRB(left, top, right, bottom); + int count = mCanvas->saveLayerAlpha(&bounds, alpha, flags | SkCanvas::kMatrixClip_SaveFlag); + recordPartialSave(flags); + return count; +} + +// ---------------------------------------------------------------------------- +// functions to emulate legacy SaveFlags (i.e. independent matrix/clip flags) +// ---------------------------------------------------------------------------- + +void SkiaCanvas::recordPartialSave(SkCanvas::SaveFlags flags) { + // A partial save is a save operation which doesn't capture the full canvas state. + // (either kMatrix_SaveFlags or kClip_SaveFlag is missing). + + // Mask-out non canvas state bits. + flags = static_cast<SkCanvas::SaveFlags>(flags & SkCanvas::kMatrixClip_SaveFlag); + + if (SkCanvas::kMatrixClip_SaveFlag == flags) { + // not a partial save. + return; + } + + if (NULL == mSaveStack.get()) { + mSaveStack.reset(SkNEW_ARGS(SkDeque, (sizeof(struct SaveRec), 8))); + } + + SaveRec* rec = static_cast<SaveRec*>(mSaveStack->push_back()); + // Store the save counter in the SkClipStack domain. + // (0-based, equal to the number of save ops on the stack). + rec->saveCount = mCanvas->getSaveCount() - 1; + rec->saveFlags = flags; +} + +void SkiaCanvas::saveClipsForFrame(SkTArray<SkClipStack::Element>& clips, int frameSaveCount) { + SkClipStack::Iter clipIterator(*mCanvas->getClipStack(), + SkClipStack::Iter::kTop_IterStart); + while (const SkClipStack::Element* elem = clipIterator.next()) { + if (elem->getSaveCount() < frameSaveCount) { + // done with the current frame. + break; + } + SkASSERT(elem->getSaveCount() == frameSaveCount); + clips.push_back(*elem); + } +} + +void SkiaCanvas::applyClips(const SkTArray<SkClipStack::Element>& clips) { + ClipCopier clipCopier(mCanvas); + + // The clip stack stores clips in device space. + SkMatrix origMatrix = mCanvas->getTotalMatrix(); + mCanvas->resetMatrix(); + + // We pushed the clips in reverse order. + for (int i = clips.count() - 1; i >= 0; --i) { + clips[i].replay(&clipCopier); + } + + mCanvas->setMatrix(origMatrix); +} + +// ---------------------------------------------------------------------------- +// Canvas state operations: Matrix +// ---------------------------------------------------------------------------- + +void SkiaCanvas::getMatrix(SkMatrix* outMatrix) const { + *outMatrix = mCanvas->getTotalMatrix(); +} + +void SkiaCanvas::setMatrix(const SkMatrix& matrix) { + mCanvas->setMatrix(matrix); +} + +void SkiaCanvas::concat(const SkMatrix& matrix) { + mCanvas->concat(matrix); +} + +void SkiaCanvas::rotate(float degrees) { + mCanvas->rotate(degrees); +} + +void SkiaCanvas::scale(float sx, float sy) { + mCanvas->scale(sx, sy); +} + +void SkiaCanvas::skew(float sx, float sy) { + mCanvas->skew(sx, sy); +} + +void SkiaCanvas::translate(float dx, float dy) { + mCanvas->translate(dx, dy); +} + +// ---------------------------------------------------------------------------- +// Canvas state operations: Clips +// ---------------------------------------------------------------------------- + +// This function is a mirror of SkCanvas::getClipBounds except that it does +// not outset the edge of the clip to account for anti-aliasing. There is +// a skia bug to investigate pushing this logic into back into skia. +// (see https://code.google.com/p/skia/issues/detail?id=1303) +bool SkiaCanvas::getClipBounds(SkRect* outRect) const { + SkIRect ibounds; + if (!mCanvas->getClipDeviceBounds(&ibounds)) { + return false; + } + + SkMatrix inverse; + // if we can't invert the CTM, we can't return local clip bounds + if (!mCanvas->getTotalMatrix().invert(&inverse)) { + if (outRect) { + outRect->setEmpty(); + } + return false; + } + + if (NULL != outRect) { + SkRect r = SkRect::Make(ibounds); + inverse.mapRect(outRect, r); + } + return true; +} + +bool SkiaCanvas::quickRejectRect(float left, float top, float right, float bottom) const { + SkRect bounds = SkRect::MakeLTRB(left, top, right, bottom); + return mCanvas->quickReject(bounds); +} + +bool SkiaCanvas::quickRejectPath(const SkPath& path) const { + return mCanvas->quickReject(path); +} + +bool SkiaCanvas::clipRect(float left, float top, float right, float bottom, SkRegion::Op op) { + SkRect rect = SkRect::MakeLTRB(left, top, right, bottom); + mCanvas->clipRect(rect, op); + return mCanvas->isClipEmpty(); +} + +bool SkiaCanvas::clipPath(const SkPath* path, SkRegion::Op op) { + mCanvas->clipPath(*path, op); + return mCanvas->isClipEmpty(); +} + +bool SkiaCanvas::clipRegion(const SkRegion* region, SkRegion::Op op) { + SkPath rgnPath; + if (region->getBoundaryPath(&rgnPath)) { + // The region is specified in device space. + SkMatrix savedMatrix = mCanvas->getTotalMatrix(); + mCanvas->resetMatrix(); + mCanvas->clipPath(rgnPath, op); + mCanvas->setMatrix(savedMatrix); + } else { + mCanvas->clipRect(SkRect::MakeEmpty(), op); + } + return mCanvas->isClipEmpty(); +} + +// ---------------------------------------------------------------------------- +// Canvas state operations: Filters +// ---------------------------------------------------------------------------- + +SkDrawFilter* SkiaCanvas::getDrawFilter() { + return mCanvas->getDrawFilter(); +} + +void SkiaCanvas::setDrawFilter(SkDrawFilter* drawFilter) { + mCanvas->setDrawFilter(drawFilter); +} + +// ---------------------------------------------------------------------------- +// Canvas draw operations +// ---------------------------------------------------------------------------- + +void SkiaCanvas::drawColor(int color, SkXfermode::Mode mode) { + mCanvas->drawColor(color, mode); +} + +void SkiaCanvas::drawPaint(const SkPaint& paint) { + mCanvas->drawPaint(paint); +} + +// ---------------------------------------------------------------------------- +// Canvas draw operations: Geometry +// ---------------------------------------------------------------------------- + +void SkiaCanvas::drawPoints(const float* points, int count, const SkPaint& paint, + SkCanvas::PointMode mode) { + // convert the floats into SkPoints + count >>= 1; // now it is the number of points + SkAutoSTMalloc<32, SkPoint> storage(count); + SkPoint* pts = storage.get(); + for (int i = 0; i < count; i++) { + pts[i].set(points[0], points[1]); + points += 2; + } + mCanvas->drawPoints(mode, count, pts, paint); +} + + +void SkiaCanvas::drawPoint(float x, float y, const SkPaint& paint) { + mCanvas->drawPoint(x, y, paint); +} + +void SkiaCanvas::drawPoints(const float* points, int count, const SkPaint& paint) { + this->drawPoints(points, count, paint, SkCanvas::kPoints_PointMode); +} + +void SkiaCanvas::drawLine(float startX, float startY, float stopX, float stopY, + const SkPaint& paint) { + mCanvas->drawLine(startX, startY, stopX, stopY, paint); +} + +void SkiaCanvas::drawLines(const float* points, int count, const SkPaint& paint) { + this->drawPoints(points, count, paint, SkCanvas::kLines_PointMode); +} + +void SkiaCanvas::drawRect(float left, float top, float right, float bottom, + const SkPaint& paint) { + mCanvas->drawRectCoords(left, top, right, bottom, paint); + +} + +void SkiaCanvas::drawRoundRect(float left, float top, float right, float bottom, + float rx, float ry, const SkPaint& paint) { + 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) { + mCanvas->drawCircle(x, y, radius, paint); +} + +void SkiaCanvas::drawOval(float left, float top, float right, float bottom, const SkPaint& paint) { + 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) { + 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); +} + +void SkiaCanvas::drawVertices(SkCanvas::VertexMode vertexMode, int vertexCount, + const float* verts, const float* texs, const int* colors, + const uint16_t* indices, int indexCount, const SkPaint& paint) { +#ifndef SK_SCALAR_IS_FLOAT + SkDEBUGFAIL("SkScalar must be a float for these conversions to be valid"); +#endif + const int ptCount = vertexCount >> 1; + mCanvas->drawVertices(vertexMode, ptCount, (SkPoint*)verts, (SkPoint*)texs, + (SkColor*)colors, NULL, 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(const SkBitmap& bitmap, const SkMatrix& matrix, const SkPaint* paint) { + SkAutoCanvasRestore acr(mCanvas, true); + mCanvas->concat(matrix); + mCanvas->drawBitmap(bitmap, 0, 0, paint); +} + +void SkiaCanvas::drawBitmap(const SkBitmap& bitmap, float srcLeft, float srcTop, + float srcRight, float srcBottom, float dstLeft, float dstTop, + float dstRight, float dstBottom, const SkPaint* paint) { + SkRect srcRect = SkRect::MakeLTRB(srcLeft, srcTop, srcRight, srcBottom); + SkRect dstRect = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom); + mCanvas->drawBitmapRectToRect(bitmap, &srcRect, dstRect, paint); +} + +void SkiaCanvas::drawBitmapMesh(const SkBitmap& bitmap, int meshWidth, int meshHeight, + const float* vertices, const int* colors, const SkPaint* paint) { + + const int ptCount = (meshWidth + 1) * (meshHeight + 1); + const int indexCount = meshWidth * meshHeight * 6; + + /* Our temp storage holds 2 or 3 arrays. + texture points [ptCount * sizeof(SkPoint)] + optionally vertex points [ptCount * sizeof(SkPoint)] if we need a + copy to convert from float to fixed + indices [ptCount * sizeof(uint16_t)] + */ + ssize_t storageSize = ptCount * sizeof(SkPoint); // texs[] + storageSize += indexCount * sizeof(uint16_t); // indices[] + + +#ifndef SK_SCALAR_IS_FLOAT + SkDEBUGFAIL("SkScalar must be a float for these conversions to be valid"); +#endif + SkAutoMalloc storage(storageSize); + SkPoint* texs = (SkPoint*)storage.get(); + uint16_t* indices = (uint16_t*)(texs + ptCount); + + // cons up texture coordinates and indices + { + const SkScalar w = SkIntToScalar(bitmap.width()); + const SkScalar h = SkIntToScalar(bitmap.height()); + const SkScalar dx = w / meshWidth; + const SkScalar dy = h / meshHeight; + + SkPoint* texsPtr = texs; + SkScalar y = 0; + for (int i = 0; i <= meshHeight; i++) { + if (i == meshHeight) { + y = h; // to ensure numerically we hit h exactly + } + SkScalar x = 0; + for (int j = 0; j < meshWidth; j++) { + texsPtr->set(x, y); + texsPtr += 1; + x += dx; + } + texsPtr->set(w, y); + texsPtr += 1; + y += dy; + } + SkASSERT(texsPtr - texs == ptCount); + } + + // cons up indices + { + uint16_t* indexPtr = indices; + int index = 0; + for (int i = 0; i < meshHeight; i++) { + for (int j = 0; j < meshWidth; j++) { + // lower-left triangle + *indexPtr++ = index; + *indexPtr++ = index + meshWidth + 1; + *indexPtr++ = index + meshWidth + 2; + // upper-right triangle + *indexPtr++ = index; + *indexPtr++ = index + meshWidth + 2; + *indexPtr++ = index + 1; + // bump to the next cell + index += 1; + } + // bump to the next row + index += 1; + } + SkASSERT(indexPtr - indices == indexCount); + SkASSERT((char*)indexPtr - (char*)storage.get() == storageSize); + } + + // double-check that we have legal indices +#ifdef SK_DEBUG + { + for (int i = 0; i < indexCount; i++) { + SkASSERT((unsigned)indices[i] < (unsigned)ptCount); + } + } +#endif + + // cons-up a shader for the bitmap + SkPaint tmpPaint; + if (paint) { + tmpPaint = *paint; + } + SkShader* shader = SkShader::CreateBitmapShader(bitmap, + SkShader::kClamp_TileMode, + SkShader::kClamp_TileMode); + SkSafeUnref(tmpPaint.setShader(shader)); + + mCanvas->drawVertices(SkCanvas::kTriangles_VertexMode, ptCount, (SkPoint*)vertices, + texs, (const SkColor*)colors, NULL, indices, + indexCount, tmpPaint); +} + +// ---------------------------------------------------------------------------- +// Canvas draw operations: Text +// ---------------------------------------------------------------------------- + +void SkiaCanvas::drawText(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) { + // 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); + + SK_COMPILE_ASSERT(sizeof(SkPoint) == sizeof(float)*2, SkPoint_is_no_longer_2_floats); + mCanvas->drawPosText(text, count << 1, reinterpret_cast<const SkPoint*>(positions), paintCopy); +} + +void SkiaCanvas::drawPosText(const uint16_t* text, const float* positions, int count, int posCount, + const SkPaint& paint) { + SkPoint* posPtr = posCount > 0 ? new SkPoint[posCount] : NULL; + int indx; + for (indx = 0; indx < posCount; indx++) { + posPtr[indx].fX = positions[indx << 1]; + posPtr[indx].fY = positions[(indx << 1) + 1]; + } + + SkPaint paintCopy(paint); + paintCopy.setTextEncoding(SkPaint::kUTF16_TextEncoding); + mCanvas->drawPosText(text, count, posPtr, paintCopy); + + delete[] posPtr; +} + +void SkiaCanvas::drawTextOnPath(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); +} + +} // namespace android diff --git a/libs/hwui/SkiaCanvasProxy.cpp b/libs/hwui/SkiaCanvasProxy.cpp new file mode 100644 index 0000000..8a6c8c5 --- /dev/null +++ b/libs/hwui/SkiaCanvasProxy.cpp @@ -0,0 +1,361 @@ +/* + * 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 "SkiaCanvasProxy.h" + +#include <cutils/log.h> +#include <SkPatchUtils.h> + +namespace android { +namespace uirenderer { + +SkiaCanvasProxy::SkiaCanvasProxy(Canvas* canvas, bool filterHwuiCalls) + : INHERITED(canvas->width(), canvas->height()) + , mCanvas(canvas) + , mFilterHwuiCalls(filterHwuiCalls) {} + +void SkiaCanvasProxy::onDrawPaint(const SkPaint& paint) { + mCanvas->drawPaint(paint); +} + +void SkiaCanvasProxy::onDrawPoints(PointMode pointMode, size_t count, const SkPoint pts[], + const SkPaint& paint) { + if (!pts || count == 0) { + return; + } + + // convert the SkPoints into floats + SK_COMPILE_ASSERT(sizeof(SkPoint) == sizeof(float)*2, SkPoint_is_no_longer_2_floats); + const size_t floatCount = count << 1; + const float* floatArray = &pts[0].fX; + + switch (pointMode) { + case kPoints_PointMode: { + mCanvas->drawPoints(floatArray, floatCount, paint); + break; + } + case kLines_PointMode: { + mCanvas->drawLines(floatArray, floatCount, paint); + break; + } + case kPolygon_PointMode: { + SkPaint strokedPaint(paint); + strokedPaint.setStyle(SkPaint::kStroke_Style); + + SkPath path; + for (size_t i = 0; i < count - 1; i++) { + path.moveTo(pts[i]); + path.lineTo(pts[i+1]); + this->drawPath(path, strokedPaint); + path.rewind(); + } + break; + } + default: + LOG_ALWAYS_FATAL("Unknown point type"); + } +} + +void SkiaCanvasProxy::onDrawOval(const SkRect& rect, const SkPaint& paint) { + mCanvas->drawOval(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, paint); +} + +void SkiaCanvasProxy::onDrawRect(const SkRect& rect, const SkPaint& paint) { + mCanvas->drawRect(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, paint); +} + +void SkiaCanvasProxy::onDrawRRect(const SkRRect& roundRect, const SkPaint& paint) { + if (!roundRect.isComplex()) { + const SkRect& rect = roundRect.rect(); + SkVector radii = roundRect.getSimpleRadii(); + mCanvas->drawRoundRect(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, + radii.fX, radii.fY, paint); + } else { + SkPath path; + path.addRRect(roundRect); + mCanvas->drawPath(path, paint); + } +} + +void SkiaCanvasProxy::onDrawPath(const SkPath& path, const SkPaint& paint) { + mCanvas->drawPath(path, paint); +} + +void SkiaCanvasProxy::onDrawBitmap(const SkBitmap& bitmap, SkScalar left, SkScalar top, + const SkPaint* paint) { + mCanvas->drawBitmap(bitmap, left, top, paint); +} + +void SkiaCanvasProxy::onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* srcPtr, + const SkRect& dst, const SkPaint* paint, DrawBitmapRectFlags) { + SkRect src = (srcPtr) ? *srcPtr : SkRect::MakeWH(bitmap.width(), bitmap.height()); + mCanvas->drawBitmap(bitmap, src.fLeft, src.fTop, src.fRight, src.fBottom, + dst.fLeft, dst.fTop, dst.fRight, dst.fBottom, paint); +} + +void SkiaCanvasProxy::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, + const SkRect& dst, const SkPaint*) { + //TODO make nine-patch drawing a method on Canvas.h + SkDEBUGFAIL("SkiaCanvasProxy::onDrawBitmapNine is not yet supported"); +} + +void SkiaCanvasProxy::onDrawSprite(const SkBitmap& bitmap, int left, int top, + const SkPaint* paint) { + mCanvas->save(SkCanvas::kMatrixClip_SaveFlag); + mCanvas->setMatrix(SkMatrix::I()); + mCanvas->drawBitmap(bitmap, left, top, paint); + mCanvas->restore(); +} + +void SkiaCanvasProxy::onDrawVertices(VertexMode mode, int vertexCount, const SkPoint vertices[], + const SkPoint texs[], const SkColor colors[], SkXfermode*, const uint16_t indices[], + int indexCount, const SkPaint& paint) { + if (mFilterHwuiCalls) { + return; + } + // convert the SkPoints into floats + SK_COMPILE_ASSERT(sizeof(SkPoint) == sizeof(float)*2, SkPoint_is_no_longer_2_floats); + const int floatCount = vertexCount << 1; + const float* vArray = &vertices[0].fX; + const float* tArray = (texs) ? &texs[0].fX : NULL; + const int* cArray = (colors) ? (int*)colors : NULL; + mCanvas->drawVertices(mode, floatCount, vArray, tArray, cArray, indices, indexCount, paint); +} + +SkSurface* SkiaCanvasProxy::onNewSurface(const SkImageInfo&, const SkSurfaceProps&) { + SkDEBUGFAIL("SkiaCanvasProxy::onNewSurface is not supported"); + return NULL; +} + +void SkiaCanvasProxy::willSave() { + mCanvas->save(SkCanvas::kMatrixClip_SaveFlag); +} + +SkCanvas::SaveLayerStrategy SkiaCanvasProxy::willSaveLayer(const SkRect* rectPtr, + const SkPaint* paint, SaveFlags flags) { + SkRect rect; + if (rectPtr) { + rect = *rectPtr; + } else if(!mCanvas->getClipBounds(&rect)) { + rect = SkRect::MakeEmpty(); + } + mCanvas->saveLayer(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, paint, flags); + return SkCanvas::kNoLayer_SaveLayerStrategy; +} + +void SkiaCanvasProxy::willRestore() { + mCanvas->restore(); +} + +void SkiaCanvasProxy::didConcat(const SkMatrix& matrix) { + mCanvas->concat(matrix); +} + +void SkiaCanvasProxy::didSetMatrix(const SkMatrix& matrix) { + mCanvas->setMatrix(matrix); +} + +void SkiaCanvasProxy::onDrawDRRect(const SkRRect& outer, const SkRRect& inner, + const SkPaint& paint) { + SkPath path; + path.addRRect(outer); + path.addRRect(inner); + path.setFillType(SkPath::kEvenOdd_FillType); + this->drawPath(path, paint); +} + +/** + * Utility class that converts the incoming text & paint from the given encoding + * into glyphIDs. + */ +class GlyphIDConverter { +public: + GlyphIDConverter(const void* text, size_t byteLength, const SkPaint& origPaint) { + paint = origPaint; + if (paint.getTextEncoding() == SkPaint::kGlyphID_TextEncoding) { + glyphIDs = (uint16_t*)text; + count = byteLength >> 1; + } else { + storage.reset(byteLength); // ensures space for one glyph per ID given UTF8 encoding. + glyphIDs = storage.get(); + count = paint.textToGlyphs(text, byteLength, storage.get()); + paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); + } + } + + SkPaint paint; + uint16_t* glyphIDs; + int count; +private: + SkAutoSTMalloc<32, uint16_t> storage; +}; + +void SkiaCanvasProxy::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y, + const SkPaint& origPaint) { + // convert to glyphIDs if necessary + GlyphIDConverter glyphs(text, byteLength, origPaint); + + // compute the glyph positions + SkAutoSTMalloc<32, SkPoint> pointStorage(glyphs.count); + SkAutoSTMalloc<32, SkScalar> glyphWidths(glyphs.count); + glyphs.paint.getTextWidths(glyphs.glyphIDs, glyphs.count << 1, glyphWidths.get()); + + // compute conservative bounds + // NOTE: We could call the faster paint.getFontBounds for a less accurate, + // but even more conservative bounds if this is too slow. + SkRect bounds; + glyphs.paint.measureText(glyphs.glyphIDs, glyphs.count << 1, &bounds); + + // adjust for non-left alignment + if (glyphs.paint.getTextAlign() != SkPaint::kLeft_Align) { + SkScalar stop = 0; + for (int i = 0; i < glyphs.count; i++) { + stop += glyphWidths[i]; + } + if (glyphs.paint.getTextAlign() == SkPaint::kCenter_Align) { + stop = SkScalarHalf(stop); + } + if (glyphs.paint.isVerticalText()) { + y -= stop; + } else { + x -= stop; + } + } + + // setup the first glyph position and adjust bounds if needed + int xBaseline = 0; + int yBaseline = 0; + if (mCanvas->drawTextAbsolutePos()) { + bounds.offset(x,y); + xBaseline = x; + yBaseline = y; + } + pointStorage[0].set(xBaseline, yBaseline); + + // setup the remaining glyph positions + if (glyphs.paint.isVerticalText()) { + for (int i = 1; i < glyphs.count; i++) { + pointStorage[i].set(xBaseline, glyphWidths[i-1] + pointStorage[i-1].fY); + } + } else { + for (int i = 1; i < glyphs.count; i++) { + pointStorage[i].set(glyphWidths[i-1] + pointStorage[i-1].fX, yBaseline); + } + } + + SK_COMPILE_ASSERT(sizeof(SkPoint) == sizeof(float)*2, SkPoint_is_no_longer_2_floats); + mCanvas->drawText(glyphs.glyphIDs, &pointStorage[0].fX, glyphs.count, glyphs.paint, + x, y, bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, 0); +} + +void SkiaCanvasProxy::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[], + const SkPaint& origPaint) { + // convert to glyphIDs if necessary + GlyphIDConverter glyphs(text, byteLength, origPaint); + + // convert to relative positions if necessary + int x, y; + const SkPoint* posArray; + SkAutoSTMalloc<32, SkPoint> pointStorage; + if (mCanvas->drawTextAbsolutePos()) { + x = 0; + y = 0; + posArray = pos; + } else { + x = pos[0].fX; + y = pos[0].fY; + posArray = pointStorage.reset(glyphs.count); + for (int i = 0; i < glyphs.count; i++) { + pointStorage[i].fX = pos[i].fX- x; + pointStorage[i].fY = pos[i].fY- y; + } + } + + // compute conservative bounds + // NOTE: We could call the faster paint.getFontBounds for a less accurate, + // but even more conservative bounds if this is too slow. + SkRect bounds; + glyphs.paint.measureText(glyphs.glyphIDs, glyphs.count << 1, &bounds); + bounds.offset(x, y); + + SK_COMPILE_ASSERT(sizeof(SkPoint) == sizeof(float)*2, SkPoint_is_no_longer_2_floats); + mCanvas->drawText(glyphs.glyphIDs, &posArray[0].fX, glyphs.count, glyphs.paint, x, y, + bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, 0); +} + +void SkiaCanvasProxy::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[], + SkScalar constY, const SkPaint& paint) { + const size_t pointCount = byteLength >> 1; + SkAutoSTMalloc<32, SkPoint> storage(pointCount); + SkPoint* pts = storage.get(); + for (size_t i = 0; i < pointCount; i++) { + pts[i].set(xpos[i], constY); + } + this->onDrawPosText(text, byteLength, pts, paint); +} + +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->drawTextOnPath(glyphs.glyphIDs, glyphs.count, path, 0, 0, glyphs.paint); +} + +void SkiaCanvasProxy::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y, + const SkPaint& paint) { + SkDEBUGFAIL("SkiaCanvasProxy::onDrawTextBlob is not supported"); +} + +void SkiaCanvasProxy::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4], + const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) { + if (mFilterHwuiCalls) { + return; + } + SkPatchUtils::VertexData data; + + SkMatrix matrix; + mCanvas->getMatrix(&matrix); + SkISize lod = SkPatchUtils::GetLevelOfDetail(cubics, &matrix); + + // It automatically adjusts lodX and lodY in case it exceeds the number of indices. + // 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, + paint); + } +} + +void SkiaCanvasProxy::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle) { + mCanvas->clipRect(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, op); +} + +void SkiaCanvasProxy::onClipRRect(const SkRRect& roundRect, SkRegion::Op op, ClipEdgeStyle) { + SkPath path; + path.addRRect(roundRect); + mCanvas->clipPath(&path, op); +} + +void SkiaCanvasProxy::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle) { + mCanvas->clipPath(&path, op); +} + +void SkiaCanvasProxy::onClipRegion(const SkRegion& region, SkRegion::Op op) { + mCanvas->clipRegion(®ion, op); +} + +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/SkiaCanvasProxy.h b/libs/hwui/SkiaCanvasProxy.h new file mode 100644 index 0000000..0de9650 --- /dev/null +++ b/libs/hwui/SkiaCanvasProxy.h @@ -0,0 +1,108 @@ +/* + * 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. + */ + +#ifndef SkiaCanvasProxy_DEFINED +#define SkiaCanvasProxy_DEFINED + +#include <cutils/compiler.h> +#include <SkCanvas.h> + +#include "Canvas.h" + +namespace android { +namespace uirenderer { + +/** + * This class serves as a proxy between Skia's SkCanvas and Android Framework's + * Canvas. The class does not maintain any draw-related state and will pass + * through most requests directly to the Canvas provided in the constructor. + * + * Upon construction it is expected that the provided Canvas has already been + * prepared for recording and will continue to be in the recording state while + * this proxy class is being used. + * + * If filterHwuiCalls is true, the proxy silently ignores away draw calls that + * aren't supported by HWUI. + */ +class ANDROID_API SkiaCanvasProxy : public SkCanvas { +public: + SkiaCanvasProxy(Canvas* canvas, bool filterHwuiCalls = false); + virtual ~SkiaCanvasProxy() {} + +protected: + + virtual SkSurface* onNewSurface(const SkImageInfo&, const SkSurfaceProps&) override; + + virtual void willSave() override; + virtual SaveLayerStrategy willSaveLayer(const SkRect*, const SkPaint*, SaveFlags) override; + virtual void willRestore() override; + + virtual void didConcat(const SkMatrix&) override; + virtual void didSetMatrix(const SkMatrix&) override; + + virtual void onDrawPaint(const SkPaint& paint) override; + virtual void onDrawPoints(PointMode, size_t count, const SkPoint pts[], + const SkPaint&) override; + virtual void onDrawOval(const SkRect&, const SkPaint&) override; + virtual void onDrawRect(const SkRect&, const SkPaint&) override; + virtual void onDrawRRect(const SkRRect&, const SkPaint&) override; + virtual void onDrawPath(const SkPath& path, const SkPaint&) override; + virtual void onDrawBitmap(const SkBitmap&, SkScalar left, SkScalar top, + const SkPaint*) override; + virtual void onDrawBitmapRect(const SkBitmap&, const SkRect* src, const SkRect& dst, + const SkPaint* paint, DrawBitmapRectFlags flags) override; + virtual void onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, + const SkRect& dst, const SkPaint*) override; + virtual void onDrawSprite(const SkBitmap&, int left, int top, + const SkPaint*) override; + virtual void onDrawVertices(VertexMode, int vertexCount, const SkPoint vertices[], + const SkPoint texs[], const SkColor colors[], SkXfermode*, + const uint16_t indices[], int indexCount, + const SkPaint&) override; + + virtual void onDrawDRRect(const SkRRect&, const SkRRect&, const SkPaint&) override; + + virtual void onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y, + const SkPaint&) override; + virtual void onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[], + const SkPaint&) override; + virtual void onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[], + 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 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 SkPaint& paint) override; + + virtual void onClipRect(const SkRect&, SkRegion::Op, ClipEdgeStyle) override; + virtual void onClipRRect(const SkRRect&, SkRegion::Op, ClipEdgeStyle) override; + virtual void onClipPath(const SkPath&, SkRegion::Op, ClipEdgeStyle) override; + virtual void onClipRegion(const SkRegion&, SkRegion::Op) override; + +private: + Canvas* mCanvas; + bool mFilterHwuiCalls; + + typedef SkCanvas INHERITED; +}; + +}; // namespace uirenderer +}; // namespace android + +#endif // SkiaCanvasProxy_DEFINED diff --git a/libs/hwui/SkiaShader.cpp b/libs/hwui/SkiaShader.cpp index c672bc4..ecf8b6b 100644 --- a/libs/hwui/SkiaShader.cpp +++ b/libs/hwui/SkiaShader.cpp @@ -16,16 +16,17 @@ #define LOG_TAG "OpenGLRenderer" -#include <utils/Log.h> - -#include <SkMatrix.h> +#include "SkiaShader.h" #include "Caches.h" +#include "Extensions.h" #include "Layer.h" #include "Matrix.h" -#include "SkiaShader.h" #include "Texture.h" +#include <SkMatrix.h> +#include <utils/Log.h> + namespace android { namespace uirenderer { @@ -33,7 +34,7 @@ namespace uirenderer { // Support /////////////////////////////////////////////////////////////////////////////// -static const GLint gTileModes[] = { +static const GLenum gTileModes[] = { GL_CLAMP_TO_EDGE, // == SkShader::kClamp_TileMode GL_REPEAT, // == SkShader::kRepeat_Mode GL_MIRRORED_REPEAT // == SkShader::kMirror_TileMode @@ -46,17 +47,12 @@ static inline bool isPowerOfTwo(unsigned int n) { return !(n & (n - 1)); } -static inline void bindUniformColor(int slot, uint32_t color) { - const float a = ((color >> 24) & 0xff) / 255.0f; - glUniform4f(slot, - a * ((color >> 16) & 0xff) / 255.0f, - a * ((color >> 8) & 0xff) / 255.0f, - a * ((color ) & 0xff) / 255.0f, - a); +static inline void bindUniformColor(int slot, FloatColor color) { + glUniform4fv(slot, 1, reinterpret_cast<const float*>(&color)); } static inline void bindTexture(Caches* caches, Texture* texture, GLenum wrapS, GLenum wrapT) { - caches->bindTexture(texture->id); + caches->textureState().bindTexture(texture->id); texture->setWrapST(wrapS, wrapT); } @@ -78,229 +74,11 @@ static void computeScreenSpaceMatrix(mat4& screenSpace, const SkMatrix& unitMatr screenSpace.multiply(modelViewMatrix); } -// Returns true if one is a bitmap and the other is a gradient -static bool bitmapAndGradient(SkiaShaderType type1, SkiaShaderType type2) { - return (type1 == kBitmap_SkiaShaderType && type2 == kGradient_SkiaShaderType) - || (type2 == kBitmap_SkiaShaderType && type1 == kGradient_SkiaShaderType); -} - -SkiaShaderType SkiaShader::getType(const SkShader& shader) { - // First check for a gradient shader. - switch (shader.asAGradient(NULL)) { - case SkShader::kNone_GradientType: - // Not a gradient shader. Fall through to check for other types. - break; - case SkShader::kLinear_GradientType: - case SkShader::kRadial_GradientType: - case SkShader::kSweep_GradientType: - return kGradient_SkiaShaderType; - default: - // This is a Skia gradient that has no SkiaShader equivalent. Return None to skip. - return kNone_SkiaShaderType; - } - - // The shader is not a gradient. Check for a bitmap shader. - if (shader.asABitmap(NULL, NULL, NULL) == SkShader::kDefault_BitmapType) { - return kBitmap_SkiaShaderType; - } - - // Check for a ComposeShader. - SkShader::ComposeRec rec; - if (shader.asACompose(&rec)) { - const SkiaShaderType shaderAType = getType(*rec.fShaderA); - const SkiaShaderType shaderBType = getType(*rec.fShaderB); - - // Compose is only supported if one is a bitmap and the other is a - // gradient. Otherwise, return None to skip. - if (!bitmapAndGradient(shaderAType, shaderBType)) { - return kNone_SkiaShaderType; - } - return kCompose_SkiaShaderType; - } - - if (shader.asACustomShader(NULL)) { - return kLayer_SkiaShaderType; - } - - return kNone_SkiaShaderType; -} - -typedef void (*describeProc)(Caches* caches, ProgramDescription& description, - const Extensions& extensions, const SkShader& shader); - -describeProc gDescribeProc[] = { - InvalidSkiaShader::describe, - SkiaBitmapShader::describe, - SkiaGradientShader::describe, - SkiaComposeShader::describe, - SkiaLayerShader::describe, -}; - -typedef void (*setupProgramProc)(Caches* caches, const mat4& modelViewMatrix, - GLuint* textureUnit, const Extensions& extensions, const SkShader& shader); - -setupProgramProc gSetupProgramProc[] = { - InvalidSkiaShader::setupProgram, - SkiaBitmapShader::setupProgram, - SkiaGradientShader::setupProgram, - SkiaComposeShader::setupProgram, - SkiaLayerShader::setupProgram, -}; - -void SkiaShader::describe(Caches* caches, ProgramDescription& description, - const Extensions& extensions, const SkShader& shader) { - gDescribeProc[getType(shader)](caches, description, extensions, shader); -} - -void SkiaShader::setupProgram(Caches* caches, const mat4& modelViewMatrix, - GLuint* textureUnit, const Extensions& extensions, const SkShader& shader) { - - gSetupProgramProc[getType(shader)](caches, modelViewMatrix, textureUnit, extensions, shader); -} - -/////////////////////////////////////////////////////////////////////////////// -// Layer shader -/////////////////////////////////////////////////////////////////////////////// - -void SkiaLayerShader::describe(Caches*, ProgramDescription& description, - const Extensions&, const SkShader& shader) { - description.hasBitmap = true; -} - -void SkiaLayerShader::setupProgram(Caches* caches, const mat4& modelViewMatrix, - GLuint* textureUnit, const Extensions&, const SkShader& shader) { - Layer* layer; - if (!shader.asACustomShader(reinterpret_cast<void**>(&layer))) { - LOG_ALWAYS_FATAL("SkiaLayerShader::setupProgram called on the wrong type of shader!"); - } - - GLuint textureSlot = (*textureUnit)++; - caches->activeTexture(textureSlot); - - const float width = layer->getWidth(); - const float height = layer->getHeight(); - - mat4 textureTransform; - computeScreenSpaceMatrix(textureTransform, SkMatrix::I(), shader.getLocalMatrix(), - modelViewMatrix); - - - // Uniforms - layer->bindTexture(); - layer->setWrap(GL_CLAMP_TO_EDGE); - layer->setFilter(GL_LINEAR); - - Program* program = caches->currentProgram; - glUniform1i(program->getUniform("bitmapSampler"), textureSlot); - glUniformMatrix4fv(program->getUniform("textureTransform"), 1, - GL_FALSE, &textureTransform.data[0]); - glUniform2f(program->getUniform("textureDimension"), 1.0f / width, 1.0f / height); -} - /////////////////////////////////////////////////////////////////////////////// -// Bitmap shader +// gradient shader matrix helpers /////////////////////////////////////////////////////////////////////////////// -struct BitmapShaderInfo { - float width; - float height; - GLenum wrapS; - GLenum wrapT; - Texture* texture; -}; - -static bool bitmapShaderHelper(Caches* caches, ProgramDescription* description, - BitmapShaderInfo* shaderInfo, - const Extensions& extensions, - const SkBitmap& bitmap, SkShader::TileMode tileModes[2]) { - Texture* texture = caches->textureCache.get(&bitmap); - if (!texture) return false; - - const float width = texture->width; - const float height = texture->height; - GLenum wrapS, wrapT; - - if (description) { - description->hasBitmap = true; - } - // The driver does not support non-power of two mirrored/repeated - // textures, so do it ourselves - if (!extensions.hasNPot() && (!isPowerOfTwo(width) || !isPowerOfTwo(height)) && - (tileModes[0] != SkShader::kClamp_TileMode || - tileModes[1] != SkShader::kClamp_TileMode)) { - if (description) { - description->isBitmapNpot = true; - description->bitmapWrapS = gTileModes[tileModes[0]]; - description->bitmapWrapT = gTileModes[tileModes[1]]; - } - wrapS = GL_CLAMP_TO_EDGE; - wrapT = GL_CLAMP_TO_EDGE; - } else { - wrapS = gTileModes[tileModes[0]]; - wrapT = gTileModes[tileModes[1]]; - } - - if (shaderInfo) { - shaderInfo->width = width; - shaderInfo->height = height; - shaderInfo->wrapS = wrapS; - shaderInfo->wrapT = wrapT; - shaderInfo->texture = texture; - } - return true; -} - -void SkiaBitmapShader::describe(Caches* caches, ProgramDescription& description, - const Extensions& extensions, const SkShader& shader) { - SkBitmap bitmap; - SkShader::TileMode xy[2]; - if (shader.asABitmap(&bitmap, NULL, xy) != SkShader::kDefault_BitmapType) { - LOG_ALWAYS_FATAL("SkiaBitmapShader::describe called with a different kind of shader!"); - } - bitmapShaderHelper(caches, &description, NULL, extensions, bitmap, xy); -} - -void SkiaBitmapShader::setupProgram(Caches* caches, const mat4& modelViewMatrix, - GLuint* textureUnit, const Extensions& extensions, const SkShader& shader) { - SkBitmap bitmap; - SkShader::TileMode xy[2]; - if (shader.asABitmap(&bitmap, NULL, xy) != SkShader::kDefault_BitmapType) { - LOG_ALWAYS_FATAL("SkiaBitmapShader::setupProgram called with a different kind of shader!"); - } - - GLuint textureSlot = (*textureUnit)++; - Caches::getInstance().activeTexture(textureSlot); - - BitmapShaderInfo shaderInfo; - if (!bitmapShaderHelper(caches, NULL, &shaderInfo, extensions, bitmap, xy)) { - return; - } - - Program* program = caches->currentProgram; - Texture* texture = shaderInfo.texture; - - const AutoTexture autoCleanup(texture); - - mat4 textureTransform; - computeScreenSpaceMatrix(textureTransform, SkMatrix::I(), shader.getLocalMatrix(), - modelViewMatrix); - - // Uniforms - bindTexture(caches, texture, shaderInfo.wrapS, shaderInfo.wrapT); - texture->setFilter(GL_LINEAR); - - glUniform1i(program->getUniform("bitmapSampler"), textureSlot); - glUniformMatrix4fv(program->getUniform("textureTransform"), 1, - GL_FALSE, &textureTransform.data[0]); - glUniform2f(program->getUniform("textureDimension"), 1.0f / shaderInfo.width, - 1.0f / shaderInfo.height); -} - -/////////////////////////////////////////////////////////////////////////////// -// Linear gradient shader -/////////////////////////////////////////////////////////////////////////////// - -static void toUnitMatrix(const SkPoint pts[2], SkMatrix* matrix) { +static void toLinearUnitMatrix(const SkPoint pts[2], SkMatrix* matrix) { SkVector vec = pts[1] - pts[0]; const float mag = vec.length(); const float inv = mag ? 1.0f / mag : 0; @@ -311,10 +89,6 @@ static void toUnitMatrix(const SkPoint pts[2], SkMatrix* matrix) { matrix->postScale(inv, inv); } -/////////////////////////////////////////////////////////////////////////////// -// Circular gradient shader -/////////////////////////////////////////////////////////////////////////////// - static void toCircularUnitMatrix(const float x, const float y, const float radius, SkMatrix* matrix) { const float inv = 1.0f / radius; @@ -322,10 +96,6 @@ static void toCircularUnitMatrix(const float x, const float y, const float radiu matrix->postScale(inv, inv); } -/////////////////////////////////////////////////////////////////////////////// -// Sweep gradient shader -/////////////////////////////////////////////////////////////////////////////// - static void toSweepUnitMatrix(const float x, const float y, SkMatrix* matrix) { matrix->setTranslate(-x, -y); } @@ -338,135 +108,289 @@ static bool isSimpleGradient(const SkShader::GradientInfo& gradInfo) { return gradInfo.fColorCount == 2 && gradInfo.fTileMode == SkShader::kClamp_TileMode; } -void SkiaGradientShader::describe(Caches*, ProgramDescription& description, - const Extensions& extensions, const SkShader& shader) { +/////////////////////////////////////////////////////////////////////////////// +// Store / apply +/////////////////////////////////////////////////////////////////////////////// + +bool tryStoreGradient(Caches& caches, const SkShader& shader, const Matrix4 modelViewMatrix, + GLuint* textureUnit, ProgramDescription* description, + SkiaShaderData::GradientShaderData* outData) { SkShader::GradientInfo gradInfo; gradInfo.fColorCount = 0; - gradInfo.fColors = NULL; - gradInfo.fColorOffsets = NULL; + gradInfo.fColors = nullptr; + gradInfo.fColorOffsets = nullptr; + SkMatrix unitMatrix; switch (shader.asAGradient(&gradInfo)) { case SkShader::kLinear_GradientType: - description.gradientType = ProgramDescription::kGradientLinear; + description->gradientType = ProgramDescription::kGradientLinear; + + toLinearUnitMatrix(gradInfo.fPoint, &unitMatrix); break; case SkShader::kRadial_GradientType: - description.gradientType = ProgramDescription::kGradientCircular; + description->gradientType = ProgramDescription::kGradientCircular; + + toCircularUnitMatrix(gradInfo.fPoint[0].fX, gradInfo.fPoint[0].fY, + gradInfo.fRadius[0], &unitMatrix); break; case SkShader::kSweep_GradientType: - description.gradientType = ProgramDescription::kGradientSweep; + description->gradientType = ProgramDescription::kGradientSweep; + + toSweepUnitMatrix(gradInfo.fPoint[0].fX, gradInfo.fPoint[0].fY, &unitMatrix); break; default: // Do nothing. This shader is unsupported. - return; + return false; } - description.hasGradient = true; - description.isSimpleGradient = isSimpleGradient(gradInfo); -} + description->hasGradient = true; + description->isSimpleGradient = isSimpleGradient(gradInfo); -void SkiaGradientShader::setupProgram(Caches* caches, const mat4& modelViewMatrix, - GLuint* textureUnit, const Extensions&, const SkShader& shader) { - // SkShader::GradientInfo.fColorCount is an in/out parameter. As input, it tells asAGradient - // how much space has been allocated for fColors and fColorOffsets. 10 was chosen - // arbitrarily, but should be >= 2. - // As output, it tells the number of actual colors/offsets in the gradient. - const int COLOR_COUNT = 10; - SkAutoSTMalloc<COLOR_COUNT, SkColor> colorStorage(COLOR_COUNT); - SkAutoSTMalloc<COLOR_COUNT, SkScalar> positionStorage(COLOR_COUNT); + computeScreenSpaceMatrix(outData->screenSpace, unitMatrix, + shader.getLocalMatrix(), modelViewMatrix); - SkShader::GradientInfo gradInfo; - gradInfo.fColorCount = COLOR_COUNT; - gradInfo.fColors = colorStorage.get(); - gradInfo.fColorOffsets = positionStorage.get(); + // re-query shader to get full color / offset data + std::unique_ptr<SkColor[]> colorStorage(new SkColor[gradInfo.fColorCount]); + std::unique_ptr<SkScalar[]> colorOffsets(new SkScalar[gradInfo.fColorCount]); + gradInfo.fColors = &colorStorage[0]; + gradInfo.fColorOffsets = &colorOffsets[0]; + shader.asAGradient(&gradInfo); - SkShader::GradientType gradType = shader.asAGradient(&gradInfo); - - Program* program = caches->currentProgram; if (CC_UNLIKELY(!isSimpleGradient(gradInfo))) { - if (gradInfo.fColorCount > COLOR_COUNT) { - // There was not enough room in our arrays for all the colors and offsets. Try again, - // now that we know the true number of colors. - gradInfo.fColors = colorStorage.reset(gradInfo.fColorCount); - gradInfo.fColorOffsets = positionStorage.reset(gradInfo.fColorCount); - - shader.asAGradient(&gradInfo); - } - GLuint textureSlot = (*textureUnit)++; - caches->activeTexture(textureSlot); + outData->gradientSampler = (*textureUnit)++; #ifndef SK_SCALAR_IS_FLOAT #error Need to convert gradInfo.fColorOffsets to float! #endif - Texture* texture = caches->gradientCache.get(gradInfo.fColors, gradInfo.fColorOffsets, - gradInfo.fColorCount); + outData->gradientTexture = caches.gradientCache.get( + gradInfo.fColors, gradInfo.fColorOffsets, gradInfo.fColorCount); + outData->wrapST = gTileModes[gradInfo.fTileMode]; + } else { + outData->gradientSampler = 0; + outData->gradientTexture = nullptr; - // Uniforms - bindTexture(caches, texture, gTileModes[gradInfo.fTileMode], gTileModes[gradInfo.fTileMode]); - glUniform1i(program->getUniform("gradientSampler"), textureSlot); + outData->startColor.set(gradInfo.fColors[0]); + outData->endColor.set(gradInfo.fColors[1]); + } + + outData->ditherSampler = (*textureUnit)++; + return true; +} + +void applyGradient(Caches& caches, const SkiaShaderData::GradientShaderData& data) { + if (CC_UNLIKELY(data.gradientTexture)) { + caches.textureState().activateTexture(data.gradientSampler); + bindTexture(&caches, data.gradientTexture, data.wrapST, data.wrapST); + glUniform1i(caches.program().getUniform("gradientSampler"), data.gradientSampler); } else { - bindUniformColor(program->getUniform("startColor"), gradInfo.fColors[0]); - bindUniformColor(program->getUniform("endColor"), gradInfo.fColors[1]); + bindUniformColor(caches.program().getUniform("startColor"), data.startColor); + bindUniformColor(caches.program().getUniform("endColor"), data.endColor); } - caches->dither.setupProgram(program, textureUnit); + // 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); + glUniformMatrix4fv(caches.program().getUniform("screenSpace"), 1, + GL_FALSE, &data.screenSpace.data[0]); +} - SkMatrix unitMatrix; - switch (gradType) { - case SkShader::kLinear_GradientType: - toUnitMatrix(gradInfo.fPoint, &unitMatrix); +bool tryStoreBitmap(Caches& caches, const SkShader& shader, const Matrix4& modelViewMatrix, + GLuint* textureUnit, ProgramDescription* description, + SkiaShaderData::BitmapShaderData* outData) { + SkBitmap bitmap; + SkShader::TileMode xy[2]; + if (shader.asABitmap(&bitmap, nullptr, xy) != SkShader::kDefault_BitmapType) { + return false; + } + + outData->bitmapTexture = caches.textureCache.get(&bitmap); + if (!outData->bitmapTexture) return false; + + outData->bitmapSampler = (*textureUnit)++; + + const float width = outData->bitmapTexture->width; + const float height = outData->bitmapTexture->height; + + description->hasBitmap = true; + if (!caches.extensions().hasNPot() + && (!isPowerOfTwo(width) || !isPowerOfTwo(height)) + && (xy[0] != SkShader::kClamp_TileMode || xy[1] != SkShader::kClamp_TileMode)) { + description->isBitmapNpot = true; + description->bitmapWrapS = gTileModes[xy[0]]; + description->bitmapWrapT = gTileModes[xy[1]]; + + outData->wrapS = GL_CLAMP_TO_EDGE; + outData->wrapT = GL_CLAMP_TO_EDGE; + } else { + outData->wrapS = gTileModes[xy[0]]; + outData->wrapT = gTileModes[xy[1]]; + } + + computeScreenSpaceMatrix(outData->textureTransform, SkMatrix::I(), shader.getLocalMatrix(), + modelViewMatrix); + outData->textureDimension[0] = 1.0f / width; + outData->textureDimension[1] = 1.0f / height; + + return true; +} + +void applyBitmap(Caches& caches, const SkiaShaderData::BitmapShaderData& data) { + caches.textureState().activateTexture(data.bitmapSampler); + bindTexture(&caches, data.bitmapTexture, data.wrapS, data.wrapT); + data.bitmapTexture->setFilter(GL_LINEAR); + + glUniform1i(caches.program().getUniform("bitmapSampler"), data.bitmapSampler); + glUniformMatrix4fv(caches.program().getUniform("textureTransform"), 1, GL_FALSE, + &data.textureTransform.data[0]); + glUniform2fv(caches.program().getUniform("textureDimension"), 1, &data.textureDimension[0]); +} + +SkiaShaderType getComposeSubType(const SkShader& shader) { + // First check for a gradient shader. + switch (shader.asAGradient(nullptr)) { + case SkShader::kNone_GradientType: + // Not a gradient shader. Fall through to check for other types. break; + case SkShader::kLinear_GradientType: case SkShader::kRadial_GradientType: - toCircularUnitMatrix(gradInfo.fPoint[0].fX, gradInfo.fPoint[0].fY, - gradInfo.fRadius[0], &unitMatrix); - break; case SkShader::kSweep_GradientType: - toSweepUnitMatrix(gradInfo.fPoint[0].fX, gradInfo.fPoint[0].fY, &unitMatrix); - break; + return kGradient_SkiaShaderType; default: - LOG_ALWAYS_FATAL("Invalid SkShader gradient type %d", gradType); + // This is a Skia gradient that has no SkiaShader equivalent. Return None to skip. + return kNone_SkiaShaderType; + } + + // The shader is not a gradient. Check for a bitmap shader. + if (shader.asABitmap(nullptr, nullptr, nullptr) == SkShader::kDefault_BitmapType) { + return kBitmap_SkiaShaderType; } + return kNone_SkiaShaderType; +} - mat4 screenSpace; - computeScreenSpaceMatrix(screenSpace, unitMatrix, shader.getLocalMatrix(), modelViewMatrix); - glUniformMatrix4fv(program->getUniform("screenSpace"), 1, GL_FALSE, &screenSpace.data[0]); +void storeCompose(Caches& caches, const SkShader& bitmapShader, const SkShader& gradientShader, + const Matrix4& modelViewMatrix, GLuint* textureUnit, + ProgramDescription* description, SkiaShaderData* outData) { + LOG_ALWAYS_FATAL_IF(!tryStoreBitmap(caches, bitmapShader, modelViewMatrix, + textureUnit, description, &outData->bitmapData), + "failed storing bitmap shader data"); + LOG_ALWAYS_FATAL_IF(!tryStoreGradient(caches, gradientShader, modelViewMatrix, + textureUnit, description, &outData->gradientData), + "failing storing gradient shader data"); } -/////////////////////////////////////////////////////////////////////////////// -// Compose shader -/////////////////////////////////////////////////////////////////////////////// +bool tryStoreCompose(Caches& caches, const SkShader& shader, const Matrix4& modelViewMatrix, + GLuint* textureUnit, ProgramDescription* description, + SkiaShaderData* outData) { -void SkiaComposeShader::describe(Caches* caches, ProgramDescription& description, - const Extensions& extensions, const SkShader& shader) { SkShader::ComposeRec rec; - if (!shader.asACompose(&rec)) { - LOG_ALWAYS_FATAL("SkiaComposeShader::describe called on the wrong shader type!"); - } - SkiaShader::describe(caches, description, extensions, *rec.fShaderA); - SkiaShader::describe(caches, description, extensions, *rec.fShaderB); - if (SkiaShader::getType(*rec.fShaderA) == kBitmap_SkiaShaderType) { - description.isBitmapFirst = true; + if (!shader.asACompose(&rec)) return false; + + const SkiaShaderType shaderAType = getComposeSubType(*rec.fShaderA); + const SkiaShaderType shaderBType = getComposeSubType(*rec.fShaderB); + + // check that type enum values are the 2 flags that compose the kCompose value + if ((shaderAType & shaderBType) != 0) return false; + if ((shaderAType | shaderBType) != kCompose_SkiaShaderType) return false; + + mat4 transform; + computeScreenSpaceMatrix(transform, SkMatrix::I(), shader.getLocalMatrix(), modelViewMatrix); + if (shaderAType == kBitmap_SkiaShaderType) { + description->isBitmapFirst = true; + storeCompose(caches, *rec.fShaderA, *rec.fShaderB, + transform, textureUnit, description, outData); + } else { + description->isBitmapFirst = false; + storeCompose(caches, *rec.fShaderB, *rec.fShaderA, + transform, textureUnit, description, outData); } - if (!SkXfermode::AsMode(rec.fMode, &description.shadersMode)) { + if (!SkXfermode::AsMode(rec.fMode, &description->shadersMode)) { // TODO: Support other modes. - description.shadersMode = SkXfermode::kSrcOver_Mode; + description->shadersMode = SkXfermode::kSrcOver_Mode; } + return true; } -void SkiaComposeShader::setupProgram(Caches* caches, const mat4& modelViewMatrix, - GLuint* textureUnit, const Extensions& extensions, const SkShader& shader) { - SkShader::ComposeRec rec; - if (!shader.asACompose(&rec)) { - LOG_ALWAYS_FATAL("SkiaComposeShader::setupProgram called on the wrong shader type!"); +bool tryStoreLayer(Caches& caches, const SkShader& shader, const Matrix4& modelViewMatrix, + GLuint* textureUnit, ProgramDescription* description, + SkiaShaderData::LayerShaderData* outData) { + Layer* layer; + if (!shader.asACustomShader(reinterpret_cast<void**>(&layer))) { + return false; } - // Apply this compose shader's local transform and pass it down to - // the child shaders. They will in turn apply their local transform - // to this matrix. - mat4 transform; - computeScreenSpaceMatrix(transform, SkMatrix::I(), shader.getLocalMatrix(), + description->hasBitmap = true; + outData->layer = layer; + outData->bitmapSampler = (*textureUnit)++; + + const float width = layer->getWidth(); + const float height = layer->getHeight(); + + computeScreenSpaceMatrix(outData->textureTransform, SkMatrix::I(), shader.getLocalMatrix(), modelViewMatrix); - SkiaShader::setupProgram(caches, transform, textureUnit, extensions, *rec.fShaderA); - SkiaShader::setupProgram(caches, transform, textureUnit, extensions, *rec.fShaderB); + outData->textureDimension[0] = 1.0f / width; + outData->textureDimension[1] = 1.0f / height; + return true; +} + +void applyLayer(Caches& caches, const SkiaShaderData::LayerShaderData& data) { + caches.textureState().activateTexture(data.bitmapSampler); + + data.layer->bindTexture(); + data.layer->setWrap(GL_CLAMP_TO_EDGE); + data.layer->setFilter(GL_LINEAR); + + glUniform1i(caches.program().getUniform("bitmapSampler"), data.bitmapSampler); + glUniformMatrix4fv(caches.program().getUniform("textureTransform"), 1, + GL_FALSE, &data.textureTransform.data[0]); + glUniform2fv(caches.program().getUniform("textureDimension"), 1, &data.textureDimension[0]); +} + +void SkiaShader::store(Caches& caches, const SkShader* shader, const Matrix4& modelViewMatrix, + GLuint* textureUnit, ProgramDescription* description, + SkiaShaderData* outData) { + if (!shader) { + outData->skiaShaderType = kNone_SkiaShaderType; + return; + } + + if (tryStoreGradient(caches, *shader, modelViewMatrix, + textureUnit, description, &outData->gradientData)) { + outData->skiaShaderType = kGradient_SkiaShaderType; + return; + } + + if (tryStoreBitmap(caches, *shader, modelViewMatrix, + textureUnit, description, &outData->bitmapData)) { + outData->skiaShaderType = kBitmap_SkiaShaderType; + return; + } + + if (tryStoreCompose(caches, *shader, modelViewMatrix, + textureUnit, description, outData)) { + outData->skiaShaderType = kCompose_SkiaShaderType; + return; + } + + if (tryStoreLayer(caches, *shader, modelViewMatrix, + textureUnit, description, &outData->layerData)) { + outData->skiaShaderType = kLayer_SkiaShaderType; + } +} + +void SkiaShader::apply(Caches& caches, const SkiaShaderData& data) { + if (!data.skiaShaderType) return; + + if (data.skiaShaderType & kGradient_SkiaShaderType) { + applyGradient(caches, data.gradientData); + } + if (data.skiaShaderType & kBitmap_SkiaShaderType) { + applyBitmap(caches, data.bitmapData); + } + + if (data.skiaShaderType == kLayer_SkiaShaderType) { + applyLayer(caches, data.layerData); + } } }; // namespace uirenderer diff --git a/libs/hwui/SkiaShader.h b/libs/hwui/SkiaShader.h index 034c3f6..5b8aa86 100644 --- a/libs/hwui/SkiaShader.h +++ b/libs/hwui/SkiaShader.h @@ -17,102 +17,82 @@ #ifndef ANDROID_HWUI_SKIA_SHADER_H #define ANDROID_HWUI_SKIA_SHADER_H -#include <SkShader.h> -#include <SkXfermode.h> +#include "FloatColor.h" +#include "Matrix.h" #include <GLES2/gl2.h> - +#include <SkShader.h> +#include <SkXfermode.h> #include <cutils/compiler.h> -#include "Extensions.h" -#include "ProgramCache.h" -#include "TextureCache.h" -#include "GradientCache.h" - namespace android { namespace uirenderer { class Caches; +class Extensions; class Layer; +class Texture; +struct ProgramDescription; /** * Type of Skia shader in use. + * + * Note that kBitmap | kGradient = kCompose, since Compose implies + * both its component types are in use simultaneously. No other + * composition of multiple types is supported. */ enum SkiaShaderType { - kNone_SkiaShaderType, - kBitmap_SkiaShaderType, - kGradient_SkiaShaderType, - kCompose_SkiaShaderType, - kLayer_SkiaShaderType + kNone_SkiaShaderType = 0, + kBitmap_SkiaShaderType = 1, + kGradient_SkiaShaderType = 2, + kCompose_SkiaShaderType = kBitmap_SkiaShaderType | kGradient_SkiaShaderType, + kLayer_SkiaShaderType = 4, }; -class SkiaShader { -public: - static SkiaShaderType getType(const SkShader& shader); - static void describe(Caches* caches, ProgramDescription& description, - const Extensions& extensions, const SkShader& shader); - static void setupProgram(Caches* caches, const mat4& modelViewMatrix, - GLuint* textureUnit, const Extensions& extensions, const SkShader& shader); +struct SkiaShaderData { + SkiaShaderType skiaShaderType; + struct BitmapShaderData { + Texture* bitmapTexture; + GLuint bitmapSampler; + GLenum wrapS; + GLenum wrapT; + + Matrix4 textureTransform; + float textureDimension[2]; + } bitmapData; + struct GradientShaderData { + Matrix4 screenSpace; + GLuint ditherSampler; + + // simple gradient + FloatColor startColor; + FloatColor endColor; + + // complex gradient + Texture* gradientTexture; + GLuint gradientSampler; + GLenum wrapST; + + } gradientData; + struct LayerShaderData { + Layer* layer; + GLuint bitmapSampler; + GLenum wrapS; + GLenum wrapT; + + Matrix4 textureTransform; + float textureDimension[2]; + } layerData; }; -class InvalidSkiaShader { -public: - static void describe(Caches* caches, ProgramDescription& description, - const Extensions& extensions, const SkShader& shader) { - // This shader is unsupported. Skip it. - } - static void setupProgram(Caches* caches, const mat4& modelViewMatrix, - GLuint* textureUnit, const Extensions& extensions, const SkShader& shader) { - // This shader is unsupported. Skip it. - } - -}; -/** - * A shader that draws a layer. - */ -class SkiaLayerShader { -public: - static void describe(Caches* caches, ProgramDescription& description, - const Extensions& extensions, const SkShader& shader); - static void setupProgram(Caches* caches, const mat4& modelViewMatrix, - GLuint* textureUnit, const Extensions& extensions, const SkShader& shader); -}; // class SkiaLayerShader - -/** - * A shader that draws a bitmap. - */ -class SkiaBitmapShader { -public: - static void describe(Caches* caches, ProgramDescription& description, - const Extensions& extensions, const SkShader& shader); - static void setupProgram(Caches* caches, const mat4& modelViewMatrix, - GLuint* textureUnit, const Extensions& extensions, const SkShader& shader); - - -}; // class SkiaBitmapShader - -/** - * A shader that draws one of three types of gradient, depending on shader param. - */ -class SkiaGradientShader { +class SkiaShader { public: - static void describe(Caches* caches, ProgramDescription& description, - const Extensions& extensions, const SkShader& shader); - static void setupProgram(Caches* caches, const mat4& modelViewMatrix, - GLuint* textureUnit, const Extensions& extensions, const SkShader& shader); + 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); }; -/** - * A shader that draws two shaders, composited with an xfermode. - */ -class SkiaComposeShader { -public: - static void describe(Caches* caches, ProgramDescription& description, - const Extensions& extensions, const SkShader& shader); - static void setupProgram(Caches* caches, const mat4& modelViewMatrix, - GLuint* textureUnit, const Extensions& extensions, const SkShader& shader); -}; // class SkiaComposeShader - }; // namespace uirenderer }; // namespace android diff --git a/libs/hwui/Snapshot.cpp b/libs/hwui/Snapshot.cpp index cf8229f..9e7faee 100644 --- a/libs/hwui/Snapshot.cpp +++ b/libs/hwui/Snapshot.cpp @@ -29,17 +29,16 @@ namespace uirenderer { Snapshot::Snapshot() : flags(0) - , previous(NULL) - , layer(NULL) + , previous(nullptr) + , layer(nullptr) , fbo(0) , invisible(false) , empty(false) , alpha(1.0f) - , roundRectClipState(NULL) { + , roundRectClipState(nullptr) + , mClipArea(&mClipAreaRoot) { transform = &mTransformRoot; - clipRect = &mClipRectRoot; - region = NULL; - clipRegion = &mClipRegionRoot; + region = nullptr; } /** @@ -55,6 +54,7 @@ Snapshot::Snapshot(const sp<Snapshot>& s, int saveFlags) , empty(false) , alpha(s->alpha) , roundRectClipState(s->roundRectClipState) + , mClipArea(nullptr) , mViewportData(s->mViewportData) , mRelativeLightCenter(s->mRelativeLightCenter) { if (saveFlags & SkCanvas::kMatrix_SaveFlag) { @@ -65,22 +65,17 @@ Snapshot::Snapshot(const sp<Snapshot>& s, int saveFlags) } if (saveFlags & SkCanvas::kClip_SaveFlag) { - mClipRectRoot.set(*s->clipRect); - clipRect = &mClipRectRoot; - if (!s->clipRegion->isEmpty()) { - mClipRegionRoot.op(*s->clipRegion, SkRegion::kUnion_Op); - } - clipRegion = &mClipRegionRoot; + mClipAreaRoot = s->getClipArea(); + mClipArea = &mClipAreaRoot; } else { - clipRect = s->clipRect; - clipRegion = s->clipRegion; + mClipArea = s->mClipArea; } if (s->flags & Snapshot::kFlagFboTarget) { flags |= Snapshot::kFlagFboTarget; region = s->region; } else { - region = NULL; + region = nullptr; } } @@ -88,88 +83,23 @@ Snapshot::Snapshot(const sp<Snapshot>& s, int saveFlags) // Clipping /////////////////////////////////////////////////////////////////////////////// -void Snapshot::ensureClipRegion() { - if (clipRegion->isEmpty()) { - clipRegion->setRect(clipRect->left, clipRect->top, clipRect->right, clipRect->bottom); - } -} - -void Snapshot::copyClipRectFromRegion() { - if (!clipRegion->isEmpty()) { - const SkIRect& bounds = clipRegion->getBounds(); - clipRect->set(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom); - - if (clipRegion->isRect()) { - clipRegion->setEmpty(); - } - } else { - clipRect->setEmpty(); - } -} - -bool Snapshot::clipRegionOp(float left, float top, float right, float bottom, SkRegion::Op op) { - SkIRect tmp; - tmp.set(left, top, right, bottom); - clipRegion->op(tmp, op); - copyClipRectFromRegion(); - return true; -} - bool Snapshot::clipRegionTransformed(const SkRegion& region, SkRegion::Op op) { - ensureClipRegion(); - clipRegion->op(region, op); - copyClipRectFromRegion(); flags |= Snapshot::kFlagClipSet; - return true; + return mClipArea->clipRegion(region, op); } bool Snapshot::clip(float left, float top, float right, float bottom, SkRegion::Op op) { - Rect r(left, top, right, bottom); - transform->mapRect(r); - return clipTransformed(r, op); + flags |= Snapshot::kFlagClipSet; + return mClipArea->clipRectWithTransform(left, top, right, bottom, transform, op); } -bool Snapshot::clipTransformed(const Rect& r, SkRegion::Op op) { - bool clipped = false; - - switch (op) { - case SkRegion::kIntersect_Op: { - if (CC_UNLIKELY(!clipRegion->isEmpty())) { - ensureClipRegion(); - clipped = clipRegionOp(r.left, r.top, r.right, r.bottom, SkRegion::kIntersect_Op); - } else { - clipped = clipRect->intersect(r); - if (!clipped) { - clipRect->setEmpty(); - clipped = true; - } - } - break; - } - case SkRegion::kReplace_Op: { - setClip(r.left, r.top, r.right, r.bottom); - clipped = true; - break; - } - default: { - ensureClipRegion(); - clipped = clipRegionOp(r.left, r.top, r.right, r.bottom, op); - break; - } - } - - if (clipped) { - flags |= Snapshot::kFlagClipSet; - } - - return clipped; +bool Snapshot::clipPath(const SkPath& path, SkRegion::Op op) { + flags |= Snapshot::kFlagClipSet; + return mClipArea->clipPathWithTransform(path, transform, op); } void Snapshot::setClip(float left, float top, float right, float bottom) { - clipRect->set(left, top, right, bottom); - if (!clipRegion->isEmpty()) { - clipRegion->setEmpty(); - } + mClipArea->setClip(left, top, right, bottom); flags |= Snapshot::kFlagClipSet; } @@ -181,7 +111,7 @@ const Rect& Snapshot::getLocalClip() { mat4 inverse; inverse.loadInverse(*transform); - mLocalClip.set(*clipRect); + mLocalClip.set(mClipArea->getClipRect()); inverse.mapRect(mLocalClip); return mLocalClip; @@ -191,8 +121,7 @@ void Snapshot::resetClip(float left, float top, float right, float bottom) { // TODO: This is incorrect, when we start rendering into a new layer, // we may have to modify the previous snapshot's clip rect and clip // region if the previous restore() call did not restore the clip - clipRect = &mClipRectRoot; - clipRegion = &mClipRegionRoot; + mClipArea = &mClipAreaRoot; setClip(left, top, right, bottom); } @@ -219,7 +148,7 @@ void Snapshot::resetTransform(float x, float y, float z) { void Snapshot::setClippingRoundRect(LinearAllocator& allocator, const Rect& bounds, float radius, bool highPriority) { if (bounds.isEmpty()) { - clipRect->setEmpty(); + mClipArea->setEmpty(); return; } @@ -272,9 +201,11 @@ bool Snapshot::isIgnored() const { void Snapshot::dump() const { ALOGD("Snapshot %p, flags %x, prev %p, height %d, ignored %d, hasComplexClip %d", - this, flags, previous.get(), getViewportHeight(), isIgnored(), clipRegion && !clipRegion->isEmpty()); - ALOGD(" ClipRect (at %p) %.1f %.1f %.1f %.1f", - clipRect, clipRect->left, clipRect->top, clipRect->right, clipRect->bottom); + this, flags, previous.get(), getViewportHeight(), isIgnored(), !mClipArea->isSimple()); + const Rect& clipRect(mClipArea->getClipRect()); + ALOGD(" ClipRect %.1f %.1f %.1f %.1f, clip simple %d", + clipRect.left, clipRect.top, clipRect.right, clipRect.bottom, mClipArea->isSimple()); + ALOGD(" Transform (at %p):", transform); transform->dump(); } diff --git a/libs/hwui/Snapshot.h b/libs/hwui/Snapshot.h index 549de9b..4d704ab 100644 --- a/libs/hwui/Snapshot.h +++ b/libs/hwui/Snapshot.h @@ -26,6 +26,7 @@ #include <SkRegion.h> +#include "ClipArea.h" #include "Layer.h" #include "Matrix.h" #include "Outline.h" @@ -129,6 +130,11 @@ public: bool clipRegionTransformed(const SkRegion& region, SkRegion::Op op); /** + * Modifies the current clip with the specified path and operation. + */ + bool clipPath(const SkPath& path, SkRegion::Op op); + + /** * Sets the current clip. */ void setClip(float left, float top, float right, float bottom); @@ -142,7 +148,16 @@ public: /** * Returns the current clip in render target coordinates. */ - const Rect& getRenderTargetClip() { return *clipRect; } + const Rect& getRenderTargetClip() { return mClipArea->getClipRect(); } + + /* + * Accessor functions so that the clip area can stay private + */ + bool clipIsEmpty() const { return mClipArea->isEmpty(); } + const Rect& getClipRect() const { return mClipArea->getClipRect(); } + const SkRegion& getClipRegion() const { return mClipArea->getClipRegion(); } + bool clipIsSimple() const { return mClipArea->isSimple(); } + const ClipArea& getClipArea() const { return *mClipArea; } /** * Resets the clip to the specified rect. @@ -156,6 +171,7 @@ public: void initializeViewport(int width, int height) { mViewportData.initialize(width, height); + mClipAreaRoot.setViewportDimensions(width, height); } int getViewportWidth() const { return mViewportData.mWidth; } @@ -175,7 +191,7 @@ public: /** * Indicates whether this snapshot should be ignored. A snapshot - * is typicalled ignored if its layer is invisible or empty. + * is typically ignored if its layer is invisible or empty. */ bool isIgnored() const; @@ -230,24 +246,6 @@ public: mat4* transform; /** - * Current clip rect. The clip is stored in canvas-space coordinates, - * (screen-space coordinates in the regular case.) - * - * This is a reference to a rect owned by this snapshot or another - * snapshot. This pointer must not be freed. See ::mClipRectRoot. - */ - Rect* clipRect; - - /** - * Current clip region. The clip is stored in canvas-space coordinates, - * (screen-space coordinates in the regular case.) - * - * This is a reference to a region owned by this snapshot or another - * snapshot. This pointer must not be freed. See ::mClipRegionRoot. - */ - SkRegion* clipRegion; - - /** * The ancestor layer's dirty region. * * This is a reference to a region owned by a layer. This pointer must @@ -260,7 +258,7 @@ public: * 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 * operations. The value is inherited by child snapshots because the same value should - * be applied to descendents of the current DisplayList (for example, a TextView contains + * be applied to descendants of the current DisplayList (for example, a TextView contains * the base alpha value which should be applied to the child DisplayLists used for drawing * the actual text). */ @@ -298,16 +296,12 @@ private: mat4 mOrthoMatrix; }; - void ensureClipRegion(); - void copyClipRectFromRegion(); - - bool clipRegionOp(float left, float top, float right, float bottom, SkRegion::Op op); - mat4 mTransformRoot; - Rect mClipRectRoot; - Rect mLocalClip; // don't use directly, call getLocalClip() which initializes this - SkRegion mClipRegionRoot; + ClipArea mClipAreaRoot; + ClipArea* mClipArea; + Rect mLocalClip; + ViewportData mViewportData; Vector3 mRelativeLightCenter; diff --git a/libs/hwui/SpotShadow.cpp b/libs/hwui/SpotShadow.cpp index b2dd899..db3c2d9 100644 --- a/libs/hwui/SpotShadow.cpp +++ b/libs/hwui/SpotShadow.cpp @@ -44,6 +44,9 @@ // For each RADIANS_DIVISOR, we would allocate one more vertex b/t the normals. #define SPOT_CORNER_RADIANS_DIVISOR (M_PI / SPOT_EXTRA_CORNER_VERTEX_PER_PI) +// For performance, we use (1 - alpha) value for the shader input. +#define TRANSFORMED_PENUMBRA_ALPHA 1.0f +#define TRANSFORMED_UMBRA_ALPHA 0.0f #include <math.h> #include <stdlib.h> @@ -52,6 +55,7 @@ #include "ShadowTessellator.h" #include "SpotShadow.h" #include "Vertex.h" +#include "VertexBuffer.h" #include "utils/MathUtils.h" // TODO: After we settle down the new algorithm, we can remove the old one and @@ -331,7 +335,7 @@ bool SpotShadow::testPointInsidePolygon(const Vector2 testPoint, * @param len the number of points of the polygon */ void SpotShadow::makeClockwise(Vector2* polygon, int len) { - if (polygon == 0 || len == 0) { + if (polygon == nullptr || len == 0) { return; } if (!ShadowTessellator::isClockwise(polygon, len)) { @@ -797,11 +801,15 @@ inline void genNewPenumbraAndPairWithUmbra(const Vector2* penumbra, int penumbra previousPenumbra * weightForPreviousPenumbra; int skippedUmbraIndex = (previousClosestUmbraIndex + k + 1) % umbraLength; - verticesPair[verticesPairIndex++] = {newPenumbraIndex, skippedUmbraIndex}; + verticesPair[verticesPairIndex].outerIndex = newPenumbraIndex; + verticesPair[verticesPairIndex].innerIndex = skippedUmbraIndex; + verticesPairIndex++; newPenumbra[newPenumbraIndex++] = interpolatedPenumbra; } } - verticesPair[verticesPairIndex++] = {newPenumbraIndex, currentClosestUmbraIndex}; + verticesPair[verticesPairIndex].outerIndex = newPenumbraIndex; + verticesPair[verticesPairIndex].innerIndex = currentClosestUmbraIndex; + verticesPairIndex++; newPenumbra[newPenumbraIndex++] = currentPenumbraVertex; previousClosestUmbraIndex = currentClosestUmbraIndex; @@ -959,11 +967,11 @@ void SpotShadow::generateTriangleStrip(bool isCasterOpaque, float shadowStrength // Fill the IB and VB for the penumbra area. for (int i = 0; i < newPenumbraLength; i++) { AlphaVertex::set(&shadowVertices[vertexBufferIndex++], newPenumbra[i].x, - newPenumbra[i].y, 0.0f); + newPenumbra[i].y, TRANSFORMED_PENUMBRA_ALPHA); } for (int i = 0; i < umbraLength; i++) { AlphaVertex::set(&shadowVertices[vertexBufferIndex++], umbra[i].x, umbra[i].y, - M_PI); + TRANSFORMED_UMBRA_ALPHA); } for (int i = 0; i < verticesPairIndex; i++) { @@ -1003,14 +1011,14 @@ void SpotShadow::generateTriangleStrip(bool isCasterOpaque, float shadowStrength indexBuffer[indexBufferIndex++] = newPenumbraLength + i; indexBuffer[indexBufferIndex++] = vertexBufferIndex; AlphaVertex::set(&shadowVertices[vertexBufferIndex++], - closerVertex.x, closerVertex.y, M_PI); + closerVertex.x, closerVertex.y, TRANSFORMED_UMBRA_ALPHA); } } else { // If there is no occluded umbra at all, then draw the triangle fan // starting from the centroid to all umbra vertices. int lastCentroidIndex = vertexBufferIndex; AlphaVertex::set(&shadowVertices[vertexBufferIndex++], centroid.x, - centroid.y, M_PI); + centroid.y, TRANSFORMED_UMBRA_ALPHA); for (int i = 0; i < umbraLength; i++) { indexBuffer[indexBufferIndex++] = newPenumbraLength + i; indexBuffer[indexBufferIndex++] = lastCentroidIndex; @@ -1026,7 +1034,7 @@ void SpotShadow::generateTriangleStrip(bool isCasterOpaque, float shadowStrength ShadowTessellator::checkOverflow(vertexBufferIndex, totalVertexCount, "Spot Vertex Buffer"); ShadowTessellator::checkOverflow(indexBufferIndex, totalIndexCount, "Spot Index Buffer"); - shadowTriangleStrip.setMode(VertexBuffer::kIndices); + shadowTriangleStrip.setMeshFeatureFlags(VertexBuffer::kAlpha | VertexBuffer::kIndices); shadowTriangleStrip.computeBounds<AlphaVertex>(); } diff --git a/libs/hwui/SpotShadow.h b/libs/hwui/SpotShadow.h index e2d94f7..62a7e5d 100644 --- a/libs/hwui/SpotShadow.h +++ b/libs/hwui/SpotShadow.h @@ -19,11 +19,12 @@ #include "Debug.h" #include "Vector.h" -#include "VertexBuffer.h" namespace android { namespace uirenderer { +class VertexBuffer; + class SpotShadow { public: static void createSpotShadow(bool isCasterOpaque, const Vector3& lightCenter, diff --git a/libs/hwui/StatefulBaseRenderer.h b/libs/hwui/StatefulBaseRenderer.h deleted file mode 100644 index 745e48a..0000000 --- a/libs/hwui/StatefulBaseRenderer.h +++ /dev/null @@ -1,170 +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 ANDROID_HWUI_STATEFUL_BASE_RENDERER_H -#define ANDROID_HWUI_STATEFUL_BASE_RENDERER_H - -#include <utils/RefBase.h> - -#include "Renderer.h" -#include "Snapshot.h" - -namespace android { -namespace uirenderer { - -/** - * Abstract Renderer subclass, which implements Canvas state methods. - * - * Manages the Snapshot stack, implementing matrix, save/restore, and clipping methods in the - * Renderer interface. Drawing and recording classes that extend StatefulBaseRenderer will have - * different use cases: - * - * Drawing subclasses (i.e. OpenGLRenderer) can query attributes (such as transform) or hook into - * changes (e.g. save/restore) with minimal surface area for manipulating the stack itself. - * - * Recording subclasses (i.e. DisplayListRenderer) can both record and pass through state operations - * to StatefulBaseRenderer, so that not only will querying operations work (getClip/Matrix), but so - * that quickRejection can also be used. - */ -class StatefulBaseRenderer : public Renderer { -public: - StatefulBaseRenderer(); - - virtual status_t prepare(bool opaque) { - return prepareDirty(0.0f, 0.0f, mWidth, mHeight, opaque); - } - - /** - * Initialize the first snapshot, computing the projection matrix, and stores the dimensions of - * the render target. - */ - virtual void setViewport(int width, int height); - void initializeSaveStack(float clipLeft, float clipTop, float clipRight, float clipBottom, - const Vector3& lightCenter); - - // getters - bool hasRectToRectTransform() const { - return CC_LIKELY(currentTransform()->rectToRect()); - } - - // Save (layer) - virtual int getSaveCount() const { return mSaveCount; } - virtual int save(int flags); - virtual void restore(); - virtual void restoreToCount(int saveCount); - //virtual int saveLayer(float left, float top, float right, float bottom, - // int alpha, SkXfermode::Mode mode, int flags); - - // Matrix - virtual void getMatrix(SkMatrix* outMatrix) const; - virtual void translate(float dx, float dy, float dz = 0.0f); - virtual void rotate(float degrees); - virtual void scale(float sx, float sy); - virtual void skew(float sx, float sy); - - virtual void setMatrix(const SkMatrix& matrix); - void setMatrix(const Matrix4& matrix); // internal only convenience method - virtual void concatMatrix(const SkMatrix& matrix); - void concatMatrix(const Matrix4& matrix); // internal only convenience method - - // Clip - virtual const Rect& getLocalClipBounds() const { return mSnapshot->getLocalClip(); } - - virtual bool quickRejectConservative(float left, float top, float right, float bottom) const; - - virtual bool clipRect(float left, float top, float right, float bottom, SkRegion::Op op); - virtual bool clipPath(const SkPath* path, SkRegion::Op op); - virtual 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); - - inline const mat4* currentTransform() const { - return mSnapshot->transform; - } - -protected: - const Rect& getRenderTargetClipBounds() const { return mSnapshot->getRenderTargetClip(); } - - int getWidth() { return mWidth; } - int getHeight() { return mHeight; } - - // Save - int saveSnapshot(int flags); - void restoreSnapshot(); - - // allows subclasses to control what value is stored in snapshot's fbo field in - // initializeSaveStack - virtual GLuint getTargetFbo() const { - return -1; - } - - // Clip - bool calculateQuickRejectForScissor(float left, float top, float right, float bottom, - bool* clipRequired, bool* roundRectClipRequired, bool snapOut) const; - - /** - * Called just after a restore has occurred. The 'removed' snapshot popped from the stack, - * 'restored' snapshot has become the top/current. - * - * Subclasses can override this method to handle layer restoration - */ - virtual void onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) {}; - - virtual void onViewportInitialized() {}; - - inline const Rect* currentClipRect() const { - return mSnapshot->clipRect; - } - - inline const Snapshot* currentSnapshot() const { - return mSnapshot != NULL ? mSnapshot.get() : mFirstSnapshot.get(); - } - - inline const Snapshot* firstSnapshot() const { - return mFirstSnapshot.get(); - } - - // indicites that the clip has been changed since the last time it was consumed - bool mDirtyClip; - -private: - // Dimensions of the drawing surface - int mWidth, mHeight; - - // Number of saved states - int mSaveCount; - - // Base state - sp<Snapshot> mFirstSnapshot; - -protected: - // Current state - // TODO: should become private, once hooks needed by OpenGLRenderer are added - sp<Snapshot> mSnapshot; -}; // class StatefulBaseRenderer - -}; // namespace uirenderer -}; // namespace android - -#endif // ANDROID_HWUI_STATEFUL_BASE_RENDERER_H diff --git a/libs/hwui/TessellationCache.cpp b/libs/hwui/TessellationCache.cpp index 1e38f9e..7edb9fb 100644 --- a/libs/hwui/TessellationCache.cpp +++ b/libs/hwui/TessellationCache.cpp @@ -76,7 +76,7 @@ void TessellationCache::Description::setupMatrixAndPaint(Matrix4* matrix, SkPain } TessellationCache::ShadowDescription::ShadowDescription() - : nodeKey(NULL) { + : nodeKey(nullptr) { memset(&matrixData, 0, 16 * sizeof(float)); } @@ -114,7 +114,7 @@ public: : TaskProcessor<VertexBuffer*>(&caches.tasks) {} ~TessellationProcessor() {} - virtual void onProcess(const sp<Task<VertexBuffer*> >& task) { + virtual void onProcess(const sp<Task<VertexBuffer*> >& task) override { TessellationTask* t = static_cast<TessellationTask*>(task.get()); ATRACE_NAME("shape tessellation"); VertexBuffer* buffer = t->tessellator(t->description); @@ -126,7 +126,7 @@ class TessellationCache::Buffer { public: Buffer(const sp<Task<VertexBuffer*> >& task) : mTask(task) - , mBuffer(NULL) { + , mBuffer(nullptr) { } ~Buffer() { @@ -146,9 +146,9 @@ public: private: void blockOnPrecache() { - if (mTask != NULL) { + if (mTask != nullptr) { mBuffer = mTask->getResult(); - LOG_ALWAYS_FATAL_IF(mBuffer == NULL, "Failed to precache"); + LOG_ALWAYS_FATAL_IF(mBuffer == nullptr, "Failed to precache"); mTask.clear(); } } @@ -278,7 +278,7 @@ public: : TaskProcessor<TessellationCache::vertexBuffer_pair_t*>(&caches.tasks) {} ~ShadowProcessor() {} - virtual void onProcess(const sp<Task<TessellationCache::vertexBuffer_pair_t*> >& task) { + virtual void onProcess(const sp<Task<TessellationCache::vertexBuffer_pair_t*> >& task) override { ShadowTask* t = static_cast<ShadowTask*>(task.get()); ATRACE_NAME("shadow tessellation"); @@ -302,7 +302,7 @@ TessellationCache::TessellationCache() , mCache(LruCache<Description, Buffer*>::kUnlimitedCapacity) , mShadowCache(LruCache<ShadowDescription, Task<vertexBuffer_pair_t*>*>::kUnlimitedCapacity) { char property[PROPERTY_VALUE_MAX]; - if (property_get(PROPERTY_VERTEX_CACHE_SIZE, property, NULL) > 0) { + if (property_get(PROPERTY_VERTEX_CACHE_SIZE, property, nullptr) > 0) { INIT_LOGD(" Setting %s cache size to %sMB", name, property); setMaxSize(MB(atof(property))); } else { @@ -380,14 +380,14 @@ void TessellationCache::precacheShadows(const Matrix4* drawTransform, const Rect const Vector3& lightCenter, float lightRadius) { ShadowDescription key(casterPerimeter, drawTransform); + if (mShadowCache.get(key)) return; sp<ShadowTask> task = new ShadowTask(drawTransform, localClip, opaque, casterPerimeter, transformXY, transformZ, lightCenter, lightRadius); - if (mShadowProcessor == NULL) { + if (mShadowProcessor == nullptr) { mShadowProcessor = new ShadowProcessor(Caches::getInstance()); } mShadowProcessor->add(task); - - task->incStrong(NULL); // not using sp<>s, so manually ref while in the cache + task->incStrong(nullptr); // not using sp<>s, so manually ref while in the cache mShadowCache.put(key, task.get()); } @@ -402,7 +402,7 @@ void TessellationCache::getShadowBuffers(const Matrix4* drawTransform, const Rec transformXY, transformZ, lightCenter, lightRadius); task = static_cast<ShadowTask*>(mShadowCache.get(key)); } - LOG_ALWAYS_FATAL_IF(task == NULL, "shadow not precached"); + LOG_ALWAYS_FATAL_IF(task == nullptr, "shadow not precached"); outBuffers = *(task->getResult()); } @@ -418,7 +418,7 @@ TessellationCache::Buffer* TessellationCache::getOrCreateBuffer( sp<TessellationTask> task = new TessellationTask(tessellator, entry); buffer = new Buffer(task); - if (mProcessor == NULL) { + if (mProcessor == nullptr) { mProcessor = new TessellationProcessor(Caches::getInstance()); } mProcessor->add(task); diff --git a/libs/hwui/TessellationCache.h b/libs/hwui/TessellationCache.h index 688a699..3efeaf6 100644 --- a/libs/hwui/TessellationCache.h +++ b/libs/hwui/TessellationCache.h @@ -24,7 +24,6 @@ #include "Debug.h" #include "utils/Macros.h" #include "utils/Pair.h" -#include "VertexBuffer.h" class SkBitmap; class SkCanvas; @@ -36,6 +35,7 @@ namespace android { namespace uirenderer { class Caches; +class VertexBuffer; /////////////////////////////////////////////////////////////////////////////// // Classes @@ -166,7 +166,7 @@ private: sp<TaskProcessor<VertexBuffer*> > mProcessor; LruCache<Description, Buffer*> mCache; class BufferRemovedListener : public OnEntryRemoved<Description, Buffer*> { - void operator()(Description& description, Buffer*& buffer); + void operator()(Description& description, Buffer*& buffer) override; }; BufferRemovedListener mBufferRemovedListener; @@ -178,8 +178,8 @@ private: // holds a pointer, and implicit strong ref to each shadow task of the frame LruCache<ShadowDescription, Task<vertexBuffer_pair_t*>*> mShadowCache; class BufferPairRemovedListener : public OnEntryRemoved<ShadowDescription, Task<vertexBuffer_pair_t*>*> { - void operator()(ShadowDescription& description, Task<vertexBuffer_pair_t*>*& bufferPairTask) { - bufferPairTask->decStrong(NULL); + void operator()(ShadowDescription& description, Task<vertexBuffer_pair_t*>*& bufferPairTask) override { + bufferPairTask->decStrong(nullptr); } }; BufferPairRemovedListener mBufferPairRemovedListener; diff --git a/libs/hwui/TextDropShadowCache.cpp b/libs/hwui/TextDropShadowCache.cpp index 4eec462..c2e88f3 100644 --- a/libs/hwui/TextDropShadowCache.cpp +++ b/libs/hwui/TextDropShadowCache.cpp @@ -20,6 +20,7 @@ #include "Caches.h" #include "Debug.h" +#include "FontRenderer.h" #include "TextDropShadowCache.h" #include "Properties.h" @@ -40,7 +41,8 @@ hash_t ShadowText::hash() const { hash = JenkinsHashMix(hash, android::hash_type(italicStyle)); hash = JenkinsHashMix(hash, android::hash_type(scaleX)); if (text) { - hash = JenkinsHashMixShorts(hash, text, charCount); + hash = JenkinsHashMixShorts( + hash, reinterpret_cast<const uint16_t*>(text), charCount); } if (positions) { for (uint32_t i = 0; i < charCount * 2; i++) { @@ -98,7 +100,7 @@ TextDropShadowCache::TextDropShadowCache(): mCache(LruCache<ShadowText, ShadowTexture*>::kUnlimitedCapacity), mSize(0), mMaxSize(MB(DEFAULT_DROP_SHADOW_CACHE_SIZE)) { char property[PROPERTY_VALUE_MAX]; - if (property_get(PROPERTY_DROP_SHADOW_CACHE_SIZE, property, NULL) > 0) { + if (property_get(PROPERTY_DROP_SHADOW_CACHE_SIZE, property, nullptr) > 0) { INIT_LOGD(" Setting drop shadow cache size to %sMB", property); setMaxSize(MB(atof(property))); } else { @@ -180,7 +182,7 @@ ShadowTexture* TextDropShadowCache::get(const SkPaint* paint, const char* text, len, numGlyphs, radius, positions); if (!shadow.image) { - return NULL; + return nullptr; } Caches& caches = Caches::getInstance(); @@ -205,7 +207,7 @@ ShadowTexture* TextDropShadowCache::get(const SkPaint* paint, const char* text, glGenTextures(1, &texture->id); - caches.bindTexture(texture->id); + caches.textureState().bindTexture(texture->id); // Textures are Alpha8 glPixelStorei(GL_UNPACK_ALIGNMENT, 1); diff --git a/libs/hwui/TextDropShadowCache.h b/libs/hwui/TextDropShadowCache.h index 54b930b..caf089f 100644 --- a/libs/hwui/TextDropShadowCache.h +++ b/libs/hwui/TextDropShadowCache.h @@ -24,17 +24,18 @@ #include <utils/LruCache.h> #include <utils/String16.h> -#include "FontRenderer.h" +#include "font/Font.h" #include "Texture.h" namespace android { namespace uirenderer { class Caches; +class FontRenderer; struct ShadowText { - ShadowText(): len(0), radius(0.0f), textSize(0.0f), typeface(NULL), - flags(0), italicStyle(0.0f), scaleX(0), text(NULL), positions(NULL) { + ShadowText(): len(0), radius(0.0f), textSize(0.0f), typeface(nullptr), + flags(0), italicStyle(0.0f), scaleX(0), text(nullptr), positions(nullptr) { } // len is the number of bytes in text @@ -75,7 +76,7 @@ struct ShadowText { uint32_t charCount = len / sizeof(char16_t); str.setTo((const char16_t*) text, charCount); text = str.string(); - if (positions != NULL) { + if (positions != nullptr) { positionsCopy.clear(); positionsCopy.appendArray(positions, charCount * 2); positions = positionsCopy.array(); @@ -133,7 +134,7 @@ public: * Used as a callback when an entry is removed from the cache. * Do not invoke directly. */ - void operator()(ShadowText& text, ShadowTexture*& texture); + void operator()(ShadowText& text, ShadowTexture*& texture) override; ShadowTexture* get(const SkPaint* paint, const char* text, uint32_t len, int numGlyphs, float radius, const float* positions); diff --git a/libs/hwui/Texture.cpp b/libs/hwui/Texture.cpp index e783905..593e918 100644 --- a/libs/hwui/Texture.cpp +++ b/libs/hwui/Texture.cpp @@ -24,20 +24,6 @@ namespace android { namespace uirenderer { -Texture::Texture(): id(0), generation(0), blend(false), width(0), height(0), - cleanup(false), bitmapSize(0), mipMap(false), uvMapper(NULL), isInUse(false), - mWrapS(GL_CLAMP_TO_EDGE), mWrapT(GL_CLAMP_TO_EDGE), - mMinFilter(GL_NEAREST), mMagFilter(GL_NEAREST), - mFirstFilter(true), mFirstWrap(true), mCaches(Caches::getInstance()) { -} - -Texture::Texture(Caches& caches): id(0), generation(0), blend(false), width(0), height(0), - cleanup(false), bitmapSize(0), mipMap(false), uvMapper(NULL), isInUse(false), - mWrapS(GL_CLAMP_TO_EDGE), mWrapT(GL_CLAMP_TO_EDGE), - mMinFilter(GL_NEAREST), mMagFilter(GL_NEAREST), - mFirstFilter(true), mFirstWrap(true), mCaches(caches) { -} - void Texture::setWrapST(GLenum wrapS, GLenum wrapT, bool bindTexture, bool force, GLenum renderTarget) { @@ -48,7 +34,7 @@ void Texture::setWrapST(GLenum wrapS, GLenum wrapT, bool bindTexture, bool force mWrapT = wrapT; if (bindTexture) { - mCaches.bindTexture(renderTarget, id); + mCaches.textureState().bindTexture(renderTarget, id); } glTexParameteri(renderTarget, GL_TEXTURE_WRAP_S, wrapS); @@ -66,7 +52,7 @@ void Texture::setFilterMinMag(GLenum min, GLenum mag, bool bindTexture, bool for mMagFilter = mag; if (bindTexture) { - mCaches.bindTexture(renderTarget, id); + mCaches.textureState().bindTexture(renderTarget, id); } if (mipMap && min == GL_LINEAR) min = GL_LINEAR_MIPMAP_LINEAR; @@ -77,7 +63,7 @@ void Texture::setFilterMinMag(GLenum min, GLenum mag, bool bindTexture, bool for } void Texture::deleteTexture() const { - mCaches.deleteTexture(id); + mCaches.textureState().deleteTexture(id); } }; // namespace uirenderer diff --git a/libs/hwui/Texture.h b/libs/hwui/Texture.h index d5601f8..7227ce0 100644 --- a/libs/hwui/Texture.h +++ b/libs/hwui/Texture.h @@ -30,8 +30,7 @@ class UvMapper; */ class Texture { public: - Texture(); - Texture(Caches& caches); + Texture(Caches& caches) : mCaches(caches) { } virtual ~Texture() { } @@ -59,62 +58,62 @@ public: /** * Name of the texture. */ - GLuint id; + GLuint id = 0; /** * Generation of the backing bitmap, */ - uint32_t generation; + uint32_t generation = 0; /** * Indicates whether the texture requires blending. */ - bool blend; + bool blend = false; /** * Width of the backing bitmap. */ - uint32_t width; + uint32_t width = 0; /** * Height of the backing bitmap. */ - uint32_t height; + uint32_t height = 0; /** * Indicates whether this texture should be cleaned up after use. */ - bool cleanup; + bool cleanup = false; /** * Optional, size of the original bitmap. */ - uint32_t bitmapSize; + uint32_t bitmapSize = 0; /** * Indicates whether this texture will use trilinear filtering. */ - bool mipMap; + bool mipMap = false; /** * Optional, pointer to a texture coordinates mapper. */ - const UvMapper* uvMapper; + const UvMapper* uvMapper = nullptr; /** * Whether or not the Texture is marked in use and thus not evictable for * the current frame. This is reset at the start of a new frame. */ - bool isInUse; + bool isInUse = false; private: /** - * Last wrap modes set on this texture. Defaults to GL_CLAMP_TO_EDGE. + * Last wrap modes set on this texture. */ - GLenum mWrapS; - GLenum mWrapT; + GLenum mWrapS = GL_CLAMP_TO_EDGE; + GLenum mWrapT = GL_CLAMP_TO_EDGE; /** - * Last filters set on this texture. Defaults to GL_NEAREST. + * Last filters set on this texture. */ - GLenum mMinFilter; - GLenum mMagFilter; + GLenum mMinFilter = GL_NEAREST; + GLenum mMagFilter = GL_NEAREST; - bool mFirstFilter; - bool mFirstWrap; + bool mFirstFilter = true; + bool mFirstWrap = true; Caches& mCaches; }; // struct Texture diff --git a/libs/hwui/TextureCache.cpp b/libs/hwui/TextureCache.cpp index 63454d8..b911a0f 100644 --- a/libs/hwui/TextureCache.cpp +++ b/libs/hwui/TextureCache.cpp @@ -26,6 +26,7 @@ #include "AssetAtlas.h" #include "Caches.h" +#include "Texture.h" #include "TextureCache.h" #include "Properties.h" #include "utils/TraceUtils.h" @@ -37,19 +38,21 @@ namespace uirenderer { // Constructors/destructor /////////////////////////////////////////////////////////////////////////////// -TextureCache::TextureCache(): - mCache(LruCache<uint32_t, Texture*>::kUnlimitedCapacity), - mSize(0), mMaxSize(MB(DEFAULT_TEXTURE_CACHE_SIZE)), - mFlushRate(DEFAULT_TEXTURE_CACHE_FLUSH_RATE), mAssetAtlas(0) { +TextureCache::TextureCache() + : mCache(LruCache<uint32_t, Texture*>::kUnlimitedCapacity) + , mSize(0) + , mMaxSize(MB(DEFAULT_TEXTURE_CACHE_SIZE)) + , mFlushRate(DEFAULT_TEXTURE_CACHE_FLUSH_RATE) + , mAssetAtlas(nullptr) { char property[PROPERTY_VALUE_MAX]; - if (property_get(PROPERTY_TEXTURE_CACHE_SIZE, property, NULL) > 0) { + if (property_get(PROPERTY_TEXTURE_CACHE_SIZE, property, nullptr) > 0) { INIT_LOGD(" Setting texture cache size to %sMB", property); setMaxSize(MB(atof(property))); } else { INIT_LOGD(" Using default texture cache size of %.2fMB", DEFAULT_TEXTURE_CACHE_SIZE); } - if (property_get(PROPERTY_TEXTURE_CACHE_FLUSH_RATE, property, NULL) > 0) { + if (property_get(PROPERTY_TEXTURE_CACHE_FLUSH_RATE, property, nullptr) > 0) { float flushRate = atof(property); INIT_LOGD(" Setting texture cache flush rate to %.2f%%", flushRate * 100.0f); setFlushRate(flushRate); @@ -58,20 +61,6 @@ TextureCache::TextureCache(): DEFAULT_TEXTURE_CACHE_FLUSH_RATE * 100.0f); } - init(); -} - -TextureCache::TextureCache(uint32_t maxByteSize): - mCache(LruCache<uint32_t, Texture*>::kUnlimitedCapacity), - mSize(0), mMaxSize(maxByteSize), mAssetAtlas(0) { - init(); -} - -TextureCache::~TextureCache() { - mCache.clear(); -} - -void TextureCache::init() { mCache.setOnEntryRemovedListener(this); glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize); @@ -80,6 +69,10 @@ void TextureCache::init() { mDebugEnabled = readDebugLevel() & kDebugCaches; } +TextureCache::~TextureCache() { + mCache.clear(); +} + /////////////////////////////////////////////////////////////////////////////// // Size management /////////////////////////////////////////////////////////////////////////////// @@ -159,7 +152,7 @@ Texture* TextureCache::getCachedTexture(const SkBitmap* bitmap) { if (!texture) { if (!canMakeTextureFromBitmap(bitmap)) { - return NULL; + return nullptr; } const uint32_t size = bitmap->rowBytes() * bitmap->height(); @@ -175,7 +168,7 @@ Texture* TextureCache::getCachedTexture(const SkBitmap* bitmap) { } if (canCache) { - texture = new Texture(); + texture = new Texture(Caches::getInstance()); texture->bitmapSize = size; generateTexture(bitmap, texture, false); @@ -209,11 +202,11 @@ Texture* TextureCache::get(const SkBitmap* bitmap) { if (!texture) { if (!canMakeTextureFromBitmap(bitmap)) { - return NULL; + return nullptr; } const uint32_t size = bitmap->rowBytes() * bitmap->height(); - texture = new Texture(); + texture = new Texture(Caches::getInstance()); texture->bitmapSize = size; generateTexture(bitmap, texture, false); texture->cleanup = true; @@ -223,7 +216,7 @@ Texture* TextureCache::get(const SkBitmap* bitmap) { } Texture* TextureCache::getTransient(const SkBitmap* bitmap) { - Texture* texture = new Texture(); + Texture* texture = new Texture(Caches::getInstance()); texture->bitmapSize = bitmap->rowBytes() * bitmap->height(); texture->cleanup = true; @@ -232,11 +225,9 @@ Texture* TextureCache::getTransient(const SkBitmap* bitmap) { return texture; } -void TextureCache::releaseTexture(const SkBitmap* bitmap) { - if (!bitmap || !bitmap->pixelRef()) return; - +void TextureCache::releaseTexture(uint32_t pixelRefStableID) { Mutex::Autolock _l(mLock); - mGarbage.push(bitmap->pixelRef()->getStableID()); + mGarbage.push(pixelRefStableID); } void TextureCache::clearGarbage() { @@ -281,7 +272,7 @@ void TextureCache::generateTexture(const SkBitmap* bitmap, Texture* texture, boo // We could also enable mipmapping if both bitmap dimensions are powers // of 2 but we'd have to deal with size changes. Let's keep this simple - const bool canMipMap = Extensions::getInstance().hasNPot(); + const bool canMipMap = Caches::getInstance().extensions().hasNPot(); // If the texture had mipmap enabled but not anymore, // force a glTexImage2D to discard the mipmap levels @@ -297,23 +288,20 @@ void TextureCache::generateTexture(const SkBitmap* bitmap, Texture* texture, boo texture->width = bitmap->width(); texture->height = bitmap->height(); - Caches::getInstance().bindTexture(texture->id); + Caches::getInstance().textureState().bindTexture(texture->id); switch (bitmap->colorType()) { case kAlpha_8_SkColorType: - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); uploadToTexture(resize, GL_ALPHA, bitmap->rowBytesAsPixels(), bitmap->bytesPerPixel(), texture->width, texture->height, GL_UNSIGNED_BYTE, bitmap->getPixels()); texture->blend = true; break; case kRGB_565_SkColorType: - glPixelStorei(GL_UNPACK_ALIGNMENT, bitmap->bytesPerPixel()); uploadToTexture(resize, GL_RGB, bitmap->rowBytesAsPixels(), bitmap->bytesPerPixel(), texture->width, texture->height, GL_UNSIGNED_SHORT_5_6_5, bitmap->getPixels()); texture->blend = false; break; case kN32_SkColorType: - glPixelStorei(GL_UNPACK_ALIGNMENT, bitmap->bytesPerPixel()); uploadToTexture(resize, GL_RGBA, bitmap->rowBytesAsPixels(), bitmap->bytesPerPixel(), texture->width, texture->height, GL_UNSIGNED_BYTE, bitmap->getPixels()); // Do this after calling getPixels() to make sure Skia's deferred @@ -322,7 +310,6 @@ void TextureCache::generateTexture(const SkBitmap* bitmap, Texture* texture, boo break; case kARGB_4444_SkColorType: case kIndex_8_SkColorType: - glPixelStorei(GL_UNPACK_ALIGNMENT, bitmap->bytesPerPixel()); uploadLoFiTexture(resize, bitmap, texture->width, texture->height); texture->blend = !bitmap->isOpaque(); break; @@ -351,7 +338,7 @@ void TextureCache::uploadLoFiTexture(bool resize, const SkBitmap* bitmap, rgbaBitmap.eraseColor(0); SkCanvas canvas(rgbaBitmap); - canvas.drawBitmap(*bitmap, 0.0f, 0.0f, NULL); + canvas.drawBitmap(*bitmap, 0.0f, 0.0f, nullptr); uploadToTexture(resize, GL_RGBA, rgbaBitmap.rowBytesAsPixels(), rgbaBitmap.bytesPerPixel(), width, height, GL_UNSIGNED_BYTE, rgbaBitmap.getPixels()); @@ -359,7 +346,9 @@ void TextureCache::uploadLoFiTexture(bool resize, const SkBitmap* bitmap, void TextureCache::uploadToTexture(bool resize, GLenum format, GLsizei stride, GLsizei bpp, GLsizei width, GLsizei height, GLenum type, const GLvoid * data) { - const bool useStride = stride != width && Extensions::getInstance().hasUnpackRowLength(); + glPixelStorei(GL_UNPACK_ALIGNMENT, bpp); + const bool useStride = stride != width + && Caches::getInstance().extensions().hasUnpackRowLength(); if ((stride == width) || useStride) { if (useStride) { glPixelStorei(GL_UNPACK_ROW_LENGTH, stride); diff --git a/libs/hwui/TextureCache.h b/libs/hwui/TextureCache.h index cf8d134..a2c6380 100644 --- a/libs/hwui/TextureCache.h +++ b/libs/hwui/TextureCache.h @@ -24,11 +24,12 @@ #include <utils/Vector.h> #include "Debug.h" -#include "Texture.h" namespace android { namespace uirenderer { +class Texture; + /////////////////////////////////////////////////////////////////////////////// // Defines /////////////////////////////////////////////////////////////////////////////// @@ -51,17 +52,16 @@ class AssetAtlas; * Any texture added to the cache causing the cache to grow beyond the maximum * allowed size will also cause the oldest texture to be kicked out. */ -class TextureCache: public OnEntryRemoved<uint32_t, Texture*> { +class TextureCache : public OnEntryRemoved<uint32_t, Texture*> { public: TextureCache(); - TextureCache(uint32_t maxByteSize); ~TextureCache(); /** * Used as a callback when an entry is removed from the cache. * Do not invoke directly. */ - void operator()(uint32_t&, Texture*& texture); + void operator()(uint32_t&, Texture*& texture) override; /** * Resets all Textures to not be marked as in use @@ -87,10 +87,10 @@ public: Texture* getTransient(const SkBitmap* bitmap); /** - * Removes the texture associated with the specified bitmap. This is meant + * Removes the texture associated with the specified pixelRef. This is meant * to be called from threads that are not the EGL context thread. */ - void releaseTexture(const SkBitmap* bitmap); + ANDROID_API void releaseTexture(uint32_t pixelRefStableID); /** * Process deferred removals. */ @@ -145,8 +145,6 @@ private: void uploadToTexture(bool resize, GLenum format, GLsizei stride, GLsizei bpp, GLsizei width, GLsizei height, GLenum type, const GLvoid * data); - void init(); - LruCache<uint32_t, Texture*> mCache; uint32_t mSize; diff --git a/libs/hwui/TreeInfo.h b/libs/hwui/TreeInfo.h index ae6ea94..0799c6c 100644 --- a/libs/hwui/TreeInfo.h +++ b/libs/hwui/TreeInfo.h @@ -20,7 +20,6 @@ #include <utils/Timers.h> -#include "DamageAccumulator.h" #include "utils/Macros.h" namespace android { @@ -30,6 +29,7 @@ namespace renderthread { class CanvasContext; } +class DamageAccumulator; class OpenGLRenderer; class RenderState; @@ -59,11 +59,11 @@ public: : mode(mode) , prepareTextures(mode == MODE_FULL) , runAnimations(true) - , damageAccumulator(NULL) + , damageAccumulator(nullptr) , renderState(renderState) - , renderer(NULL) - , errorHandler(NULL) - , canvasContext(NULL) + , renderer(nullptr) + , errorHandler(nullptr) + , canvasContext(nullptr) {} explicit TreeInfo(TraversalMode mode, const TreeInfo& clone) diff --git a/libs/hwui/Vector.h b/libs/hwui/Vector.h index aa6acc9..7c3f2fd 100644 --- a/libs/hwui/Vector.h +++ b/libs/hwui/Vector.h @@ -17,6 +17,9 @@ #ifndef ANDROID_HWUI_VECTOR_H #define ANDROID_HWUI_VECTOR_H +#include <math.h> +#include <utils/Log.h> + namespace android { namespace uirenderer { diff --git a/libs/hwui/Vertex.h b/libs/hwui/Vertex.h index 4ff0b18..11d0c4b 100644 --- a/libs/hwui/Vertex.h +++ b/libs/hwui/Vertex.h @@ -19,6 +19,8 @@ #include "Vector.h" +#include "utils/Macros.h" + namespace android { namespace uirenderer { @@ -39,8 +41,8 @@ struct Vertex { float x, y; static inline void set(Vertex* vertex, float x, float y) { - vertex[0].x = x; - vertex[0].y = y; + vertex->x = x; + vertex->y = y; } static inline void set(Vertex* vertex, Vector2 val) { @@ -53,6 +55,8 @@ struct Vertex { }; // struct Vertex +REQUIRE_COMPATIBLE_LAYOUT(Vertex); + /** * Simple structure to describe a vertex with a position and texture UV. */ @@ -61,10 +65,7 @@ struct TextureVertex { float u, v; static inline void set(TextureVertex* vertex, float x, float y, float u, float v) { - vertex[0].x = x; - vertex[0].y = y; - vertex[0].u = u; - vertex[0].v = v; + *vertex = { x, y, u, v }; } static inline void setUV(TextureVertex* vertex, float u, float v) { @@ -73,39 +74,43 @@ struct TextureVertex { } }; // struct TextureVertex +REQUIRE_COMPATIBLE_LAYOUT(TextureVertex); + /** * Simple structure to describe a vertex with a position, texture UV and ARGB color. */ -struct ColorTextureVertex : TextureVertex { +struct ColorTextureVertex { + float x, y; + float u, v; float r, g, b, a; static inline void set(ColorTextureVertex* vertex, float x, float y, float u, float v, int color) { - TextureVertex::set(vertex, x, y, u, v); - const float a = ((color >> 24) & 0xff) / 255.0f; - vertex[0].r = a * ((color >> 16) & 0xff) / 255.0f; - vertex[0].g = a * ((color >> 8) & 0xff) / 255.0f; - vertex[0].b = a * ((color ) & 0xff) / 255.0f; - vertex[0].a = a; + 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 }; } }; // struct ColorTextureVertex +REQUIRE_COMPATIBLE_LAYOUT(ColorTextureVertex); + /** * Simple structure to describe a vertex with a position and an alpha value. */ -struct AlphaVertex : Vertex { +struct AlphaVertex { + float x, y; float alpha; static inline void set(AlphaVertex* vertex, float x, float y, float alpha) { - Vertex::set(vertex, x, y); - vertex[0].alpha = alpha; + *vertex = { x, y, alpha }; } static inline void copyWithOffset(AlphaVertex* vertex, const AlphaVertex& src, float x, float y) { - Vertex::set(vertex, src.x + x, src.y + y); - vertex[0].alpha = src.alpha; + AlphaVertex::set(vertex, src.x + x, src.y + y, src.alpha); } static inline void setColor(AlphaVertex* vertex, float alpha) { @@ -113,6 +118,8 @@ struct AlphaVertex : Vertex { } }; // struct AlphaVertex +REQUIRE_COMPATIBLE_LAYOUT(AlphaVertex); + }; // namespace uirenderer }; // namespace android diff --git a/libs/hwui/VertexBuffer.h b/libs/hwui/VertexBuffer.h index 8c3a272..9be4d84 100644 --- a/libs/hwui/VertexBuffer.h +++ b/libs/hwui/VertexBuffer.h @@ -24,25 +24,24 @@ namespace uirenderer { class VertexBuffer { public: - enum Mode { - kStandard = 0, - kOnePolyRingShadow = 1, - kTwoPolyRingShadow = 2, - kIndices = 3 + enum MeshFeatureFlags { + kNone = 0, + kAlpha = 1 << 0, + kIndices = 1 << 1, }; VertexBuffer() - : mBuffer(0) - , mIndices(0) + : mBuffer(nullptr) + , mIndices(nullptr) , mVertexCount(0) , mIndexCount(0) , mAllocatedVertexCount(0) , mAllocatedIndexCount(0) , mByteCount(0) - , mMode(kStandard) - , mReallocBuffer(0) - , mCleanupMethod(NULL) - , mCleanupIndexMethod(NULL) + , mMeshFeatureFlags(kNone) + , mReallocBuffer(nullptr) + , mCleanupMethod(nullptr) + , mCleanupIndexMethod(nullptr) {} ~VertexBuffer() { @@ -135,10 +134,12 @@ public: void updateVertexCount(unsigned int newCount) { mVertexCount = MathUtils::min(newCount, mAllocatedVertexCount); } - Mode getMode() const { return mMode; } + MeshFeatureFlags getMeshFeatureFlags() const { return mMeshFeatureFlags; } + void setMeshFeatureFlags(int flags) { + mMeshFeatureFlags = static_cast<MeshFeatureFlags>(flags); + } void setBounds(Rect bounds) { mBounds = bounds; } - void setMode(Mode mode) { mMode = mode; } template <class TYPE> void createDegenerateSeparators(int allocSize) { @@ -166,7 +167,7 @@ private: unsigned int mAllocatedIndexCount; unsigned int mByteCount; - Mode mMode; + MeshFeatureFlags mMeshFeatureFlags; void* mReallocBuffer; // used for multi-allocation diff --git a/libs/hwui/font/CacheTexture.cpp b/libs/hwui/font/CacheTexture.cpp index 24ffb80..845cf30 100644 --- a/libs/hwui/font/CacheTexture.cpp +++ b/libs/hwui/font/CacheTexture.cpp @@ -17,6 +17,7 @@ #include <SkGlyph.h> #include "CacheTexture.h" +#include "FontUtil.h" #include "../Caches.h" #include "../Debug.h" #include "../Extensions.h" @@ -42,7 +43,7 @@ CacheBlock* CacheBlock::insertBlock(CacheBlock* head, CacheBlock* newBlock) { #endif CacheBlock* currBlock = head; - CacheBlock* prevBlock = NULL; + CacheBlock* prevBlock = nullptr; while (currBlock && currBlock->mY != TEXTURE_BORDER_SIZE) { if (newBlock->mWidth < currBlock->mWidth) { @@ -108,29 +109,33 @@ CacheBlock* CacheBlock::removeBlock(CacheBlock* head, CacheBlock* blockToRemove) // CacheTexture /////////////////////////////////////////////////////////////////////////////// -CacheTexture::CacheTexture(uint16_t width, uint16_t height, GLenum format, uint32_t maxQuadCount) : - mTexture(NULL), mTextureId(0), mWidth(width), mHeight(height), mFormat(format), - mLinearFiltering(false), mDirty(false), mNumGlyphs(0), - mMesh(NULL), mCurrentQuad(0), mMaxQuadCount(maxQuadCount), - mCaches(Caches::getInstance()) { +CacheTexture::CacheTexture(uint16_t width, uint16_t height, GLenum format, uint32_t maxQuadCount) + : mTexture(Caches::getInstance()) + , mFormat(format) + , mMaxQuadCount(maxQuadCount) + , mCaches(Caches::getInstance()) { + mTexture.width = width; + mTexture.height = height; + mTexture.blend = true; + mCacheBlocks = new CacheBlock(TEXTURE_BORDER_SIZE, TEXTURE_BORDER_SIZE, - mWidth - TEXTURE_BORDER_SIZE, mHeight - TEXTURE_BORDER_SIZE); + getWidth() - TEXTURE_BORDER_SIZE, getHeight() - TEXTURE_BORDER_SIZE); // OpenGL ES 3.0+ lets us specify the row length for unpack operations such // as glTexSubImage2D(). This allows us to upload a sub-rectangle of a texture. // With OpenGL ES 2.0 we have to upload entire stripes instead. - mHasUnpackRowLength = Extensions::getInstance().hasUnpackRowLength(); + mHasUnpackRowLength = mCaches.extensions().hasUnpackRowLength(); } CacheTexture::~CacheTexture() { releaseMesh(); - releaseTexture(); + releasePixelBuffer(); reset(); } void CacheTexture::reset() { // Delete existing cache blocks - while (mCacheBlocks != NULL) { + while (mCacheBlocks != nullptr) { CacheBlock* tmpBlock = mCacheBlocks; mCacheBlocks = mCacheBlocks->mNext; delete tmpBlock; @@ -143,35 +148,28 @@ void CacheTexture::init() { // reset, then create a new remainder space to start again reset(); mCacheBlocks = new CacheBlock(TEXTURE_BORDER_SIZE, TEXTURE_BORDER_SIZE, - mWidth - TEXTURE_BORDER_SIZE, mHeight - TEXTURE_BORDER_SIZE); + getWidth() - TEXTURE_BORDER_SIZE, getHeight() - TEXTURE_BORDER_SIZE); } void CacheTexture::releaseMesh() { delete[] mMesh; } -void CacheTexture::releaseTexture() { - if (mTexture) { - delete mTexture; - mTexture = NULL; +void CacheTexture::releasePixelBuffer() { + if (mPixelBuffer) { + delete mPixelBuffer; + mPixelBuffer = nullptr; } - if (mTextureId) { - mCaches.deleteTexture(mTextureId); - mTextureId = 0; + if (mTexture.id) { + mCaches.textureState().deleteTexture(mTexture.id); + mTexture.id = 0; } mDirty = false; mCurrentQuad = 0; } -void CacheTexture::setLinearFiltering(bool linearFiltering, bool bind) { - if (linearFiltering != mLinearFiltering) { - mLinearFiltering = linearFiltering; - - const GLenum filtering = linearFiltering ? GL_LINEAR : GL_NEAREST; - if (bind) mCaches.bindTexture(getTextureId()); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering); - } +void CacheTexture::setLinearFiltering(bool linearFiltering) { + mTexture.setFilter(linearFiltering ? GL_LINEAR : GL_NEAREST); } void CacheTexture::allocateMesh() { @@ -180,19 +178,19 @@ void CacheTexture::allocateMesh() { } } -void CacheTexture::allocateTexture() { - if (!mTexture) { - mTexture = PixelBuffer::create(mFormat, mWidth, mHeight); +void CacheTexture::allocatePixelBuffer() { + if (!mPixelBuffer) { + mPixelBuffer = PixelBuffer::create(mFormat, getWidth(), getHeight()); } - if (!mTextureId) { - glGenTextures(1, &mTextureId); + if (!mTexture.id) { + glGenTextures(1, &mTexture.id); - mCaches.bindTexture(mTextureId); + mCaches.textureState().bindTexture(mTexture.id); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); // Initialize texture dimensions - glTexImage2D(GL_TEXTURE_2D, 0, mFormat, mWidth, mHeight, 0, - mFormat, GL_UNSIGNED_BYTE, 0); + glTexImage2D(GL_TEXTURE_2D, 0, mFormat, getWidth(), getHeight(), 0, + mFormat, GL_UNSIGNED_BYTE, nullptr); const GLenum filtering = getLinearFiltering() ? GL_LINEAR : GL_NEAREST; glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering); @@ -208,16 +206,16 @@ bool CacheTexture::upload() { uint32_t x = mHasUnpackRowLength ? dirtyRect.left : 0; uint32_t y = dirtyRect.top; - uint32_t width = mHasUnpackRowLength ? dirtyRect.getWidth() : mWidth; + uint32_t width = mHasUnpackRowLength ? dirtyRect.getWidth() : getWidth(); uint32_t height = dirtyRect.getHeight(); // The unpack row length only needs to be specified when a new // texture is bound if (mHasUnpackRowLength) { - glPixelStorei(GL_UNPACK_ROW_LENGTH, mWidth); + glPixelStorei(GL_UNPACK_ROW_LENGTH, getWidth()); } - mTexture->upload(x, y, width, height); + mPixelBuffer->upload(x, y, width, height); setDirty(false); return mHasUnpackRowLength; @@ -257,7 +255,7 @@ bool CacheTexture::fitBitmap(const SkGlyph& glyph, uint32_t* retOriginX, uint32_ return false; } - if (glyph.fHeight + TEXTURE_BORDER_SIZE * 2 > mHeight) { + if (glyph.fHeight + TEXTURE_BORDER_SIZE * 2 > getHeight()) { return false; } @@ -294,10 +292,10 @@ bool CacheTexture::fitBitmap(const SkGlyph& glyph, uint32_t* retOriginX, uint32_ cacheBlock->mWidth -= roundedUpW; cacheBlock->mX += roundedUpW; - if (mHeight - glyphH >= glyphH) { + if (getHeight() - glyphH >= glyphH) { // There's enough height left over to create a new CacheBlock CacheBlock* newBlock = new CacheBlock(oldX, glyphH + TEXTURE_BORDER_SIZE, - roundedUpW, mHeight - glyphH - TEXTURE_BORDER_SIZE); + roundedUpW, getHeight() - glyphH - TEXTURE_BORDER_SIZE); #if DEBUG_FONT_RENDERER ALOGD("fitBitmap: Created new block: this, x, y, w, h = %p, %d, %d, %d, %d", newBlock, newBlock->mX, newBlock->mY, diff --git a/libs/hwui/font/CacheTexture.h b/libs/hwui/font/CacheTexture.h index 4cc4f22..6dabc76 100644 --- a/libs/hwui/font/CacheTexture.h +++ b/libs/hwui/font/CacheTexture.h @@ -17,16 +17,15 @@ #ifndef ANDROID_HWUI_CACHE_TEXTURE_H #define ANDROID_HWUI_CACHE_TEXTURE_H -#include <GLES3/gl3.h> +#include "PixelBuffer.h" +#include "Rect.h" +#include "Texture.h" +#include "Vertex.h" +#include <GLES3/gl3.h> #include <SkScalerContext.h> - #include <utils/Log.h> -#include "FontUtil.h" -#include "../PixelBuffer.h" -#include "../Rect.h" -#include "../Vertex.h" namespace android { namespace uirenderer { @@ -55,7 +54,7 @@ struct CacheBlock { CacheBlock* mPrev; CacheBlock(uint16_t x, uint16_t y, uint16_t width, uint16_t height): - mX(x), mY(y), mWidth(width), mHeight(height), mNext(NULL), mPrev(NULL) { + mX(x), mY(y), mWidth(width), mHeight(height), mNext(nullptr), mPrev(nullptr) { } static CacheBlock* insertBlock(CacheBlock* head, CacheBlock* newBlock); @@ -81,9 +80,9 @@ public: void init(); void releaseMesh(); - void releaseTexture(); + void releasePixelBuffer(); - void allocateTexture(); + void allocatePixelBuffer(); void allocateMesh(); // Returns true if glPixelStorei(GL_UNPACK_ROW_LENGTH) must be reset @@ -93,11 +92,11 @@ public: bool fitBitmap(const SkGlyph& glyph, uint32_t* retOriginX, uint32_t* retOriginY); inline uint16_t getWidth() const { - return mWidth; + return mTexture.width; } inline uint16_t getHeight() const { - return mHeight; + return mTexture.height; } inline GLenum getFormat() const { @@ -105,7 +104,7 @@ public: } inline uint32_t getOffset(uint16_t x, uint16_t y) const { - return (y * mWidth + x) * PixelBuffer::formatSize(mFormat); + return (y * getWidth() + x) * PixelBuffer::formatSize(mFormat); } inline const Rect* getDirtyRect() const { @@ -113,12 +112,17 @@ public: } inline PixelBuffer* getPixelBuffer() const { + return mPixelBuffer; + } + + Texture& getTexture() { + allocatePixelBuffer(); return mTexture; } GLuint getTextureId() { - allocateTexture(); - return mTextureId; + allocatePixelBuffer(); + return mTexture.id; } inline bool isDirty() const { @@ -132,7 +136,7 @@ public: /** * This method assumes that the proper texture unit is active. */ - void setLinearFiltering(bool linearFiltering, bool bind = true); + void setLinearFiltering(bool linearFiltering); inline uint16_t getGlyphCount() const { return mNumGlyphs; @@ -147,7 +151,7 @@ public: } uint16_t* indices() const { - return (uint16_t*) 0; + return (uint16_t*) nullptr; } void resetMesh() { @@ -177,16 +181,14 @@ public: private: void setDirty(bool dirty); - PixelBuffer* mTexture; - GLuint mTextureId; - uint16_t mWidth; - uint16_t mHeight; + PixelBuffer* mPixelBuffer = nullptr; + Texture mTexture; GLenum mFormat; - bool mLinearFiltering; - bool mDirty; - uint16_t mNumGlyphs; - TextureVertex* mMesh; - uint32_t mCurrentQuad; + bool mLinearFiltering = false; + bool mDirty = false; + uint16_t mNumGlyphs = 0; + TextureVertex* mMesh = nullptr; + uint32_t mCurrentQuad = 0; uint32_t mMaxQuadCount; Caches& mCaches; CacheBlock* mCacheBlocks; diff --git a/libs/hwui/font/CachedGlyphInfo.h b/libs/hwui/font/CachedGlyphInfo.h index 6680a00..0642d59 100644 --- a/libs/hwui/font/CachedGlyphInfo.h +++ b/libs/hwui/font/CachedGlyphInfo.h @@ -19,11 +19,11 @@ #include <SkFixed.h> -#include "CacheTexture.h" - namespace android { namespace uirenderer { +class CacheTexture; + struct CachedGlyphInfo { // Has the cache been invalidated? bool mIsValid; diff --git a/libs/hwui/font/Font.cpp b/libs/hwui/font/Font.cpp index af39f16..b07a3c8 100644 --- a/libs/hwui/font/Font.cpp +++ b/libs/hwui/font/Font.cpp @@ -22,6 +22,7 @@ #include <utils/JenkinsHash.h> #include <utils/Trace.h> +#include <SkDeviceProperties.h> #include <SkGlyph.h> #include <SkGlyphCache.h> #include <SkUtils.h> @@ -41,9 +42,7 @@ namespace uirenderer { /////////////////////////////////////////////////////////////////////////////// Font::Font(FontRenderer* state, const Font::FontDescription& desc) : - mState(state), mDescription(desc) { - mDeviceProperties = SkDeviceProperties::Make(SkDeviceProperties::Geometry::MakeDefault(), 1.0f); -} + mState(state), mDescription(desc) { } Font::FontDescription::FontDescription(const SkPaint* paint, const SkMatrix& rasterMatrix) : mLookupTransform(rasterMatrix) { @@ -285,7 +284,8 @@ CachedGlyphInfo* Font::getCachedGlyph(const SkPaint* paint, glyph_t textUnit, bo if (cachedGlyph) { // Is the glyph still in texture cache? if (!cachedGlyph->mIsValid) { - SkAutoGlyphCache autoCache(*paint, &mDeviceProperties, &mDescription.mLookupTransform); + SkDeviceProperties deviceProperties(kUnknown_SkPixelGeometry, 1.0f); + SkAutoGlyphCache autoCache(*paint, &deviceProperties, &mDescription.mLookupTransform); const SkGlyph& skiaGlyph = GET_METRICS(autoCache.getCache(), textUnit); updateGlyphCache(paint, skiaGlyph, autoCache.getCache(), cachedGlyph, precaching); } @@ -298,13 +298,13 @@ CachedGlyphInfo* Font::getCachedGlyph(const SkPaint* paint, glyph_t textUnit, bo void Font::render(const SkPaint* paint, const char *text, uint32_t start, uint32_t len, int numGlyphs, int x, int y, const float* positions) { - render(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL, - 0, 0, NULL, positions); + render(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, nullptr, + 0, 0, nullptr, positions); } void Font::render(const SkPaint* paint, const char *text, uint32_t start, uint32_t len, int numGlyphs, const SkPath* path, float hOffset, float vOffset) { - if (numGlyphs == 0 || text == NULL || len == 0) { + if (numGlyphs == 0 || text == nullptr || len == 0) { return; } @@ -354,18 +354,18 @@ void Font::render(const SkPaint* paint, const char *text, uint32_t start, uint32 void Font::measure(const SkPaint* paint, const char* text, uint32_t start, uint32_t len, int numGlyphs, Rect *bounds, const float* positions) { - if (bounds == NULL) { + if (bounds == nullptr) { ALOGE("No return rectangle provided to measure text"); return; } bounds->set(1e6, -1e6, -1e6, 1e6); - render(paint, text, start, len, numGlyphs, 0, 0, MEASURE, NULL, 0, 0, bounds, positions); + render(paint, text, start, len, numGlyphs, 0, 0, MEASURE, nullptr, 0, 0, bounds, positions); } void Font::precache(const SkPaint* paint, const char* text, int numGlyphs) { ATRACE_NAME("Precache Glyphs"); - if (numGlyphs == 0 || text == NULL) { + if (numGlyphs == 0 || text == nullptr) { return; } @@ -378,7 +378,7 @@ void Font::precache(const SkPaint* paint, const char* text, int numGlyphs) { break; } - CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph, true); + getCachedGlyph(paint, glyph, true); glyphsCount++; } } @@ -386,7 +386,7 @@ void Font::precache(const SkPaint* paint, const char* text, int numGlyphs) { void Font::render(const SkPaint* paint, const char* text, uint32_t start, uint32_t len, int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* positions) { - if (numGlyphs == 0 || text == NULL || len == 0) { + if (numGlyphs == 0 || text == nullptr || len == 0) { return; } @@ -403,8 +403,6 @@ void Font::render(const SkPaint* paint, const char* text, uint32_t start, uint32 text += start; int glyphsCount = 0; - const SkPaint::Align align = paint->getTextAlign(); - while (glyphsCount < numGlyphs) { glyph_t glyph = GET_GLYPH(text); @@ -477,7 +475,8 @@ CachedGlyphInfo* Font::cacheGlyph(const SkPaint* paint, glyph_t glyph, bool prec CachedGlyphInfo* newGlyph = new CachedGlyphInfo(); mCachedGlyphs.add(glyph, newGlyph); - SkAutoGlyphCache autoCache(*paint, &mDeviceProperties, &mDescription.mLookupTransform); + SkDeviceProperties deviceProperties(kUnknown_SkPixelGeometry, 1.0f); + SkAutoGlyphCache autoCache(*paint, &deviceProperties, &mDescription.mLookupTransform); const SkGlyph& skiaGlyph = GET_METRICS(autoCache.getCache(), glyph); newGlyph->mIsValid = false; newGlyph->mGlyphIndex = skiaGlyph.fID; diff --git a/libs/hwui/font/Font.h b/libs/hwui/font/Font.h index 0f10464..3119d73 100644 --- a/libs/hwui/font/Font.h +++ b/libs/hwui/font/Font.h @@ -22,13 +22,12 @@ #include <utils/KeyedVector.h> #include <SkScalar.h> -#include <SkDeviceProperties.h> #include <SkGlyphCache.h> #include <SkScalerContext.h> #include <SkPaint.h> #include <SkPathMeasure.h> -#include "CachedGlyphInfo.h" +#include "FontUtil.h" #include "../Rect.h" #include "../Matrix.h" @@ -39,6 +38,8 @@ namespace uirenderer { // Font /////////////////////////////////////////////////////////////////////////////// +struct CachedGlyphInfo; +class CacheTexture; class FontRenderer; /** @@ -119,7 +120,7 @@ private: void measure(const SkPaint* paint, const char* text, uint32_t start, uint32_t len, int numGlyphs, Rect *bounds, const float* positions); - void invalidateTextureCache(CacheTexture* cacheTexture = NULL); + void invalidateTextureCache(CacheTexture* cacheTexture = nullptr); CachedGlyphInfo* cacheGlyph(const SkPaint* paint, glyph_t glyph, bool precaching); void updateGlyphCache(const SkPaint* paint, const SkGlyph& skiaGlyph, @@ -150,7 +151,6 @@ private: DefaultKeyedVector<glyph_t, CachedGlyphInfo*> mCachedGlyphs; bool mIdentityTransform; - SkDeviceProperties mDeviceProperties; }; inline int strictly_order_type(const Font::FontDescription& lhs, diff --git a/libs/hwui/font/FontUtil.h b/libs/hwui/font/FontUtil.h index c2fd5f5..4e5debe 100644 --- a/libs/hwui/font/FontUtil.h +++ b/libs/hwui/font/FontUtil.h @@ -30,9 +30,12 @@ #define DEFAULT_TEXT_LARGE_CACHE_WIDTH 2048 #define DEFAULT_TEXT_LARGE_CACHE_HEIGHT 512 -#define TEXTURE_BORDER_SIZE 1 -#if TEXTURE_BORDER_SIZE != 1 -# error TEXTURE_BORDER_SIZE other than 1 is not currently supported +#ifdef TEXTURE_BORDER_SIZE + #if TEXTURE_BORDER_SIZE != 1 + #error TEXTURE_BORDER_SIZE other than 1 is not currently supported + #endif +#else + #define TEXTURE_BORDER_SIZE 1 #endif #define CACHE_BLOCK_ROUNDING_SIZE 4 @@ -44,7 +47,7 @@ #define GET_GLYPH(text) nextGlyph((const uint16_t**) &text) #define IS_END_OF_STRING(glyph) false - static glyph_t nextGlyph(const uint16_t** srcPtr) { + static inline glyph_t nextGlyph(const uint16_t** srcPtr) { const uint16_t* src = *srcPtr; glyph_t g = *src++; *srcPtr = src; diff --git a/libs/hwui/renderstate/Blend.cpp b/libs/hwui/renderstate/Blend.cpp new file mode 100644 index 0000000..29927ed --- /dev/null +++ b/libs/hwui/renderstate/Blend.cpp @@ -0,0 +1,136 @@ +/* + * 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 <renderstate/Blend.h> +#include "Program.h" + +#include "ShadowTessellator.h" + +namespace android { +namespace uirenderer { + +/** + * Structure mapping Skia xfermodes to OpenGL blending factors. + */ +struct Blender { + SkXfermode::Mode mode; + GLenum src; + GLenum dst; +}; + +// In this array, the index of each Blender equals the value of the first +// entry. For instance, gBlends[1] == gBlends[SkXfermode::kSrc_Mode] +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 } +}; + +// This array contains the swapped version of each SkXfermode. 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 } +}; + +Blend::Blend() + : mEnabled(false) + , mSrcMode(GL_ZERO) + , mDstMode(GL_ZERO) { + // gl blending off by default +} + +void Blend::enable(SkXfermode::Mode mode, ModeOrderSwap modeUsage) { + GLenum srcMode; + GLenum dstMode; + getFactors(mode, modeUsage, &srcMode, &dstMode); + setFactors(srcMode, dstMode); +} + +void Blend::disable() { + if (mEnabled) { + glDisable(GL_BLEND); + mEnabled = false; + } +} + +void Blend::invalidate() { + syncEnabled(); + mSrcMode = mDstMode = GL_ZERO; +} + +void Blend::syncEnabled() { + if (mEnabled) { + glEnable(GL_BLEND); + } else { + glDisable(GL_BLEND); + } +} + +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::setFactors(GLenum srcMode, GLenum dstMode) { + if (srcMode == GL_ZERO && dstMode == GL_ZERO) { + disable(); + } else { + if (!mEnabled) { + glEnable(GL_BLEND); + mEnabled = true; + } + + if (srcMode != mSrcMode || dstMode != mDstMode) { + glBlendFunc(srcMode, dstMode); + mSrcMode = srcMode; + mDstMode = dstMode; + } + } +} + +void Blend::dump() { + ALOGD("Blend: enabled %d, func src %d, dst %d", mEnabled, mSrcMode, mDstMode); +} + +} /* namespace uirenderer */ +} /* namespace android */ + diff --git a/libs/hwui/renderstate/Blend.h b/libs/hwui/renderstate/Blend.h new file mode 100644 index 0000000..dcc681d --- /dev/null +++ b/libs/hwui/renderstate/Blend.h @@ -0,0 +1,58 @@ +/* + * 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. + */ +#ifndef RENDERSTATE_BLEND_H +#define RENDERSTATE_BLEND_H + +#include "Vertex.h" + +#include <GLES2/gl2.h> +#include <GLES2/gl2ext.h> +#include <SkXfermode.h> +#include <memory> + +namespace android { +namespace uirenderer { + +class Blend { + friend class RenderState; +public: + // dictates whether to swap src/dst + enum class ModeOrderSwap { + NoSwap, + Swap, + }; + + void enable(SkXfermode::Mode mode, ModeOrderSwap modeUsage); + void disable(); + void syncEnabled(); + + static void getFactors(SkXfermode::Mode mode, ModeOrderSwap modeUsage, + GLenum* outSrc, GLenum* outDst); + void setFactors(GLenum src, GLenum dst); + + void dump(); +private: + Blend(); + void invalidate(); + bool mEnabled; + GLenum mSrcMode; + GLenum mDstMode; +}; + +} /* namespace uirenderer */ +} /* namespace android */ + +#endif // RENDERSTATE_BLEND_H diff --git a/libs/hwui/renderstate/MeshState.cpp b/libs/hwui/renderstate/MeshState.cpp new file mode 100644 index 0000000..0521f65 --- /dev/null +++ b/libs/hwui/renderstate/MeshState.cpp @@ -0,0 +1,175 @@ +/* + * 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 "renderstate/MeshState.h" + +#include "Program.h" + +#include "ShadowTessellator.h" + +namespace android { +namespace uirenderer { + +MeshState::MeshState() + : mCurrentIndicesBuffer(0) + , mCurrentPixelBuffer(0) + , mCurrentPositionPointer(this) + , mCurrentPositionStride(0) + , mCurrentTexCoordsPointer(this) + , mCurrentTexCoordsStride(0) + , mTexCoordsArrayEnabled(false) + , mQuadListIndices(0) { + glGenBuffers(1, &mUnitQuadBuffer); + glBindBuffer(GL_ARRAY_BUFFER, mUnitQuadBuffer); + glBufferData(GL_ARRAY_BUFFER, sizeof(kUnitQuadVertices), kUnitQuadVertices, GL_STATIC_DRAW); + + mCurrentBuffer = mUnitQuadBuffer; + + uint16_t regionIndices[kMaxNumberOfQuads * 6]; + for (uint32_t i = 0; i < kMaxNumberOfQuads; i++) { + uint16_t quad = i * 4; + int index = i * 6; + regionIndices[index ] = quad; // top-left + regionIndices[index + 1] = quad + 1; // top-right + regionIndices[index + 2] = quad + 2; // bottom-left + regionIndices[index + 3] = quad + 2; // bottom-left + regionIndices[index + 4] = quad + 1; // top-right + regionIndices[index + 5] = quad + 3; // bottom-right + } + glGenBuffers(1, &mQuadListIndices); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mQuadListIndices); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(regionIndices), regionIndices, GL_STATIC_DRAW); + mCurrentIndicesBuffer = mQuadListIndices; + + // position attribute always enabled + glEnableVertexAttribArray(Program::kBindingPosition); +} + +MeshState::~MeshState() { + glDeleteBuffers(1, &mUnitQuadBuffer); + mCurrentBuffer = 0; + + glDeleteBuffers(1, &mQuadListIndices); + mQuadListIndices = 0; +} + +void MeshState::dump() { + ALOGD("MeshState VBOs: unitQuad %d, current %d", mUnitQuadBuffer, mCurrentBuffer); + ALOGD("MeshState IBOs: quadList %d, current %d", mQuadListIndices, mCurrentIndicesBuffer); + ALOGD("MeshState vertices: vertex data %p, stride %d", + mCurrentPositionPointer, mCurrentPositionStride); + ALOGD("MeshState texCoord: data %p, stride %d", + mCurrentTexCoordsPointer, mCurrentTexCoordsStride); +} + +/////////////////////////////////////////////////////////////////////////////// +// Buffer Objects +/////////////////////////////////////////////////////////////////////////////// + +bool MeshState::bindMeshBuffer() { + return bindMeshBuffer(mUnitQuadBuffer); +} + +bool MeshState::bindMeshBuffer(GLuint buffer) { + if (!buffer) buffer = mUnitQuadBuffer; + return bindMeshBufferInternal(buffer); +} + +bool MeshState::unbindMeshBuffer() { + return bindMeshBufferInternal(0); +} + +bool MeshState::bindMeshBufferInternal(GLuint buffer) { + if (mCurrentBuffer != buffer) { + glBindBuffer(GL_ARRAY_BUFFER, buffer); + mCurrentBuffer = buffer; + return true; + } + return false; +} + +/////////////////////////////////////////////////////////////////////////////// +// Vertices +/////////////////////////////////////////////////////////////////////////////// + +void MeshState::bindPositionVertexPointer(bool force, const GLvoid* vertices, GLsizei stride) { + if (force || vertices != mCurrentPositionPointer || stride != mCurrentPositionStride) { + glVertexAttribPointer(Program::kBindingPosition, 2, GL_FLOAT, GL_FALSE, stride, vertices); + mCurrentPositionPointer = vertices; + mCurrentPositionStride = stride; + } +} + +void MeshState::bindTexCoordsVertexPointer(bool force, const GLvoid* vertices, GLsizei stride) { + if (force || vertices != mCurrentTexCoordsPointer || stride != mCurrentTexCoordsStride) { + glVertexAttribPointer(Program::kBindingTexCoords, 2, GL_FLOAT, GL_FALSE, stride, vertices); + mCurrentTexCoordsPointer = vertices; + mCurrentTexCoordsStride = stride; + } +} + +void MeshState::resetVertexPointers() { + mCurrentPositionPointer = this; + mCurrentTexCoordsPointer = this; +} + +void MeshState::resetTexCoordsVertexPointer() { + mCurrentTexCoordsPointer = this; +} + +void MeshState::enableTexCoordsVertexArray() { + if (!mTexCoordsArrayEnabled) { + glEnableVertexAttribArray(Program::kBindingTexCoords); + mCurrentTexCoordsPointer = this; + mTexCoordsArrayEnabled = true; + } +} + +void MeshState::disableTexCoordsVertexArray() { + if (mTexCoordsArrayEnabled) { + glDisableVertexAttribArray(Program::kBindingTexCoords); + mTexCoordsArrayEnabled = false; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// Indices +/////////////////////////////////////////////////////////////////////////////// + +bool MeshState::bindIndicesBufferInternal(const GLuint buffer) { + if (mCurrentIndicesBuffer != buffer) { + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer); + mCurrentIndicesBuffer = buffer; + return true; + } + return false; +} + +bool MeshState::bindQuadIndicesBuffer() { + return bindIndicesBufferInternal(mQuadListIndices); +} + +bool MeshState::unbindIndicesBuffer() { + if (mCurrentIndicesBuffer) { + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + mCurrentIndicesBuffer = 0; + return true; + } + return false; +} + +} /* namespace uirenderer */ +} /* namespace android */ + diff --git a/libs/hwui/renderstate/MeshState.h b/libs/hwui/renderstate/MeshState.h new file mode 100644 index 0000000..e80f4d0 --- /dev/null +++ b/libs/hwui/renderstate/MeshState.h @@ -0,0 +1,144 @@ +/* + * 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. + */ +#ifndef RENDERSTATE_MESHSTATE_H +#define RENDERSTATE_MESHSTATE_H + +#include "Vertex.h" + +#include <GLES2/gl2.h> +#include <GLES2/gl2ext.h> +#include <memory> + +namespace android { +namespace uirenderer { + +class Program; + +// Maximum number of quads that pre-allocated meshes can draw +const uint32_t kMaxNumberOfQuads = 2048; + +// This array is never used directly but used as a memcpy source in the +// OpenGLRenderer constructor +const TextureVertex kUnitQuadVertices[] = { + { 0, 0, 0, 0 }, + { 1, 0, 1, 0 }, + { 0, 1, 0, 1 }, + { 1, 1, 1, 1 }, +}; + +const GLsizei kVertexStride = sizeof(Vertex); +const GLsizei kAlphaVertexStride = sizeof(AlphaVertex); +const GLsizei kTextureVertexStride = sizeof(TextureVertex); +const GLsizei kColorTextureVertexStride = sizeof(ColorTextureVertex); + +const GLsizei kMeshTextureOffset = 2 * sizeof(float); +const GLsizei kVertexAlphaOffset = 2 * sizeof(float); +const GLsizei kVertexAAWidthOffset = 2 * sizeof(float); +const GLsizei kVertexAALengthOffset = 3 * sizeof(float); +const GLsizei kUnitQuadCount = 4; + +class MeshState { +private: + friend class RenderState; + +public: + ~MeshState(); + void dump(); + /////////////////////////////////////////////////////////////////////////////// + // Buffer objects + /////////////////////////////////////////////////////////////////////////////// + /** + * Binds the VBO used to render simple textured quads. + */ + bool bindMeshBuffer(); + + /** + * Binds the specified VBO if needed. If buffer == 0, binds default simple textured quad. + */ + bool bindMeshBuffer(GLuint buffer); + + /** + * Unbinds the VBO used to render simple textured quads. + */ + bool unbindMeshBuffer(); + + /////////////////////////////////////////////////////////////////////////////// + // Vertices + /////////////////////////////////////////////////////////////////////////////// + /** + * Binds an attrib to the specified float vertex pointer. + * Assumes a stride of gTextureVertexStride and a size of 2. + */ + void bindPositionVertexPointer(bool force, const GLvoid* vertices, + GLsizei stride = kTextureVertexStride); + + /** + * Binds an attrib to the specified float vertex pointer. + * Assumes a stride of gTextureVertexStride and a size of 2. + */ + void bindTexCoordsVertexPointer(bool force, const GLvoid* vertices, + GLsizei stride = kTextureVertexStride); + + /** + * Resets the vertex pointers. + */ + void resetVertexPointers(); + void resetTexCoordsVertexPointer(); + + void enableTexCoordsVertexArray(); + void disableTexCoordsVertexArray(); + + /////////////////////////////////////////////////////////////////////////////// + // Indices + /////////////////////////////////////////////////////////////////////////////// + /** + * Binds a global indices buffer that can draw up to + * gMaxNumberOfQuads quads. + */ + bool bindQuadIndicesBuffer(); + bool unbindIndicesBuffer(); + + /////////////////////////////////////////////////////////////////////////////// + // Getters - for use in Glop building + /////////////////////////////////////////////////////////////////////////////// + GLuint getUnitQuadVBO() { return mUnitQuadBuffer; } + GLuint getQuadListIBO() { return mQuadListIndices; } +private: + MeshState(); + bool bindMeshBufferInternal(const GLuint buffer); + bool bindIndicesBufferInternal(const GLuint buffer); + + GLuint mUnitQuadBuffer; + + GLuint mCurrentBuffer; + GLuint mCurrentIndicesBuffer; + GLuint mCurrentPixelBuffer; + + const void* mCurrentPositionPointer; + GLsizei mCurrentPositionStride; + const void* mCurrentTexCoordsPointer; + GLsizei mCurrentTexCoordsStride; + + bool mTexCoordsArrayEnabled; + + // Global index buffer + GLuint mQuadListIndices; +}; + +} /* namespace uirenderer */ +} /* namespace android */ + +#endif // RENDERSTATE_MESHSTATE_H diff --git a/libs/hwui/renderstate/PixelBufferState.cpp b/libs/hwui/renderstate/PixelBufferState.cpp new file mode 100644 index 0000000..c23af52 --- /dev/null +++ b/libs/hwui/renderstate/PixelBufferState.cpp @@ -0,0 +1,45 @@ +/* + * 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 "renderstate/PixelBufferState.h" + +namespace android { +namespace uirenderer { + +PixelBufferState::PixelBufferState() + : mCurrentPixelBuffer(0) { +} + +bool PixelBufferState::bind(GLuint buffer) { + if (mCurrentPixelBuffer != buffer) { + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, buffer); + mCurrentPixelBuffer = buffer; + return true; + } + return false; +} + +bool PixelBufferState::unbind() { + if (mCurrentPixelBuffer) { + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); + mCurrentPixelBuffer = 0; + return true; + } + return false; +} + +} /* namespace uirenderer */ +} /* namespace android */ + diff --git a/libs/hwui/renderstate/PixelBufferState.h b/libs/hwui/renderstate/PixelBufferState.h new file mode 100644 index 0000000..8dab21d --- /dev/null +++ b/libs/hwui/renderstate/PixelBufferState.h @@ -0,0 +1,37 @@ +/* + * 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. + */ +#ifndef RENDERSTATE_PIXELBUFFERSTATE_H +#define RENDERSTATE_PIXELBUFFERSTATE_H + +#include <GLES3/gl3.h> + +namespace android { +namespace uirenderer { + +class PixelBufferState { + friend class Caches; // TODO: move to RenderState +public: + bool bind(GLuint buffer); + bool unbind(); +private: + PixelBufferState(); + GLuint mCurrentPixelBuffer; +}; + +} /* namespace uirenderer */ +} /* namespace android */ + +#endif // RENDERSTATE_PIXELBUFFERSTATE_H diff --git a/libs/hwui/renderstate/RenderState.cpp b/libs/hwui/renderstate/RenderState.cpp new file mode 100644 index 0000000..7b44d6d --- /dev/null +++ b/libs/hwui/renderstate/RenderState.cpp @@ -0,0 +1,354 @@ +/* + * 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. + */ +#include "renderstate/RenderState.h" + +#include "renderthread/CanvasContext.h" +#include "renderthread/EglManager.h" +#include "utils/GLUtils.h" + +namespace android { +namespace uirenderer { + +RenderState::RenderState(renderthread::RenderThread& thread) + : mRenderThread(thread) + , mViewportWidth(0) + , mViewportHeight(0) + , mFramebuffer(0) { + mThreadId = pthread_self(); +} + +RenderState::~RenderState() { + LOG_ALWAYS_FATAL_IF(mBlend || mMeshState || mScissor || mStencil, + "State object lifecycle not managed correctly"); +} + +void RenderState::onGLContextCreated() { + LOG_ALWAYS_FATAL_IF(mBlend || mMeshState || mScissor || mStencil, + "State object lifecycle not managed correctly"); + mBlend = new Blend(); + mMeshState = new MeshState(); + mScissor = new Scissor(); + mStencil = new Stencil(); + + // This is delayed because the first access of Caches makes GL calls + if (!mCaches) { + mCaches = &Caches::createInstance(*this); + } + mCaches->init(); + mCaches->textureCache.setAssetAtlas(&mAssetAtlas); +} + +static void layerLostGlContext(Layer* layer) { + layer->onGlContextLost(); +} + +void RenderState::onGLContextDestroyed() { +/* + size_t size = mActiveLayers.size(); + if (CC_UNLIKELY(size != 0)) { + ALOGE("Crashing, have %d contexts and %d layers at context destruction. isempty %d", + mRegisteredContexts.size(), size, mActiveLayers.empty()); + mCaches->dumpMemoryUsage(); + for (std::set<renderthread::CanvasContext*>::iterator cit = mRegisteredContexts.begin(); + cit != mRegisteredContexts.end(); cit++) { + renderthread::CanvasContext* context = *cit; + ALOGE("Context: %p (root = %p)", context, context->mRootRenderNode.get()); + ALOGE(" Prefeteched layers: %zu", context->mPrefetechedLayers.size()); + for (std::set<RenderNode*>::iterator pit = context->mPrefetechedLayers.begin(); + pit != context->mPrefetechedLayers.end(); pit++) { + (*pit)->debugDumpLayers(" "); + } + context->mRootRenderNode->debugDumpLayers(" "); + } + + + if (mActiveLayers.begin() == mActiveLayers.end()) { + ALOGE("set has become empty. wat."); + } + for (std::set<const Layer*>::iterator lit = mActiveLayers.begin(); + lit != mActiveLayers.end(); lit++) { + const Layer* layer = *(lit); + ALOGE("Layer %p, state %d, texlayer %d, fbo %d, buildlayered %d", + layer, layer->state, layer->isTextureLayer(), layer->getFbo(), layer->wasBuildLayered); + } + LOG_ALWAYS_FATAL("%d layers have survived gl context destruction", size); + } +*/ + + // TODO: reset all cached state in state objects + std::for_each(mActiveLayers.begin(), mActiveLayers.end(), layerLostGlContext); + mAssetAtlas.terminate(); + + mCaches->terminate(); + + delete mBlend; + mBlend = nullptr; + delete mMeshState; + mMeshState = nullptr; + delete mScissor; + mScissor = nullptr; + delete mStencil; + mStencil = nullptr; +} + +void RenderState::setViewport(GLsizei width, GLsizei height) { + mViewportWidth = width; + mViewportHeight = height; + glViewport(0, 0, mViewportWidth, mViewportHeight); +} + + +void RenderState::getViewport(GLsizei* outWidth, GLsizei* outHeight) { + *outWidth = mViewportWidth; + *outHeight = mViewportHeight; +} + +void RenderState::bindFramebuffer(GLuint fbo) { + if (mFramebuffer != fbo) { + mFramebuffer = fbo; + glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer); + } +} + +void RenderState::invokeFunctor(Functor* functor, DrawGlInfo::Mode mode, DrawGlInfo* info) { + interruptForFunctorInvoke(); + (*functor)(mode, info); + resumeFromFunctorInvoke(); +} + +void RenderState::interruptForFunctorInvoke() { + mCaches->setProgram(nullptr); + mCaches->textureState().resetActiveTexture(); + meshState().unbindMeshBuffer(); + meshState().unbindIndicesBuffer(); + meshState().resetVertexPointers(); + meshState().disableTexCoordsVertexArray(); + debugOverdraw(false, false); +} + +void RenderState::resumeFromFunctorInvoke() { + glViewport(0, 0, mViewportWidth, mViewportHeight); + glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer); + debugOverdraw(false, false); + + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + + scissor().invalidate(); + blend().invalidate(); + + mCaches->textureState().activateTexture(0); + mCaches->textureState().resetBoundTextures(); +} + +void RenderState::debugOverdraw(bool enable, bool clear) { + if (mCaches->debugOverdraw && mFramebuffer == 0) { + if (clear) { + scissor().setEnabled(false); + stencil().clear(); + } + if (enable) { + stencil().enableDebugWrite(); + } else { + stencil().disable(); + } + } +} + +void RenderState::requireGLContext() { + assertOnGLThread(); + mRenderThread.eglManager().requireGlContext(); +} + +void RenderState::assertOnGLThread() { + pthread_t curr = pthread_self(); + LOG_ALWAYS_FATAL_IF(!pthread_equal(mThreadId, curr), "Wrong thread!"); +} + +class DecStrongTask : public renderthread::RenderTask { +public: + DecStrongTask(VirtualLightRefBase* object) : mObject(object) {} + + virtual void run() override { + mObject->decStrong(nullptr); + mObject = nullptr; + delete this; + } + +private: + VirtualLightRefBase* mObject; +}; + +void RenderState::postDecStrong(VirtualLightRefBase* object) { + mRenderThread.queue(new DecStrongTask(object)); +} + +/////////////////////////////////////////////////////////////////////////////// +// Render +/////////////////////////////////////////////////////////////////////////////// + +void RenderState::render(const Glop& glop) { + const Glop::Mesh& mesh = glop.mesh; + const Glop::Mesh::Vertices& vertices = mesh.vertices; + const Glop::Mesh::Indices& indices = mesh.indices; + const Glop::Fill& fill = glop.fill; + + // --------------------------------------------- + // ---------- Program + uniform setup ---------- + // --------------------------------------------- + mCaches->setProgram(fill.program); + + if (fill.colorEnabled) { + fill.program->setColor(fill.color); + } + + fill.program->set(glop.transform.ortho, + glop.transform.modelView, + glop.transform.canvas, + glop.transform.fudgingOffset); + + // Color filter uniforms + if (fill.filterMode == ProgramDescription::kColorBlend) { + const FloatColor& color = fill.filter.color; + glUniform4f(mCaches->program().getUniform("colorBlend"), + color.r, color.g, color.b, color.a); + } else if (fill.filterMode == ProgramDescription::kColorMatrix) { + glUniformMatrix4fv(mCaches->program().getUniform("colorMatrix"), 1, GL_FALSE, + fill.filter.matrix.matrix); + glUniform4fv(mCaches->program().getUniform("colorMatrixVector"), 1, + fill.filter.matrix.vector); + } + + // Round rect clipping uniforms + if (glop.roundRectClipState) { + // TODO: avoid query, and cache values (or RRCS ptr) in program + const RoundRectClipState* state = glop.roundRectClipState; + const Rect& innerRect = state->innerRect; + glUniform4f(fill.program->getUniform("roundRectInnerRectLTRB"), + innerRect.left, innerRect.top, + innerRect.right, innerRect.bottom); + glUniformMatrix4fv(fill.program->getUniform("roundRectInvTransform"), + 1, GL_FALSE, &state->matrix.data[0]); + + // add half pixel to round out integer rect space to cover pixel centers + float roundedOutRadius = state->radius + 0.5f; + glUniform1f(fill.program->getUniform("roundRectRadius"), + roundedOutRadius); + } + + // -------------------------------- + // ---------- Mesh setup ---------- + // -------------------------------- + // vertices + const bool force = meshState().bindMeshBufferInternal(vertices.bufferObject) + || (vertices.position != nullptr); + meshState().bindPositionVertexPointer(force, vertices.position, vertices.stride); + + // indices + meshState().bindIndicesBufferInternal(indices.bufferObject); + + if (vertices.attribFlags & VertexAttribFlags::kTextureCoord) { + const Glop::Fill::TextureData& texture = fill.texture; + // texture always takes slot 0, shader samplers increment from there + mCaches->textureState().activateTexture(0); + + if (texture.clamp != GL_INVALID_ENUM) { + texture.texture->setWrap(texture.clamp, true); + } + if (texture.filter != GL_INVALID_ENUM) { + texture.texture->setFilter(texture.filter, true); + } + + mCaches->textureState().bindTexture(texture.target, texture.texture->id); + meshState().enableTexCoordsVertexArray(); + meshState().bindTexCoordsVertexPointer(force, vertices.texCoord, vertices.stride); + + if (texture.textureTransform) { + glUniformMatrix4fv(fill.program->getUniform("mainTextureTransform"), 1, + GL_FALSE, &texture.textureTransform->data[0]); + } + } else { + meshState().disableTexCoordsVertexArray(); + } + int colorLocation = -1; + if (vertices.attribFlags & VertexAttribFlags::kColor) { + colorLocation = fill.program->getAttrib("colors"); + glEnableVertexAttribArray(colorLocation); + glVertexAttribPointer(colorLocation, 4, GL_FLOAT, GL_FALSE, vertices.stride, vertices.color); + } + int alphaLocation = -1; + if (vertices.attribFlags & VertexAttribFlags::kAlpha) { + // NOTE: alpha vertex position is computed assuming no VBO + const void* alphaCoords = ((const GLbyte*) vertices.position) + kVertexAlphaOffset; + alphaLocation = fill.program->getAttrib("vtxAlpha"); + glEnableVertexAttribArray(alphaLocation); + glVertexAttribPointer(alphaLocation, 1, GL_FLOAT, GL_FALSE, vertices.stride, alphaCoords); + } + // Shader uniforms + SkiaShader::apply(*mCaches, fill.skiaShaderData); + + // ------------------------------------ + // ---------- GL state setup ---------- + // ------------------------------------ + blend().setFactors(glop.blend.src, glop.blend.dst); + + // ------------------------------------ + // ---------- Actual drawing ---------- + // ------------------------------------ + if (indices.bufferObject == meshState().getQuadListIBO()) { + // Since the indexed quad list is of limited length, we loop over + // the glDrawXXX method while updating the vertex pointer + GLsizei elementsCount = mesh.elementCount; + const GLbyte* vertexData = static_cast<const GLbyte*>(vertices.position); + while (elementsCount > 0) { + GLsizei drawCount = MathUtils::min(elementsCount, (GLsizei) kMaxNumberOfQuads * 6); + + // rebind pointers without forcing, since initial bind handled above + meshState().bindPositionVertexPointer(false, vertexData, vertices.stride); + if (vertices.attribFlags & VertexAttribFlags::kTextureCoord) { + meshState().bindTexCoordsVertexPointer(false, + vertexData + kMeshTextureOffset, vertices.stride); + } + + glDrawElements(mesh.primitiveMode, drawCount, GL_UNSIGNED_SHORT, nullptr); + elementsCount -= drawCount; + vertexData += (drawCount / 6) * 4 * vertices.stride; + } + } else if (indices.bufferObject || indices.indices) { + glDrawElements(mesh.primitiveMode, mesh.elementCount, GL_UNSIGNED_SHORT, indices.indices); + } else { + glDrawArrays(mesh.primitiveMode, 0, mesh.elementCount); + } + + // ----------------------------------- + // ---------- Mesh teardown ---------- + // ----------------------------------- + if (vertices.attribFlags & VertexAttribFlags::kAlpha) { + glDisableVertexAttribArray(alphaLocation); + } + if (vertices.attribFlags & VertexAttribFlags::kColor) { + glDisableVertexAttribArray(colorLocation); + } +} + +void RenderState::dump() { + blend().dump(); + meshState().dump(); + scissor().dump(); + stencil().dump(); +} + +} /* namespace uirenderer */ +} /* namespace android */ diff --git a/libs/hwui/RenderState.h b/libs/hwui/renderstate/RenderState.h index 9ac9356..4fd792c 100644 --- a/libs/hwui/RenderState.h +++ b/libs/hwui/renderstate/RenderState.h @@ -20,16 +20,26 @@ #include <GLES2/gl2.h> #include <GLES2/gl2ext.h> #include <utils/Mutex.h> - +#include <utils/Functor.h> +#include <utils/RefBase.h> #include <private/hwui/DrawGlInfo.h> +#include <renderstate/Blend.h> #include "AssetAtlas.h" #include "Caches.h" +#include "Glop.h" +#include "renderstate/MeshState.h" +#include "renderstate/PixelBufferState.h" +#include "renderstate/Scissor.h" +#include "renderstate/Stencil.h" #include "utils/Macros.h" namespace android { namespace uirenderer { +class Caches; +class Layer; + namespace renderthread { class CanvasContext; class RenderThread; @@ -74,8 +84,15 @@ public: // more thinking... void postDecStrong(VirtualLightRefBase* object); + void render(const Glop& glop); + AssetAtlas& assetAtlas() { return mAssetAtlas; } + Blend& blend() { return *mBlend; } + MeshState& meshState() { return *mMeshState; } + Scissor& scissor() { return *mScissor; } + Stencil& stencil() { return *mStencil; } + void dump(); private: friend class renderthread::RenderThread; friend class Caches; @@ -87,8 +104,15 @@ private: RenderState(renderthread::RenderThread& thread); ~RenderState(); + renderthread::RenderThread& mRenderThread; - Caches* mCaches; + Caches* mCaches = nullptr; + + Blend* mBlend = nullptr; + MeshState* mMeshState = nullptr; + Scissor* mScissor = nullptr; + Stencil* mStencil = nullptr; + AssetAtlas mAssetAtlas; std::set<Layer*> mActiveLayers; std::set<renderthread::CanvasContext*> mRegisteredContexts; diff --git a/libs/hwui/renderstate/Scissor.cpp b/libs/hwui/renderstate/Scissor.cpp new file mode 100644 index 0000000..95dcd18 --- /dev/null +++ b/libs/hwui/renderstate/Scissor.cpp @@ -0,0 +1,91 @@ +/* + * 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 "renderstate/Scissor.h" + +#include <utils/Log.h> + +namespace android { +namespace uirenderer { + +Scissor::Scissor() + : mEnabled(false) + , mScissorX(0) + , mScissorY(0) + , mScissorWidth(0) + , mScissorHeight(0) { +} + +bool Scissor::setEnabled(bool enabled) { + if (mEnabled != enabled) { + if (enabled) { + glEnable(GL_SCISSOR_TEST); + } else { + glDisable(GL_SCISSOR_TEST); + } + mEnabled = enabled; + return true; + } + return false; +} + +bool Scissor::set(GLint x, GLint y, GLint width, GLint height) { + if (mEnabled && (x != mScissorX || y != mScissorY + || width != mScissorWidth || height != mScissorHeight)) { + + if (x < 0) { + width += x; + x = 0; + } + if (y < 0) { + height += y; + y = 0; + } + if (width < 0) { + width = 0; + } + if (height < 0) { + height = 0; + } + glScissor(x, y, width, height); + + mScissorX = x; + mScissorY = y; + mScissorWidth = width; + mScissorHeight = height; + + return true; + } + return false; +} + +void Scissor::reset() { + mScissorX = mScissorY = mScissorWidth = mScissorHeight = 0; +} + +void Scissor::invalidate() { + mEnabled = glIsEnabled(GL_SCISSOR_TEST); + setEnabled(true); + reset(); +} + +void Scissor::dump() { + ALOGD("Scissor: enabled %d, %d %d %d %d", + mEnabled, mScissorX, mScissorY, mScissorWidth, mScissorHeight); +} + +} /* namespace uirenderer */ +} /* namespace android */ + diff --git a/libs/hwui/renderstate/Scissor.h b/libs/hwui/renderstate/Scissor.h new file mode 100644 index 0000000..b37ec58 --- /dev/null +++ b/libs/hwui/renderstate/Scissor.h @@ -0,0 +1,46 @@ +/* + * 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. + */ +#ifndef RENDERSTATE_SCISSOR_H +#define RENDERSTATE_SCISSOR_H + +#include <GLES2/gl2.h> +#include <GLES2/gl2ext.h> + +namespace android { +namespace uirenderer { + +class Scissor { + friend class RenderState; +public: + bool setEnabled(bool enabled); + bool set(GLint x, GLint y, GLint width, GLint height); + void reset(); + bool isEnabled() { return mEnabled; } + void dump(); +private: + Scissor(); + void invalidate(); + bool mEnabled; + GLint mScissorX; + GLint mScissorY; + GLint mScissorWidth; + GLint mScissorHeight; +}; + +} /* namespace uirenderer */ +} /* namespace android */ + +#endif // RENDERSTATE_SCISSOR_H diff --git a/libs/hwui/Stencil.cpp b/libs/hwui/renderstate/Stencil.cpp index 8ce57db..cedb233 100644 --- a/libs/hwui/Stencil.cpp +++ b/libs/hwui/renderstate/Stencil.cpp @@ -14,10 +14,12 @@ * limitations under the License. */ +#include "renderstate/Stencil.h" + +#include "Caches.h" #include "Debug.h" #include "Extensions.h" #include "Properties.h" -#include "Stencil.h" #include <GLES2/gl2ext.h> @@ -32,7 +34,8 @@ namespace uirenderer { #define STENCIL_MASK_VALUE 0x1 #endif -Stencil::Stencil(): mState(kDisabled) { +Stencil::Stencil() + : mState(kDisabled) { } uint8_t Stencil::getStencilSize() { @@ -41,7 +44,7 @@ uint8_t Stencil::getStencilSize() { GLenum Stencil::getSmallestStencilFormat() { #if !DEBUG_STENCIL - const Extensions& extensions = Extensions::getInstance(); + const Extensions& extensions = Caches::getInstance().extensions(); if (extensions.has1BitStencil()) { return GL_STENCIL_INDEX1_OES; } else if (extensions.has4BitStencil()) { @@ -56,24 +59,36 @@ void Stencil::clear() { glClear(GL_STENCIL_BUFFER_BIT); } -void Stencil::enableTest() { +void Stencil::enableTest(int incrementThreshold) { if (mState != kTest) { enable(); - glStencilFunc(GL_EQUAL, STENCIL_WRITE_VALUE, STENCIL_MASK_VALUE); + if (incrementThreshold > 0) { + glStencilFunc(GL_EQUAL, incrementThreshold, 0xff); + } else { + glStencilFunc(GL_EQUAL, STENCIL_WRITE_VALUE, STENCIL_MASK_VALUE); + } // We only want to test, let's keep everything glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + glStencilMask(0); mState = kTest; } } -void Stencil::enableWrite() { +void Stencil::enableWrite(int incrementThreshold) { if (mState != kWrite) { enable(); - glStencilFunc(GL_ALWAYS, STENCIL_WRITE_VALUE, STENCIL_MASK_VALUE); - // The test always passes so the first two values are meaningless - glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); + if (incrementThreshold > 0) { + glStencilFunc(GL_ALWAYS, 1, 0xff); + // The test always passes so the first two values are meaningless + glStencilOp(GL_INCR, GL_INCR, GL_INCR); + } else { + glStencilFunc(GL_ALWAYS, STENCIL_WRITE_VALUE, STENCIL_MASK_VALUE); + // The test always passes so the first two values are meaningless + glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); + } glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); + glStencilMask(0xff); mState = kWrite; } } @@ -110,5 +125,9 @@ void Stencil::disable() { } } +void Stencil::dump() { + ALOGD("Stencil: state %d", mState); +} + }; // namespace uirenderer }; // namespace android diff --git a/libs/hwui/Stencil.h b/libs/hwui/renderstate/Stencil.h index c5e5186..e4f0f3f 100644 --- a/libs/hwui/Stencil.h +++ b/libs/hwui/renderstate/Stencil.h @@ -53,17 +53,21 @@ public: void clear(); /** - * Enables stencil test. When the stencil test is enabled the stencil - * buffer is not written into. + * Enables stencil test. When the stencil test is enabled the stencil buffer is not written + * into. An increment threshold of zero causes the stencil to use a constant reference value + * and GL_EQUAL for the test. A non-zero increment threshold causes the stencil to use that + * value as the reference value and GL_EQUAL for the test. */ - void enableTest(); + void enableTest(int incrementThreshold); /** * Enables stencil write. When stencil write is enabled, the stencil * test always succeeds and the value 0x1 is written in the stencil - * buffer for each fragment. + * buffer for each fragment. An increment threshold of zero causes the stencil to use a constant + * reference value and GL_EQUAL for the test. A non-zero increment threshold causes the stencil + * to use that value as the reference value and GL_EQUAL for the test. */ - void enableWrite(); + void enableWrite(int incrementThreshold); /** * The test passes only when equal to the specified value. @@ -94,6 +98,12 @@ public: return mState == kTest; } + bool isWriteEnabled() { + return mState == kWrite; + } + + void dump(); + private: void enable(); diff --git a/libs/hwui/renderstate/TextureState.cpp b/libs/hwui/renderstate/TextureState.cpp new file mode 100644 index 0000000..a211de7 --- /dev/null +++ b/libs/hwui/renderstate/TextureState.cpp @@ -0,0 +1,103 @@ +/* + * 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 "renderstate/TextureState.h" + +namespace android { +namespace uirenderer { + +// Must define as many texture units as specified by kTextureUnitsCount +const GLenum kTextureUnits[] = { + GL_TEXTURE0, + GL_TEXTURE1, + GL_TEXTURE2 +}; + +TextureState::TextureState() + : mTextureUnit(0) { + glActiveTexture(kTextureUnits[0]); + resetBoundTextures(); + + GLint maxTextureUnits; + glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxTextureUnits); + LOG_ALWAYS_FATAL_IF(maxTextureUnits < kTextureUnitsCount, + "At least %d texture units are required!", kTextureUnitsCount); +} + +void TextureState::activateTexture(GLuint textureUnit) { + if (mTextureUnit != textureUnit) { + glActiveTexture(kTextureUnits[textureUnit]); + mTextureUnit = textureUnit; + } +} + +void TextureState::resetActiveTexture() { + mTextureUnit = -1; +} + +void TextureState::bindTexture(GLuint texture) { + if (mBoundTextures[mTextureUnit] != texture) { + glBindTexture(GL_TEXTURE_2D, texture); + mBoundTextures[mTextureUnit] = texture; + } +} + +void TextureState::bindTexture(GLenum target, GLuint texture) { + if (target == GL_TEXTURE_2D) { + bindTexture(texture); + } else { + // GLConsumer directly calls glBindTexture() with + // target=GL_TEXTURE_EXTERNAL_OES, don't cache this target + // since the cached state could be stale + glBindTexture(target, texture); + } +} + +void TextureState::deleteTexture(GLuint texture) { + // When glDeleteTextures() is called on a currently bound texture, + // OpenGL ES specifies that the texture is then considered unbound + // Consider the following series of calls: + // + // glGenTextures -> creates texture name 2 + // glBindTexture(2) + // glDeleteTextures(2) -> 2 is now unbound + // glGenTextures -> can return 2 again + // + // If we don't call glBindTexture(2) after the second glGenTextures + // call, any texture operation will be performed on the default + // texture (name=0) + + unbindTexture(texture); + + glDeleteTextures(1, &texture); +} + +void TextureState::resetBoundTextures() { + for (int i = 0; i < kTextureUnitsCount; i++) { + mBoundTextures[i] = 0; + } +} + +void TextureState::unbindTexture(GLuint texture) { + for (int i = 0; i < kTextureUnitsCount; i++) { + if (mBoundTextures[i] == texture) { + mBoundTextures[i] = 0; + } + } +} + +} /* namespace uirenderer */ +} /* namespace android */ + diff --git a/libs/hwui/renderstate/TextureState.h b/libs/hwui/renderstate/TextureState.h new file mode 100644 index 0000000..5a57b9f --- /dev/null +++ b/libs/hwui/renderstate/TextureState.h @@ -0,0 +1,88 @@ +/* + * 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. + */ +#ifndef RENDERSTATE_TEXTURESTATE_H +#define RENDERSTATE_TEXTURESTATE_H + +#include "Vertex.h" + +#include <GLES2/gl2.h> +#include <GLES2/gl2ext.h> +#include <SkXfermode.h> +#include <memory> + +namespace android { +namespace uirenderer { + +class TextureState { + friend class Caches; // TODO: move to RenderState +public: + /** + * Activate the specified texture unit. The texture unit must + * be specified using an integer number (0 for GL_TEXTURE0 etc.) + */ + void activateTexture(GLuint textureUnit); + + /** + * Invalidate the cached value of the active texture unit. + */ + void resetActiveTexture(); + + /** + * Binds the specified texture as a GL_TEXTURE_2D texture. + * All texture bindings must be performed with this method or + * bindTexture(GLenum, GLuint). + */ + void bindTexture(GLuint texture); + + /** + * Binds the specified texture with the specified render target. + * All texture bindings must be performed with this method or + * bindTexture(GLuint). + */ + void bindTexture(GLenum target, GLuint texture); + + /** + * Deletes the specified texture and clears it from the cache + * of bound textures. + * All textures must be deleted using this method. + */ + void deleteTexture(GLuint texture); + + /** + * Signals that the cache of bound textures should be cleared. + * Other users of the context may have altered which textures are bound. + */ + void resetBoundTextures(); + + /** + * Clear the cache of bound textures. + */ + void unbindTexture(GLuint texture); +private: + // total number of texture units available for use + static const int kTextureUnitsCount = 3; + + TextureState(); + GLuint mTextureUnit; + + // Caches texture bindings for the GL_TEXTURE_2D target + GLuint mBoundTextures[kTextureUnitsCount]; +}; + +} /* namespace uirenderer */ +} /* namespace android */ + +#endif // RENDERSTATE_BLEND_H diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index 75bd067..9237151 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -16,19 +16,19 @@ #include "CanvasContext.h" -#include <algorithm> -#include <private/hwui/DrawGlInfo.h> -#include <strings.h> - #include "EglManager.h" #include "RenderThread.h" #include "../AnimationContext.h" #include "../Caches.h" #include "../DeferredLayerUpdater.h" -#include "../RenderState.h" +#include "../renderstate/RenderState.h" +#include "../renderstate/Stencil.h" #include "../LayerRenderer.h" #include "../OpenGLRenderer.h" -#include "../Stencil.h" + +#include <algorithm> +#include <private/hwui/DrawGlInfo.h> +#include <strings.h> #define TRIM_MEMORY_COMPLETE 80 #define TRIM_MEMORY_UI_HIDDEN 20 @@ -41,32 +41,28 @@ CanvasContext::CanvasContext(RenderThread& thread, bool translucent, RenderNode* rootRenderNode, IContextFactory* contextFactory) : mRenderThread(thread) , mEglManager(thread.eglManager()) - , mEglSurface(EGL_NO_SURFACE) - , mBufferPreserved(false) - , mSwapBehavior(kSwap_default) , mOpaque(!translucent) - , mCanvas(NULL) - , mHaveNewSurface(false) - , mRootRenderNode(rootRenderNode) { - mAnimationContext = contextFactory->createAnimationContext(mRenderThread.timeLord()); + , mAnimationContext(contextFactory->createAnimationContext(mRenderThread.timeLord())) + , mRootRenderNode(rootRenderNode) + , mJankTracker(thread.timeLord().frameIntervalNanos()) { mRenderThread.renderState().registerCanvasContext(this); + mProfiler.setDensity(mRenderThread.mainDisplayInfo().density); } CanvasContext::~CanvasContext() { destroy(); - delete mAnimationContext; mRenderThread.renderState().unregisterCanvasContext(this); } void CanvasContext::destroy() { stopDrawing(); - setSurface(NULL); + setSurface(nullptr); freePrefetechedLayers(); destroyHardwareResources(); mAnimationContext->destroy(); if (mCanvas) { delete mCanvas; - mCanvas = 0; + mCanvas = nullptr; } } @@ -96,7 +92,7 @@ void CanvasContext::setSurface(ANativeWindow* window) { void CanvasContext::swapBuffers() { if (CC_UNLIKELY(!mEglManager.swapBuffers(mEglSurface))) { - setSurface(NULL); + setSurface(nullptr); } mHaveNewSurface = false; } @@ -152,9 +148,13 @@ void CanvasContext::processLayerUpdate(DeferredLayerUpdater* layerUpdater) { } } -void CanvasContext::prepareTree(TreeInfo& info) { +void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo) { mRenderThread.removeFrameCallback(this); + mCurrentFrameInfo = &mFrames.next(); + mCurrentFrameInfo->importUiThreadInfo(uiFrameInfo); + mCurrentFrameInfo->markSyncStart(); + info.damageAccumulator = &mDamageAccumulator; info.renderer = mCanvas; if (mPrefetechedLayers.size() && info.mode == TreeInfo::MODE_FULL) { @@ -204,6 +204,7 @@ void CanvasContext::draw() { "drawRenderNode called on a context with no canvas or surface!"); profiler().markPlaybackStart(); + mCurrentFrameInfo->markIssueDrawCommandsStart(); SkRect dirty; mDamageAccumulator.finish(&dirty); @@ -224,29 +225,36 @@ void CanvasContext::draw() { profiler().unionDirty(&dirty); } - status_t status; if (!dirty.isEmpty()) { - status = mCanvas->prepareDirty(dirty.fLeft, dirty.fTop, + mCanvas->prepareDirty(dirty.fLeft, dirty.fTop, dirty.fRight, dirty.fBottom, mOpaque); } else { - status = mCanvas->prepare(mOpaque); + mCanvas->prepare(mOpaque); } Rect outBounds; - status |= mCanvas->drawRenderNode(mRootRenderNode.get(), outBounds); + mCanvas->drawRenderNode(mRootRenderNode.get(), outBounds); profiler().draw(mCanvas); - mCanvas->finish(); + bool drew = mCanvas->finish(); profiler().markPlaybackEnd(); - if (status & DrawGlInfo::kStatusDrew) { + // Even if we decided to cancel the frame, from the perspective of jank + // metrics the frame was swapped at this point + mCurrentFrameInfo->markSwapBuffers(); + + if (drew) { swapBuffers(); } else { mEglManager.cancelFrame(); } + // TODO: Use a fence for real completion? + mCurrentFrameInfo->markFrameCompleted(); + mJankTracker.addFrame(*mCurrentFrameInfo); + mRenderThread.jankTracker().addFrame(*mCurrentFrameInfo); profiler().finishFrame(); } @@ -259,9 +267,14 @@ void CanvasContext::doFrame() { ATRACE_CALL(); profiler().startFrame(); + int64_t frameInfo[UI_THREAD_FRAME_INFO_SIZE]; + UiFrameInfoBuilder(frameInfo) + .addFlag(FrameInfoFlags::kRTAnimation) + .setVsync(mRenderThread.timeLord().computeFrameTimeNanos(), + mRenderThread.timeLord().latestVsync()); TreeInfo info(TreeInfo::MODE_RT_ONLY, mRenderThread.renderState()); - prepareTree(info); + prepareTree(info, frameInfo); if (info.out.canDrawThisFrame) { draw(); } @@ -275,19 +288,19 @@ void CanvasContext::invokeFunctor(RenderThread& thread, Functor* functor) { mode = DrawGlInfo::kModeProcess; } - thread.renderState().invokeFunctor(functor, mode, NULL); + thread.renderState().invokeFunctor(functor, mode, nullptr); } void CanvasContext::markLayerInUse(RenderNode* node) { if (mPrefetechedLayers.erase(node)) { - node->decStrong(0); + node->decStrong(nullptr); } } static void destroyPrefetechedNode(RenderNode* node) { ALOGW("Incorrectly called buildLayer on View: %s, destroying layer...", node->getName()); node->destroyHardwareResources(); - node->decStrong(0); + node->decStrong(nullptr); } void CanvasContext::freePrefetechedLayers() { @@ -321,7 +334,7 @@ void CanvasContext::buildLayer(RenderNode* node) { mCanvas->markLayersAsBuildLayers(); mCanvas->flushLayerUpdates(); - node->incStrong(0); + node->incStrong(nullptr); mPrefetechedLayers.insert(node); } @@ -374,6 +387,28 @@ void CanvasContext::setTextureAtlas(RenderThread& thread, thread.eglManager().setTextureAtlas(buffer, map, mapSize); } +void CanvasContext::dumpFrames(int fd) { + FILE* file = fdopen(fd, "a"); + fprintf(file, "\n\n---PROFILEDATA---"); + for (size_t i = 0; i < mFrames.size(); i++) { + FrameInfo& frame = mFrames[i]; + if (frame[FrameInfoIndex::kSyncStart] == 0) { + continue; + } + fprintf(file, "\n"); + for (int i = 0; i < static_cast<int>(FrameInfoIndex::kNumIndexes); i++) { + fprintf(file, "%" PRId64 ",", frame[i]); + } + } + fprintf(file, "\n---PROFILEDATA---\n\n"); + fflush(file); +} + +void CanvasContext::resetFrameStats() { + mFrames.clear(); + mRenderThread.jankTracker().reset(); +} + } /* namespace renderthread */ } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h index 0cc2c7c..f5f1f54 100644 --- a/libs/hwui/renderthread/CanvasContext.h +++ b/libs/hwui/renderthread/CanvasContext.h @@ -17,7 +17,14 @@ #ifndef CANVASCONTEXT_H_ #define CANVASCONTEXT_H_ -#include <set> +#include "DamageAccumulator.h" +#include "DrawProfiler.h" +#include "IContextFactory.h" +#include "FrameInfo.h" +#include "RenderNode.h" +#include "utils/RingBuffer.h" +#include "renderthread/RenderTask.h" +#include "renderthread/RenderThread.h" #include <cutils/compiler.h> #include <EGL/egl.h> @@ -25,14 +32,8 @@ #include <utils/Functor.h> #include <utils/Vector.h> -#include "../DamageAccumulator.h" -#include "../DrawProfiler.h" -#include "../IContextFactory.h" -#include "../RenderNode.h" -#include "RenderTask.h" -#include "RenderThread.h" - -#define FUNCTOR_PROCESS_DELAY 4 +#include <set> +#include <string> namespace android { namespace uirenderer { @@ -75,12 +76,12 @@ public: void setOpaque(bool opaque); void makeCurrent(); void processLayerUpdate(DeferredLayerUpdater* layerUpdater); - void prepareTree(TreeInfo& info); + void prepareTree(TreeInfo& info, int64_t* uiFrameInfo); void draw(); void destroy(); // IFrameCallback, Chroreographer-driven frame callback entry point - virtual void doFrame(); + virtual void doFrame() override; void buildLayer(RenderNode* node); bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap); @@ -103,6 +104,12 @@ public: DrawProfiler& profiler() { return mProfiler; } + void dumpFrames(int fd); + void resetFrameStats(); + + void setName(const std::string&& name) { mName = name; } + const std::string& name() { return mName; } + private: friend class RegisterFrameCallbackTask; // TODO: Replace with something better for layer & other GL object @@ -120,19 +127,24 @@ private: RenderThread& mRenderThread; EglManager& mEglManager; sp<ANativeWindow> mNativeWindow; - EGLSurface mEglSurface; - bool mBufferPreserved; - SwapBehavior mSwapBehavior; + EGLSurface mEglSurface = EGL_NO_SURFACE; + bool mBufferPreserved = false; + SwapBehavior mSwapBehavior = kSwap_default; bool mOpaque; - OpenGLRenderer* mCanvas; - bool mHaveNewSurface; + OpenGLRenderer* mCanvas = nullptr; + bool mHaveNewSurface = false; DamageAccumulator mDamageAccumulator; - AnimationContext* mAnimationContext; + std::unique_ptr<AnimationContext> mAnimationContext; const sp<RenderNode> mRootRenderNode; DrawProfiler mProfiler; + FrameInfo* mCurrentFrameInfo = nullptr; + // Ring buffer large enough for 1 second worth of frames + RingBuffer<FrameInfo, 60> mFrames; + std::string mName; + JankTracker mJankTracker; std::set<RenderNode*> mPrefetechedLayers; }; diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp index 97b31a9..35391b2 100644 --- a/libs/hwui/renderthread/DrawFrameTask.cpp +++ b/libs/hwui/renderthread/DrawFrameTask.cpp @@ -32,11 +32,8 @@ namespace uirenderer { namespace renderthread { DrawFrameTask::DrawFrameTask() - : mRenderThread(NULL) - , mContext(NULL) - , mFrameTimeNanos(0) - , mRecordDurationNanos(0) - , mDensity(1.0f) // safe enough default + : mRenderThread(nullptr) + , mContext(nullptr) , mSyncResult(kSync_OK) { } @@ -68,18 +65,12 @@ void DrawFrameTask::removeLayerUpdate(DeferredLayerUpdater* layer) { } } -int DrawFrameTask::drawFrame(nsecs_t frameTimeNanos, nsecs_t recordDurationNanos) { +int DrawFrameTask::drawFrame() { LOG_ALWAYS_FATAL_IF(!mContext, "Cannot drawFrame with no CanvasContext!"); mSyncResult = kSync_OK; - mFrameTimeNanos = frameTimeNanos; - mRecordDurationNanos = recordDurationNanos; postAndWait(); - // Reset the single-frame data - mFrameTimeNanos = 0; - mRecordDurationNanos = 0; - return mSyncResult; } @@ -92,8 +83,7 @@ void DrawFrameTask::postAndWait() { void DrawFrameTask::run() { ATRACE_NAME("DrawFrame"); - mContext->profiler().setDensity(mDensity); - mContext->profiler().startFrame(mRecordDurationNanos); + mContext->profiler().startFrame(); bool canUnblockUiThread; bool canDrawThisFrame; @@ -122,7 +112,8 @@ void DrawFrameTask::run() { bool DrawFrameTask::syncFrameState(TreeInfo& info) { ATRACE_CALL(); - mRenderThread->timeLord().vsyncReceived(mFrameTimeNanos); + int64_t vsync = mFrameInfo[static_cast<int>(FrameInfoIndex::kVsync)]; + mRenderThread->timeLord().vsyncReceived(vsync); mContext->makeCurrent(); Caches::getInstance().textureCache.resetMarkInUse(); @@ -130,7 +121,7 @@ bool DrawFrameTask::syncFrameState(TreeInfo& info) { mContext->processLayerUpdate(mLayers[i].get()); } mLayers.clear(); - mContext->prepareTree(info); + mContext->prepareTree(info, mFrameInfo); // This is after the prepareTree so that any pending operations // (RenderNode tree state, prefetched layers, etc...) will be flushed. diff --git a/libs/hwui/renderthread/DrawFrameTask.h b/libs/hwui/renderthread/DrawFrameTask.h index 28f6cb2..8039643 100644 --- a/libs/hwui/renderthread/DrawFrameTask.h +++ b/libs/hwui/renderthread/DrawFrameTask.h @@ -25,6 +25,7 @@ #include "RenderTask.h" #include "../Rect.h" +#include "../FrameInfo.h" #include "../TreeInfo.h" namespace android { @@ -61,10 +62,11 @@ public: void pushLayerUpdate(DeferredLayerUpdater* layer); void removeLayerUpdate(DeferredLayerUpdater* layer); - void setDensity(float density) { mDensity = density; } - int drawFrame(nsecs_t frameTimeNanos, nsecs_t recordDurationNanos); + int drawFrame(); - virtual void run(); + int64_t* frameInfo() { return mFrameInfo; } + + virtual void run() override; private: void postAndWait(); @@ -80,12 +82,11 @@ private: /********************************************* * Single frame data *********************************************/ - nsecs_t mFrameTimeNanos; - nsecs_t mRecordDurationNanos; - float mDensity; std::vector< sp<DeferredLayerUpdater> > mLayers; int mSyncResult; + + int64_t mFrameInfo[UI_THREAD_FRAME_INFO_SIZE]; }; } /* namespace renderthread */ diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp index 8fb1b10..3afca2f 100644 --- a/libs/hwui/renderthread/EglManager.cpp +++ b/libs/hwui/renderthread/EglManager.cpp @@ -16,15 +16,19 @@ #include "EglManager.h" +#include "../Caches.h" +#include "../renderstate/RenderState.h" +#include "RenderThread.h" + #include <cutils/log.h> #include <cutils/properties.h> - -#include "../RenderState.h" -#include "RenderThread.h" +#include <EGL/eglext.h> #define PROPERTY_RENDER_DIRTY_REGIONS "debug.hwui.render_dirty_regions" #define GLES_VERSION 2 +#define WAIT_FOR_GPU_COMPLETION 0 + // Android-specific addition that is used to show when frames began in systrace EGLAPI void EGLAPIENTRY eglBeginFrame(EGLDisplay dpy, EGLSurface surface); @@ -67,12 +71,12 @@ static bool load_dirty_regions_property() { EglManager::EglManager(RenderThread& thread) : mRenderThread(thread) , mEglDisplay(EGL_NO_DISPLAY) - , mEglConfig(0) + , mEglConfig(nullptr) , mEglContext(EGL_NO_CONTEXT) , mPBufferSurface(EGL_NO_SURFACE) , mAllowPreserveBuffer(load_dirty_regions_property()) , mCurrentSurface(EGL_NO_SURFACE) - , mAtlasMap(NULL) + , mAtlasMap(nullptr) , mAtlasMapSize(0) , mInFrame(false) { mCanSetPreserveBuffer = mAllowPreserveBuffer; @@ -193,7 +197,7 @@ void EglManager::usePBufferSurface() { EGLSurface EglManager::createSurface(EGLNativeWindowType window) { initialize(); - EGLSurface surface = eglCreateWindowSurface(mEglDisplay, mEglConfig, window, NULL); + EGLSurface surface = eglCreateWindowSurface(mEglDisplay, mEglConfig, window, nullptr); LOG_ALWAYS_FATAL_IF(surface == EGL_NO_SURFACE, "Failed to create EGLSurface for window %p, eglErr = %s", (void*) window, egl_error_str()); @@ -213,9 +217,6 @@ void EglManager::destroy() { if (mEglDisplay == EGL_NO_DISPLAY) return; usePBufferSurface(); - if (Caches::hasInstance()) { - Caches::getInstance().terminate(); - } mRenderThread.renderState().onGLContextDestroyed(); eglDestroyContext(mEglDisplay, mEglContext); @@ -262,6 +263,14 @@ void EglManager::beginFrame(EGLSurface surface, EGLint* width, EGLint* height) { bool EglManager::swapBuffers(EGLSurface surface) { mInFrame = false; + +#if WAIT_FOR_GPU_COMPLETION + { + ATRACE_NAME("Finishing GPU work"); + fence(); + } +#endif + eglSwapBuffers(mEglDisplay, surface); EGLint err = eglGetError(); if (CC_LIKELY(err == EGL_SUCCESS)) { @@ -280,6 +289,13 @@ bool EglManager::swapBuffers(EGLSurface surface) { return false; } +void EglManager::fence() { + EGLSyncKHR fence = eglCreateSyncKHR(mEglDisplay, EGL_SYNC_FENCE_KHR, NULL); + eglClientWaitSyncKHR(mEglDisplay, fence, + EGL_SYNC_FLUSH_COMMANDS_BIT_KHR, EGL_FOREVER_KHR); + eglDestroySyncKHR(mEglDisplay, fence); +} + void EglManager::cancelFrame() { mInFrame = false; } diff --git a/libs/hwui/renderthread/EglManager.h b/libs/hwui/renderthread/EglManager.h index e12db3a..b1a18a9 100644 --- a/libs/hwui/renderthread/EglManager.h +++ b/libs/hwui/renderthread/EglManager.h @@ -55,6 +55,8 @@ public: void setTextureAtlas(const sp<GraphicBuffer>& buffer, int64_t* map, size_t mapSize); + void fence(); + private: friend class RenderThread; diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp index 6d063a4..cc87241 100644 --- a/libs/hwui/renderthread/RenderProxy.cpp +++ b/libs/hwui/renderthread/RenderProxy.cpp @@ -16,14 +16,14 @@ #include "RenderProxy.h" -#include "CanvasContext.h" -#include "RenderTask.h" -#include "RenderThread.h" - -#include "../DeferredLayerUpdater.h" -#include "../DisplayList.h" -#include "../LayerRenderer.h" -#include "../Rect.h" +#include "DeferredLayerUpdater.h" +#include "DisplayList.h" +#include "LayerRenderer.h" +#include "Rect.h" +#include "renderthread/CanvasContext.h" +#include "renderthread/RenderTask.h" +#include "renderthread/RenderThread.h" +#include "utils/Macros.h" namespace android { namespace uirenderer { @@ -52,6 +52,12 @@ namespace renderthread { MethodInvokeRenderTask* task = new MethodInvokeRenderTask((RunnableMethod) Bridge_ ## method); \ ARGS(method) *args = (ARGS(method) *) task->payload() +enum class DumpFlags { + kFrameStats = 1 << 0, + kReset = 1 << 1, +}; +MAKE_FLAGS_ENUM(DumpFlags) + CREATE_BRIDGE4(createContext, RenderThread* thread, bool translucent, RenderNode* rootRenderNode, IContextFactory* contextFactory) { return new CanvasContext(*args->thread, args->translucent, @@ -60,7 +66,7 @@ CREATE_BRIDGE4(createContext, RenderThread* thread, bool translucent, RenderProxy::RenderProxy(bool translucent, RenderNode* rootRenderNode, IContextFactory* contextFactory) : mRenderThread(RenderThread::getInstance()) - , mContext(0) { + , mContext(nullptr) { SETUP_TASK(createContext); args->translucent = translucent; args->rootRenderNode = rootRenderNode; @@ -76,36 +82,24 @@ RenderProxy::~RenderProxy() { CREATE_BRIDGE1(destroyContext, CanvasContext* context) { delete args->context; - return NULL; + return nullptr; } void RenderProxy::destroyContext() { if (mContext) { SETUP_TASK(destroyContext); args->context = mContext; - mContext = 0; - mDrawFrameTask.setContext(NULL, NULL); + mContext = nullptr; + mDrawFrameTask.setContext(nullptr, nullptr); // This is also a fence as we need to be certain that there are no // outstanding mDrawFrame tasks posted before it is destroyed postAndWait(task); } } -CREATE_BRIDGE2(setFrameInterval, RenderThread* thread, nsecs_t frameIntervalNanos) { - args->thread->timeLord().setFrameInterval(args->frameIntervalNanos); - return NULL; -} - -void RenderProxy::setFrameInterval(nsecs_t frameIntervalNanos) { - SETUP_TASK(setFrameInterval); - args->thread = &mRenderThread; - args->frameIntervalNanos = frameIntervalNanos; - post(task); -} - CREATE_BRIDGE2(setSwapBehavior, CanvasContext* context, SwapBehavior swapBehavior) { args->context->setSwapBehavior(args->swapBehavior); - return NULL; + return nullptr; } void RenderProxy::setSwapBehavior(SwapBehavior swapBehavior) { @@ -132,6 +126,18 @@ bool RenderProxy::loadSystemProperties() { return (bool) postAndWait(task); } +CREATE_BRIDGE2(setName, CanvasContext* context, const char* name) { + args->context->setName(std::string(args->name)); + return nullptr; +} + +void RenderProxy::setName(const char* name) { + SETUP_TASK(setName); + args->context = mContext; + args->name = name; + post(task); +} + CREATE_BRIDGE2(initialize, CanvasContext* context, ANativeWindow* window) { return (void*) args->context->initialize(args->window); } @@ -145,7 +151,7 @@ bool RenderProxy::initialize(const sp<ANativeWindow>& window) { CREATE_BRIDGE2(updateSurface, CanvasContext* context, ANativeWindow* window) { args->context->updateSurface(args->window); - return NULL; + return nullptr; } void RenderProxy::updateSurface(const sp<ANativeWindow>& window) { @@ -171,7 +177,7 @@ CREATE_BRIDGE7(setup, CanvasContext* context, int width, int height, uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha) { args->context->setup(args->width, args->height, args->lightCenter, args->lightRadius, args->ambientShadowAlpha, args->spotShadowAlpha); - return NULL; + return nullptr; } void RenderProxy::setup(int width, int height, const Vector3& lightCenter, float lightRadius, @@ -189,7 +195,7 @@ void RenderProxy::setup(int width, int height, const Vector3& lightCenter, float CREATE_BRIDGE2(setOpaque, CanvasContext* context, bool opaque) { args->context->setOpaque(args->opaque); - return NULL; + return nullptr; } void RenderProxy::setOpaque(bool opaque) { @@ -199,15 +205,17 @@ void RenderProxy::setOpaque(bool opaque) { post(task); } -int RenderProxy::syncAndDrawFrame(nsecs_t frameTimeNanos, nsecs_t recordDurationNanos, - float density) { - mDrawFrameTask.setDensity(density); - return mDrawFrameTask.drawFrame(frameTimeNanos, recordDurationNanos); +int64_t* RenderProxy::frameInfo() { + return mDrawFrameTask.frameInfo(); +} + +int RenderProxy::syncAndDrawFrame() { + return mDrawFrameTask.drawFrame(); } CREATE_BRIDGE1(destroy, CanvasContext* context) { args->context->destroy(); - return NULL; + return nullptr; } void RenderProxy::destroy() { @@ -221,7 +229,7 @@ void RenderProxy::destroy() { CREATE_BRIDGE2(invokeFunctor, RenderThread* thread, Functor* functor) { CanvasContext::invokeFunctor(*args->thread, args->functor); - return NULL; + return nullptr; } void RenderProxy::invokeFunctor(Functor* functor, bool waitForCompletion) { @@ -242,7 +250,7 @@ void RenderProxy::invokeFunctor(Functor* functor, bool waitForCompletion) { CREATE_BRIDGE2(runWithGlContext, CanvasContext* context, RenderTask* task) { args->context->runWithGlContext(args->task); - return NULL; + return nullptr; } void RenderProxy::runWithGlContext(RenderTask* gltask) { @@ -254,7 +262,7 @@ void RenderProxy::runWithGlContext(RenderTask* gltask) { CREATE_BRIDGE2(createTextureLayer, RenderThread* thread, CanvasContext* context) { Layer* layer = args->context->createTextureLayer(); - if (!layer) return 0; + if (!layer) return nullptr; return new DeferredLayerUpdater(*args->thread, layer); } @@ -269,7 +277,7 @@ DeferredLayerUpdater* RenderProxy::createTextureLayer() { CREATE_BRIDGE2(buildLayer, CanvasContext* context, RenderNode* node) { args->context->buildLayer(args->node); - return NULL; + return nullptr; } void RenderProxy::buildLayer(RenderNode* node) { @@ -303,7 +311,7 @@ void RenderProxy::cancelLayerUpdate(DeferredLayerUpdater* layer) { CREATE_BRIDGE1(detachSurfaceTexture, DeferredLayerUpdater* layer) { args->layer->detachSurfaceTexture(); - return NULL; + return nullptr; } void RenderProxy::detachSurfaceTexture(DeferredLayerUpdater* layer) { @@ -314,7 +322,7 @@ void RenderProxy::detachSurfaceTexture(DeferredLayerUpdater* layer) { CREATE_BRIDGE1(destroyHardwareResources, CanvasContext* context) { args->context->destroyHardwareResources(); - return NULL; + return nullptr; } void RenderProxy::destroyHardwareResources() { @@ -325,7 +333,7 @@ void RenderProxy::destroyHardwareResources() { CREATE_BRIDGE2(timMemory, RenderThread* thread, int level) { CanvasContext::trimMemory(*args->thread, args->level); - return NULL; + return nullptr; } void RenderProxy::trimMemory(int level) { @@ -341,17 +349,21 @@ void RenderProxy::trimMemory(int level) { CREATE_BRIDGE0(fence) { // Intentionally empty - return NULL; + return nullptr; } +template <typename T> +void UNUSED(T t) {} + void RenderProxy::fence() { SETUP_TASK(fence); + UNUSED(args); postAndWait(task); } CREATE_BRIDGE1(stopDrawing, CanvasContext* context) { args->context->stopDrawing(); - return NULL; + return nullptr; } void RenderProxy::stopDrawing() { @@ -362,7 +374,7 @@ void RenderProxy::stopDrawing() { CREATE_BRIDGE1(notifyFramePending, CanvasContext* context) { args->context->notifyFramePending(); - return NULL; + return nullptr; } void RenderProxy::notifyFramePending() { @@ -371,45 +383,77 @@ void RenderProxy::notifyFramePending() { mRenderThread.queueAtFront(task); } -CREATE_BRIDGE2(dumpProfileInfo, CanvasContext* context, int fd) { +CREATE_BRIDGE3(dumpProfileInfo, CanvasContext* context, int fd, int dumpFlags) { args->context->profiler().dumpData(args->fd); - return NULL; + if (args->dumpFlags & DumpFlags::kFrameStats) { + args->context->dumpFrames(args->fd); + } + if (args->dumpFlags & DumpFlags::kReset) { + args->context->resetFrameStats(); + } + return nullptr; } -void RenderProxy::dumpProfileInfo(int fd) { +void RenderProxy::dumpProfileInfo(int fd, int dumpFlags) { SETUP_TASK(dumpProfileInfo); args->context = mContext; args->fd = fd; + args->dumpFlags = dumpFlags; postAndWait(task); } -CREATE_BRIDGE1(outputLogBuffer, int fd) { - RenderNode::outputLogBuffer(args->fd); - return NULL; +CREATE_BRIDGE2(dumpGraphicsMemory, int fd, RenderThread* thread) { + args->thread->jankTracker().dump(args->fd); + + FILE *file = fdopen(args->fd, "a"); + if (Caches::hasInstance()) { + String8 cachesLog; + Caches::getInstance().dumpMemoryUsage(cachesLog); + fprintf(file, "\nCaches:\n%s\n", cachesLog.string()); + } else { + fprintf(file, "\nNo caches instance.\n"); + } + fflush(file); + return nullptr; } -void RenderProxy::outputLogBuffer(int fd) { - SETUP_TASK(outputLogBuffer); +void RenderProxy::dumpGraphicsMemory(int fd) { + if (!RenderThread::hasInstance()) return; + SETUP_TASK(dumpGraphicsMemory); args->fd = fd; + args->thread = &RenderThread::getInstance(); 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(0); - return NULL; + 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(0); + 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); + return nullptr; +} + +void RenderProxy::setProcessStatsBuffer(int fd) { + SETUP_TASK(setProcessStatsBuffer); + args->thread = &mRenderThread; + args->fd = dup(fd); + post(task); +} + void RenderProxy::post(RenderTask* task) { mRenderThread.queue(task); } diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h index fd1fe05..29c6f08 100644 --- a/libs/hwui/renderthread/RenderProxy.h +++ b/libs/hwui/renderthread/RenderProxy.h @@ -62,10 +62,10 @@ public: ANDROID_API RenderProxy(bool translucent, RenderNode* rootNode, IContextFactory* contextFactory); ANDROID_API virtual ~RenderProxy(); - ANDROID_API void setFrameInterval(nsecs_t frameIntervalNanos); // Won't take effect until next EGLSurface creation ANDROID_API void setSwapBehavior(SwapBehavior swapBehavior); ANDROID_API bool loadSystemProperties(); + ANDROID_API void setName(const char* name); ANDROID_API bool initialize(const sp<ANativeWindow>& window); ANDROID_API void updateSurface(const sp<ANativeWindow>& window); @@ -73,8 +73,8 @@ public: ANDROID_API void setup(int width, int height, const Vector3& lightCenter, float lightRadius, uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha); ANDROID_API void setOpaque(bool opaque); - ANDROID_API int syncAndDrawFrame(nsecs_t frameTimeNanos, nsecs_t recordDurationNanos, - float density); + ANDROID_API int64_t* frameInfo(); + ANDROID_API int syncAndDrawFrame(); ANDROID_API void destroy(); ANDROID_API static void invokeFunctor(Functor* functor, bool waitForCompletion); @@ -95,10 +95,11 @@ public: ANDROID_API void stopDrawing(); ANDROID_API void notifyFramePending(); - ANDROID_API void dumpProfileInfo(int fd); - ANDROID_API static void outputLogBuffer(int fd); + ANDROID_API void dumpProfileInfo(int fd, int dumpFlags); + 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); private: RenderThread& mRenderThread; diff --git a/libs/hwui/renderthread/RenderTask.cpp b/libs/hwui/renderthread/RenderTask.cpp index 7ca61e4..b14f580 100644 --- a/libs/hwui/renderthread/RenderTask.cpp +++ b/libs/hwui/renderthread/RenderTask.cpp @@ -14,11 +14,8 @@ * limitations under the License. */ -#define LOG_TAG "RenderTask" - #include "RenderTask.h" -#include <utils/Log.h> #include <utils/Condition.h> #include <utils/Mutex.h> diff --git a/libs/hwui/renderthread/RenderTask.h b/libs/hwui/renderthread/RenderTask.h index 1554a16..89c3a7d 100644 --- a/libs/hwui/renderthread/RenderTask.h +++ b/libs/hwui/renderthread/RenderTask.h @@ -47,7 +47,7 @@ namespace renderthread { class ANDROID_API RenderTask { public: - ANDROID_API RenderTask() : mNext(0), mRunAt(0) {} + ANDROID_API RenderTask() : mNext(nullptr), mRunAt(0) {} ANDROID_API virtual ~RenderTask() {} ANDROID_API virtual void run() = 0; @@ -61,7 +61,7 @@ public: // Takes ownership of task, caller owns lock and signal SignalingRenderTask(RenderTask* task, Mutex* lock, Condition* signal) : mTask(task), mLock(lock), mSignal(signal) {} - virtual void run(); + virtual void run() override; private: RenderTask* mTask; @@ -74,12 +74,12 @@ typedef void* (*RunnableMethod)(void* data); class MethodInvokeRenderTask : public RenderTask { public: MethodInvokeRenderTask(RunnableMethod method) - : mMethod(method), mReturnPtr(0) {} + : mMethod(method), mReturnPtr(nullptr) {} void* payload() { return mData; } void setReturnPtr(void** retptr) { mReturnPtr = retptr; } - virtual void run() { + virtual void run() override { void* retval = mMethod(mData); if (mReturnPtr) { *mReturnPtr = retval; diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp index 84826b7..3ac2976 100644 --- a/libs/hwui/renderthread/RenderThread.cpp +++ b/libs/hwui/renderthread/RenderThread.cpp @@ -16,17 +16,17 @@ #include "RenderThread.h" -#if defined(HAVE_PTHREADS) -#include <sys/resource.h> -#endif -#include <gui/DisplayEventReceiver.h> -#include <utils/Log.h> - -#include "../RenderState.h" +#include "../renderstate/RenderState.h" #include "CanvasContext.h" #include "EglManager.h" #include "RenderProxy.h" +#include <gui/DisplayEventReceiver.h> +#include <gui/ISurfaceComposer.h> +#include <gui/SurfaceComposerClient.h> +#include <sys/resource.h> +#include <utils/Log.h> + namespace android { using namespace uirenderer::renderthread; ANDROID_SINGLETON_STATIC_INSTANCE(RenderThread); @@ -42,16 +42,16 @@ static const size_t EVENT_BUFFER_SIZE = 100; // Slight delay to give the UI time to push us a new frame before we replay static const nsecs_t DISPATCH_FRAME_CALLBACKS_DELAY = milliseconds_to_nanoseconds(4); -TaskQueue::TaskQueue() : mHead(0), mTail(0) {} +TaskQueue::TaskQueue() : mHead(nullptr), mTail(nullptr) {} RenderTask* TaskQueue::next() { RenderTask* ret = mHead; if (ret) { mHead = ret->mNext; if (!mHead) { - mTail = 0; + mTail = nullptr; } - ret->mNext = 0; + ret->mNext = nullptr; } return ret; } @@ -71,7 +71,7 @@ void TaskQueue::queue(RenderTask* task) { mTail = task; } else { // Need to find the proper insertion point - RenderTask* previous = 0; + RenderTask* previous = nullptr; RenderTask* next = mHead; while (next && next->mRunAt <= task->mRunAt) { previous = next; @@ -131,19 +131,19 @@ private: public: DispatchFrameCallbacks(RenderThread* rt) : mRenderThread(rt) {} - virtual void run() { + virtual void run() override { mRenderThread->dispatchFrameCallbacks(); } }; RenderThread::RenderThread() : Thread(true), Singleton<RenderThread>() , mNextWakeup(LLONG_MAX) - , mDisplayEventReceiver(0) + , mDisplayEventReceiver(nullptr) , mVsyncRequested(false) , mFrameCallbackTaskPending(false) - , mFrameCallbackTask(0) - , mRenderState(NULL) - , mEglManager(NULL) { + , mFrameCallbackTask(nullptr) + , mRenderState(nullptr) + , mEglManager(nullptr) { mFrameCallbackTask = new DispatchFrameCallbacks(this); mLooper = new Looper(false); run("RenderThread"); @@ -166,9 +166,16 @@ void RenderThread::initializeDisplayEventReceiver() { } void RenderThread::initThreadLocals() { + sp<IBinder> dtoken(SurfaceComposerClient::getBuiltInDisplay( + ISurfaceComposer::eDisplayIdMain)); + status_t status = SurfaceComposerClient::getDisplayInfo(dtoken, &mDisplayInfo); + LOG_ALWAYS_FATAL_IF(status, "Failed to get display info\n"); + nsecs_t frameIntervalNanos = static_cast<nsecs_t>(1000000000 / mDisplayInfo.fps); + mTimeLord.setFrameInterval(frameIntervalNanos); initializeDisplayEventReceiver(); mEglManager = new EglManager(*this); mRenderState = new RenderState(*this); + mJankTracker = new JankTracker(frameIntervalNanos); } int RenderThread::displayEventReceiverCallback(int fd, int events, void* data) { @@ -250,9 +257,7 @@ void RenderThread::requestVsync() { } bool RenderThread::threadLoop() { -#if defined(HAVE_PTHREADS) setpriority(PRIO_PROCESS, 0, PRIORITY_DISPLAY); -#endif initThreadLocals(); int timeoutMillis = -1; @@ -350,7 +355,7 @@ RenderTask* RenderThread::nextTask(nsecs_t* nextWakeup) { if (next->mRunAt <= 0 || next->mRunAt <= systemTime(SYSTEM_TIME_MONOTONIC)) { next = mQueue.next(); } else { - next = 0; + next = nullptr; } } if (nextWakeup) { diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h index 99c2e15..8096099 100644 --- a/libs/hwui/renderthread/RenderThread.h +++ b/libs/hwui/renderthread/RenderThread.h @@ -19,16 +19,18 @@ #include "RenderTask.h" -#include <memory> -#include <set> +#include "../JankTracker.h" +#include "TimeLord.h" #include <cutils/compiler.h> +#include <ui/DisplayInfo.h> #include <utils/Looper.h> #include <utils/Mutex.h> #include <utils/Singleton.h> #include <utils/Thread.h> -#include "TimeLord.h" +#include <memory> +#include <set> namespace android { @@ -88,9 +90,12 @@ public: TimeLord& timeLord() { return mTimeLord; } RenderState& renderState() { return *mRenderState; } EglManager& eglManager() { return *mEglManager; } + JankTracker& jankTracker() { return *mJankTracker; } + + const DisplayInfo& mainDisplayInfo() { return mDisplayInfo; } protected: - virtual bool threadLoop(); + virtual bool threadLoop() override; private: friend class Singleton<RenderThread>; @@ -118,6 +123,8 @@ private: nsecs_t mNextWakeup; TaskQueue mQueue; + DisplayInfo mDisplayInfo; + DisplayEventReceiver* mDisplayEventReceiver; bool mVsyncRequested; std::set<IFrameCallback*> mFrameCallbacks; @@ -132,6 +139,8 @@ private: TimeLord mTimeLord; RenderState* mRenderState; EglManager* mEglManager; + + JankTracker* mJankTracker = nullptr; }; } /* namespace renderthread */ diff --git a/libs/hwui/renderthread/TimeLord.cpp b/libs/hwui/renderthread/TimeLord.cpp index f187493..f846d6a 100644 --- a/libs/hwui/renderthread/TimeLord.cpp +++ b/libs/hwui/renderthread/TimeLord.cpp @@ -32,7 +32,7 @@ bool TimeLord::vsyncReceived(nsecs_t vsync) { return false; } -nsecs_t TimeLord::computeFrameTimeMs() { +nsecs_t TimeLord::computeFrameTimeNanos() { // Logic copied from Choreographer.java nsecs_t now = systemTime(CLOCK_MONOTONIC); nsecs_t jitterNanos = now - mFrameTimeNanos; @@ -40,7 +40,11 @@ nsecs_t TimeLord::computeFrameTimeMs() { nsecs_t lastFrameOffset = jitterNanos % mFrameIntervalNanos; mFrameTimeNanos = now - lastFrameOffset; } - return nanoseconds_to_milliseconds(mFrameTimeNanos); + return mFrameTimeNanos; +} + +nsecs_t TimeLord::computeFrameTimeMs() { + return nanoseconds_to_milliseconds(computeFrameTimeNanos()); } } /* namespace renderthread */ diff --git a/libs/hwui/renderthread/TimeLord.h b/libs/hwui/renderthread/TimeLord.h index 7c155d2..5464399 100644 --- a/libs/hwui/renderthread/TimeLord.h +++ b/libs/hwui/renderthread/TimeLord.h @@ -29,9 +29,13 @@ class RenderThread; class TimeLord { public: void setFrameInterval(nsecs_t intervalNanos) { mFrameIntervalNanos = intervalNanos; } + nsecs_t frameIntervalNanos() const { return mFrameIntervalNanos; } + // returns true if the vsync is newer, false if it was rejected for staleness bool vsyncReceived(nsecs_t vsync); + nsecs_t latestVsync() { return mFrameTimeNanos; } nsecs_t computeFrameTimeMs(); + nsecs_t computeFrameTimeNanos(); private: friend class RenderThread; diff --git a/libs/hwui/tests/Android.mk b/libs/hwui/tests/Android.mk index 7bdce7f..b6f0baf 100644 --- a/libs/hwui/tests/Android.mk +++ b/libs/hwui/tests/Android.mk @@ -15,34 +15,9 @@ # local_target_dir := $(TARGET_OUT_DATA)/local/tmp -LOCAL_PATH:= $(call my-dir) +LOCAL_PATH:= $(call my-dir)/.. include $(CLEAR_VARS) -LOCAL_CFLAGS += -DUSE_OPENGL_RENDERER -DEGL_EGLEXT_PROTOTYPES -DGL_GLEXT_PROTOTYPES -LOCAL_CFLAGS += -Wno-unused-parameter -LOCAL_CFLAGS += -DATRACE_TAG=ATRACE_TAG_VIEW -DLOG_TAG=\"OpenGLRenderer\" - -LOCAL_SRC_FILES:= \ - TestContext.cpp \ - main.cpp - -LOCAL_C_INCLUDES += \ - $(LOCAL_PATH)/.. \ - external/skia/src/core - -LOCAL_SHARED_LIBRARIES := \ - liblog \ - libcutils \ - libutils \ - libskia \ - libgui \ - libui \ - libhwui - -ifeq ($(WITH_MALLOC_LEAK_CHECK),true) - LOCAL_CFLAGS += -DMALLOC_LEAK_CHECK -endif - LOCAL_MODULE_PATH := $(local_target_dir) LOCAL_MODULE:= hwuitest LOCAL_MODULE_TAGS := tests @@ -50,7 +25,12 @@ LOCAL_MULTILIB := both LOCAL_MODULE_STEM_32 := hwuitest LOCAL_MODULE_STEM_64 := hwuitest64 -include external/stlport/libstlport.mk -include $(BUILD_EXECUTABLE) +HWUI_NULL_GPU := false + +include $(LOCAL_PATH)/Android.common.mk -include $(call all-makefiles-under,$(LOCAL_PATH)) +LOCAL_SRC_FILES += \ + tests/TestContext.cpp \ + tests/main.cpp + +include $(BUILD_EXECUTABLE) diff --git a/libs/hwui/tests/TestContext.cpp b/libs/hwui/tests/TestContext.cpp index 35e402d..542bbae 100644 --- a/libs/hwui/tests/TestContext.cpp +++ b/libs/hwui/tests/TestContext.cpp @@ -16,30 +16,59 @@ #include "TestContext.h" -#include <gui/ISurfaceComposer.h> -#include <gui/SurfaceComposerClient.h> +namespace android { +namespace uirenderer { +namespace test { -using namespace android; +static const int IDENT_DISPLAYEVENT = 1; -DisplayInfo gDisplay; -sp<SurfaceComposerClient> gSession; - -void createTestEnvironment() { - gSession = new SurfaceComposerClient(); +static DisplayInfo getBuiltInDisplay() { + DisplayInfo display; sp<IBinder> dtoken(SurfaceComposerClient::getBuiltInDisplay( - ISurfaceComposer::eDisplayIdMain)); - status_t status = SurfaceComposerClient::getDisplayInfo(dtoken, &gDisplay); + ISurfaceComposer::eDisplayIdMain)); + status_t status = SurfaceComposerClient::getDisplayInfo(dtoken, &display); LOG_ALWAYS_FATAL_IF(status, "Failed to get display info\n"); + return display; +} + +android::DisplayInfo gDisplay = getBuiltInDisplay(); + +TestContext::TestContext() { + mLooper = new Looper(true); + mSurfaceComposerClient = new SurfaceComposerClient(); + mLooper->addFd(mDisplayEventReceiver.getFd(), IDENT_DISPLAYEVENT, + Looper::EVENT_INPUT, nullptr, nullptr); } -sp<SurfaceControl> createWindow(int width, int height) { - sp<SurfaceControl> control = gSession->createSurface(String8("HwuiTest"), - width, height, PIXEL_FORMAT_RGBX_8888); +TestContext::~TestContext() {} + +sp<Surface> TestContext::surface() { + if (!mSurfaceControl.get()) { + mSurfaceControl = mSurfaceComposerClient->createSurface(String8("HwuiTest"), + gDisplay.w, gDisplay.h, PIXEL_FORMAT_RGBX_8888); - SurfaceComposerClient::openGlobalTransaction(); - control->setLayer(0x7FFFFFF); - control->show(); - SurfaceComposerClient::closeGlobalTransaction(); + SurfaceComposerClient::openGlobalTransaction(); + mSurfaceControl->setLayer(0x7FFFFFF); + mSurfaceControl->show(); + SurfaceComposerClient::closeGlobalTransaction(); + } - return control; + return mSurfaceControl->getSurface(); } + +void TestContext::waitForVsync() { + // Request vsync + mDisplayEventReceiver.requestNextVsync(); + + // Wait + mLooper->pollOnce(-1); + + // Drain it + DisplayEventReceiver::Event buf[100]; + while (mDisplayEventReceiver.getEvents(buf, 100) > 0) { } +} + +} // namespace test +} // namespace uirenderer +} // namespace android + diff --git a/libs/hwui/tests/TestContext.h b/libs/hwui/tests/TestContext.h index 8a5d530..7b30fc1 100644 --- a/libs/hwui/tests/TestContext.h +++ b/libs/hwui/tests/TestContext.h @@ -17,17 +17,39 @@ #ifndef TESTCONTEXT_H #define TESTCONTEXT_H -#include <ui/DisplayInfo.h> +#include <gui/DisplayEventReceiver.h> +#include <gui/ISurfaceComposer.h> +#include <gui/SurfaceComposerClient.h> #include <gui/SurfaceControl.h> +#include <gui/Surface.h> +#include <ui/DisplayInfo.h> +#include <utils/Looper.h> + +namespace android { +namespace uirenderer { +namespace test { + +extern DisplayInfo gDisplay; +#define dp(x) ((x) * android::uirenderer::test::gDisplay.density) + +class TestContext { +public: + TestContext(); + ~TestContext(); + + sp<Surface> surface(); -extern android::DisplayInfo gDisplay; -#define dp(x) ((x) * gDisplay.density) + void waitForVsync(); -// Initializes all the static globals that are shared across all contexts -// such as display info -void createTestEnvironment(); +private: + sp<SurfaceComposerClient> mSurfaceComposerClient; + sp<SurfaceControl> mSurfaceControl; + DisplayEventReceiver mDisplayEventReceiver; + sp<Looper> mLooper; +}; -// Defaults to fullscreen -android::sp<android::SurfaceControl> createWindow(int width = -1, int height = -1); +} // namespace test +} // namespace uirenderer +} // namespace android #endif diff --git a/libs/hwui/tests/main.cpp b/libs/hwui/tests/main.cpp index 2d99e9f..61ad082 100644 --- a/libs/hwui/tests/main.cpp +++ b/libs/hwui/tests/main.cpp @@ -21,111 +21,243 @@ #include <ui/PixelFormat.h> #include <AnimationContext.h> -#include <DisplayListRenderer.h> +#include <DisplayListCanvas.h> #include <RenderNode.h> #include <renderthread/RenderProxy.h> +#include <renderthread/RenderTask.h> #include "TestContext.h" using namespace android; using namespace android::uirenderer; using namespace android::uirenderer::renderthread; +using namespace android::uirenderer::test; class ContextFactory : public IContextFactory { public: - virtual AnimationContext* createAnimationContext(renderthread::TimeLord& clock) { + virtual AnimationContext* createAnimationContext(renderthread::TimeLord& clock) override { return new AnimationContext(clock); } }; -static DisplayListRenderer* startRecording(RenderNode* node) { - DisplayListRenderer* renderer = new DisplayListRenderer(); - renderer->setViewport(node->getWidth(), node->getHeight()); - renderer->prepare(false); +static DisplayListCanvas* startRecording(RenderNode* node) { + DisplayListCanvas* renderer = new DisplayListCanvas(); + renderer->setViewport(node->stagingProperties().getWidth(), + node->stagingProperties().getHeight()); + renderer->prepare(); return renderer; } -static void endRecording(DisplayListRenderer* renderer, RenderNode* node) { +static void endRecording(DisplayListCanvas* renderer, RenderNode* node) { renderer->finish(); node->setStagingDisplayList(renderer->finishRecording()); delete renderer; } -sp<RenderNode> createCard(int x, int y, int width, int height) { - sp<RenderNode> node = new RenderNode(); - node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height); - node->mutateStagingProperties().setElevation(dp(16)); - node->mutateStagingProperties().mutableOutline().setRoundRect(0, 0, width, height, dp(10), 1); - node->mutateStagingProperties().mutableOutline().setShouldClip(true); - node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y | RenderNode::Z); +class TreeContentAnimation { +public: + virtual ~TreeContentAnimation() {} + virtual int getFrameCount() { return 150; } + virtual void createContent(int width, int height, DisplayListCanvas* renderer) = 0; + virtual void doFrame(int frameNr) = 0; - DisplayListRenderer* renderer = startRecording(node.get()); - renderer->drawColor(0xFFEEEEEE, SkXfermode::kSrcOver_Mode); - endRecording(renderer, node.get()); + template <class T> + static void run() { + T animation; - return node; -} + TestContext testContext; -int main(int argc, char* argv[]) { - createTestEnvironment(); - - // create the native surface - const int width = gDisplay.w; - const int height = gDisplay.h; - sp<SurfaceControl> control = createWindow(width, height); - sp<Surface> surface = control->getSurface(); - - RenderNode* rootNode = new RenderNode(); - rootNode->incStrong(0); - rootNode->mutateStagingProperties().setLeftTopRightBottom(0, 0, width, height); - rootNode->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y); - rootNode->mutateStagingProperties().setClipToBounds(false); - rootNode->setPropertyFieldsDirty(RenderNode::GENERIC); - - ContextFactory factory; - RenderProxy* proxy = new RenderProxy(false, rootNode, &factory); - proxy->loadSystemProperties(); - proxy->initialize(surface); - float lightX = width / 2.0; - proxy->setup(width, height, (Vector3){lightX, dp(-200.0f), dp(800.0f)}, - dp(800.0f), 255 * 0.075, 255 * 0.15); - - android::uirenderer::Rect DUMMY; + // create the native surface + const int width = gDisplay.w; + const int height = gDisplay.h; + sp<Surface> surface = testContext.surface(); - std::vector< sp<RenderNode> > cards; + RenderNode* rootNode = new RenderNode(); + rootNode->incStrong(nullptr); + rootNode->mutateStagingProperties().setLeftTopRightBottom(0, 0, width, height); + rootNode->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y); + rootNode->mutateStagingProperties().setClipToBounds(false); + rootNode->setPropertyFieldsDirty(RenderNode::GENERIC); + + ContextFactory factory; + std::unique_ptr<RenderProxy> proxy(new RenderProxy(false, rootNode, &factory)); + proxy->loadSystemProperties(); + proxy->initialize(surface); + float lightX = width / 2.0; + proxy->setup(width, height, (Vector3){lightX, dp(-200.0f), dp(800.0f)}, + dp(800.0f), 255 * 0.075, 255 * 0.15); + + android::uirenderer::Rect DUMMY; + + DisplayListCanvas* renderer = startRecording(rootNode); + animation.createContent(width, height, renderer); + endRecording(renderer, rootNode); - DisplayListRenderer* renderer = startRecording(rootNode); - renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode); - renderer->insertReorderBarrier(true); + for (int i = 0; i < animation.getFrameCount(); i++) { +#if !HWUI_NULL_GPU + testContext.waitForVsync(); +#endif - for (int x = dp(16); x < (width - dp(116)); x += dp(116)) { - for (int y = dp(16); y < (height - dp(116)); y += dp(116)) { - sp<RenderNode> card = createCard(x, y, dp(100), dp(100)); - renderer->drawRenderNode(card.get(), DUMMY, 0); - cards.push_back(card); + ATRACE_NAME("UI-Draw Frame"); + animation.doFrame(i); + proxy->syncAndDrawFrame(); } + + rootNode->decStrong(nullptr); } +}; + +class ShadowGridAnimation : public TreeContentAnimation { +public: + std::vector< sp<RenderNode> > cards; + void createContent(int width, int height, DisplayListCanvas* renderer) override { + android::uirenderer::Rect DUMMY; + + renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode); + renderer->insertReorderBarrier(true); - renderer->insertReorderBarrier(false); - endRecording(renderer, rootNode); + for (int x = dp(16); x < (width - dp(116)); x += dp(116)) { + for (int y = dp(16); y < (height - dp(116)); y += dp(116)) { + sp<RenderNode> card = createCard(x, y, dp(100), dp(100)); + renderer->drawRenderNode(card.get(), DUMMY, 0); + cards.push_back(card); + } + } - for (int i = 0; i < 150; i++) { - ATRACE_NAME("UI-Draw Frame"); - for (int ci = 0; ci < cards.size(); ci++) { - cards[ci]->mutateStagingProperties().setTranslationX(i); - cards[ci]->mutateStagingProperties().setTranslationY(i); + renderer->insertReorderBarrier(false); + } + void doFrame(int frameNr) override { + for (size_t ci = 0; ci < cards.size(); ci++) { + cards[ci]->mutateStagingProperties().setTranslationX(frameNr); + cards[ci]->mutateStagingProperties().setTranslationY(frameNr); cards[ci]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y); } - nsecs_t frameTimeNs = systemTime(CLOCK_MONOTONIC); - proxy->syncAndDrawFrame(frameTimeNs, 0, gDisplay.density); - usleep(12000); } +private: + sp<RenderNode> createCard(int x, int y, int width, int height) { + sp<RenderNode> node = new RenderNode(); + node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height); + node->mutateStagingProperties().setElevation(dp(16)); + node->mutateStagingProperties().mutableOutline().setRoundRect(0, 0, width, height, dp(10), 1); + node->mutateStagingProperties().mutableOutline().setShouldClip(true); + node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y | RenderNode::Z); + + DisplayListCanvas* renderer = startRecording(node.get()); + renderer->drawColor(0xFFEEEEEE, SkXfermode::kSrcOver_Mode); + endRecording(renderer, node.get()); + return node; + } +}; + +class RectGridAnimation : public TreeContentAnimation { +public: + sp<RenderNode> card; + void createContent(int width, int height, DisplayListCanvas* renderer) override { + android::uirenderer::Rect DUMMY; - sleep(5); + renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode); + renderer->insertReorderBarrier(true); - delete proxy; - rootNode->decStrong(0); + card = createCard(40, 40, 200, 200); + renderer->drawRenderNode(card.get(), DUMMY, 0); + + renderer->insertReorderBarrier(false); + } + void doFrame(int frameNr) override { + card->mutateStagingProperties().setTranslationX(frameNr); + card->mutateStagingProperties().setTranslationY(frameNr); + card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y); + } +private: + sp<RenderNode> createCard(int x, int y, int width, int height) { + sp<RenderNode> node = new RenderNode(); + node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height); + node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y); + + DisplayListCanvas* renderer = startRecording(node.get()); + renderer->drawColor(0xFFFF00FF, SkXfermode::kSrcOver_Mode); + + float rects[width * height]; + int index = 0; + for (int xOffset = 0; xOffset < width; xOffset+=2) { + for (int yOffset = 0; yOffset < height; yOffset+=2) { + rects[index++] = xOffset; + rects[index++] = yOffset; + rects[index++] = xOffset + 1; + rects[index++] = yOffset + 1; + } + } + int count = width * height; + SkPaint paint; + paint.setColor(0xff00ffff); + renderer->drawRects(rects, count, &paint); + + endRecording(renderer, node.get()); + return node; + } +}; + +class OvalAnimation : public TreeContentAnimation { +public: + sp<RenderNode> card; + void createContent(int width, int height, DisplayListCanvas* renderer) override { + android::uirenderer::Rect DUMMY; + + renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode); + renderer->insertReorderBarrier(true); + + card = createCard(40, 40, 400, 400); + renderer->drawRenderNode(card.get(), DUMMY, 0); + + renderer->insertReorderBarrier(false); + } + + void doFrame(int frameNr) override { + card->mutateStagingProperties().setTranslationX(frameNr); + card->mutateStagingProperties().setTranslationY(frameNr); + card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y); + } +private: + sp<RenderNode> createCard(int x, int y, int width, int height) { + sp<RenderNode> node = new RenderNode(); + node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height); + node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y); + + DisplayListCanvas* renderer = startRecording(node.get()); + + SkPaint paint; + paint.setAntiAlias(true); + paint.setColor(0xFF000000); + renderer->drawOval(0, 0, width, height, paint); + + endRecording(renderer, node.get()); + return node; + } +}; + +struct cstr_cmp { + bool operator()(const char *a, const char *b) const { + return std::strcmp(a, b) < 0; + } +}; + +typedef void (*testProc)(); + +std::map<const char*, testProc, cstr_cmp> gTestMap { + {"shadowgrid", TreeContentAnimation::run<ShadowGridAnimation>}, + {"rectgrid", TreeContentAnimation::run<RectGridAnimation> }, + {"oval", TreeContentAnimation::run<OvalAnimation> }, +}; + +int main(int argc, char* argv[]) { + const char* testName = argc > 1 ? argv[1] : "shadowgrid"; + testProc proc = gTestMap[testName]; + if(!proc) { + printf("Error: couldn't find test %s\n", testName); + return 1; + } + proc(); printf("Success!\n"); return 0; } diff --git a/libs/hwui/tests/nullegl.cpp b/libs/hwui/tests/nullegl.cpp new file mode 100644 index 0000000..b6cc2f2 --- /dev/null +++ b/libs/hwui/tests/nullegl.cpp @@ -0,0 +1,160 @@ +/* + * 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 <EGL/egl.h> +#include <EGL/eglext.h> + +#include <pthread.h> +#include <stdlib.h> +#include <string.h> + +static EGLDisplay gDisplay = (EGLDisplay) 1; + +typedef struct { + EGLSurface surface; + EGLContext context; +} ThreadState; + +static pthread_key_t ThreadStateKey; +static pthread_once_t ThreadStateSetupOnce = PTHREAD_ONCE_INIT; + +static void destroyThreadState(void* state) { + free(state); +} + +static void makeThreadState() { + pthread_key_create(&ThreadStateKey, destroyThreadState); +} + +ThreadState* getThreadState() { + ThreadState* ptr; + pthread_once(&ThreadStateSetupOnce, makeThreadState); + if ((ptr = (ThreadState*) pthread_getspecific(ThreadStateKey)) == NULL) { + ptr = (ThreadState*) calloc(1, sizeof(ThreadState)); + ptr->context = EGL_NO_CONTEXT; + ptr->surface = EGL_NO_SURFACE; + pthread_setspecific(ThreadStateKey, ptr); + } + return ptr; +} + +EGLint eglGetError(void) { + return EGL_SUCCESS; +} + +EGLDisplay eglGetDisplay(EGLNativeDisplayType display_id) { + return gDisplay; +} + +EGLBoolean eglInitialize(EGLDisplay dpy, EGLint *major, EGLint *minor) { + return EGL_TRUE; +} + +EGLBoolean eglTerminate(EGLDisplay dpy) { + return EGL_TRUE; +} + +const char * eglQueryString(EGLDisplay dpy, EGLint name) { + return ""; +} + +EGLBoolean eglChooseConfig(EGLDisplay dpy, const EGLint *attrib_list, + EGLConfig *configs, EGLint config_size, + EGLint *num_config) { + memset(configs, 9, sizeof(EGLConfig) * config_size); + *num_config = config_size; + return EGL_TRUE; +} + +EGLSurface eglCreateWindowSurface(EGLDisplay dpy, EGLConfig config, + EGLNativeWindowType win, + const EGLint *attrib_list) { + return (EGLSurface) malloc(sizeof(void*)); +} + +EGLSurface eglCreatePbufferSurface(EGLDisplay dpy, EGLConfig config, + const EGLint *attrib_list) { + return (EGLSurface) malloc(sizeof(void*)); +} + +EGLBoolean eglDestroySurface(EGLDisplay dpy, EGLSurface surface) { + free(surface); + return EGL_TRUE; +} + +EGLBoolean eglQuerySurface(EGLDisplay dpy, EGLSurface surface, + EGLint attribute, EGLint *value) { + *value = 1000; + return EGL_TRUE; +} + +EGLBoolean eglReleaseThread(void) { + return EGL_TRUE; +} + +EGLBoolean eglSurfaceAttrib(EGLDisplay dpy, EGLSurface surface, + EGLint attribute, EGLint value) { + return EGL_TRUE; +} + +EGLBoolean eglSwapInterval(EGLDisplay dpy, EGLint interval) { + return EGL_TRUE; +} + +EGLContext eglCreateContext(EGLDisplay dpy, EGLConfig config, + EGLContext share_context, + const EGLint *attrib_list) { + return (EGLContext) malloc(sizeof(void*)); +} +EGLBoolean eglDestroyContext(EGLDisplay dpy, EGLContext ctx) { + free(ctx); + return EGL_TRUE; +} + +EGLBoolean eglMakeCurrent(EGLDisplay dpy, EGLSurface draw, + EGLSurface read, EGLContext ctx) { + ThreadState* state = getThreadState(); + state->surface = draw; + state->context = ctx; + return EGL_TRUE; +} + +EGLContext eglGetCurrentContext(void) { + return getThreadState()->context; +} + +EGLSurface eglGetCurrentSurface(EGLint readdraw) { + return getThreadState()->surface; +} + +EGLDisplay eglGetCurrentDisplay(void) { + return gDisplay; +} + +EGLBoolean eglSwapBuffers(EGLDisplay dpy, EGLSurface surface) { + return EGL_TRUE; +} + +EGLImageKHR eglCreateImageKHR(EGLDisplay dpy, EGLContext ctx, EGLenum target, EGLClientBuffer buffer, const EGLint *attrib_list) { + return (EGLImageKHR) malloc(sizeof(EGLImageKHR)); +} + +EGLBoolean eglDestroyImageKHR(EGLDisplay dpy, EGLImageKHR image) { + free(image); + return EGL_TRUE; +} + +void eglBeginFrame(EGLDisplay dpy, EGLSurface surface) {} diff --git a/libs/hwui/tests/nullgles.cpp b/libs/hwui/tests/nullgles.cpp new file mode 100644 index 0000000..8ca7598 --- /dev/null +++ b/libs/hwui/tests/nullgles.cpp @@ -0,0 +1,275 @@ +/* + * 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 <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; + } +} + +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 glStartTilingQCOM(GLuint x, GLuint y, GLuint width, GLuint height, GLbitfield preserveMask) {} +void glEndTilingQCOM(GLbitfield preserveMask) {} +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/thread/Signal.h b/libs/hwui/thread/Signal.h index dcf5449..d4cfeeb 100644 --- a/libs/hwui/thread/Signal.h +++ b/libs/hwui/thread/Signal.h @@ -30,8 +30,10 @@ public: ~Signal() { } void signal() { - Mutex::Autolock l(mLock); - mSignaled = true; + { + Mutex::Autolock l(mLock); + mSignaled = true; + } mCondition.signal(mType); } diff --git a/libs/hwui/thread/TaskManager.cpp b/libs/hwui/thread/TaskManager.cpp index cb5401c..f0ed0bb 100644 --- a/libs/hwui/thread/TaskManager.cpp +++ b/libs/hwui/thread/TaskManager.cpp @@ -14,10 +14,8 @@ * limitations under the License. */ -#include <sys/sysinfo.h> -#if defined(HAVE_PTHREADS) #include <sys/resource.h> -#endif +#include <sys/sysinfo.h> #include "TaskManager.h" #include "Task.h" @@ -83,9 +81,7 @@ bool TaskManager::addTaskBase(const sp<TaskBase>& task, const sp<TaskProcessorBa /////////////////////////////////////////////////////////////////////////////// status_t TaskManager::WorkerThread::readyToRun() { -#if defined(HAVE_PTHREADS) setpriority(PRIO_PROCESS, 0, PRIORITY_FOREGROUND); -#endif return NO_ERROR; } @@ -109,10 +105,15 @@ bool TaskManager::WorkerThread::threadLoop() { bool TaskManager::WorkerThread::addTask(TaskWrapper task) { if (!isRunning()) { run(mName.string(), PRIORITY_DEFAULT); + } else if (exitPending()) { + return false; } - Mutex::Autolock l(mLock); - ssize_t index = mTasks.add(task); + ssize_t index; + { + Mutex::Autolock l(mLock); + index = mTasks.add(task); + } mSignal.signal(); return index >= 0; @@ -124,10 +125,6 @@ size_t TaskManager::WorkerThread::getTaskCount() const { } void TaskManager::WorkerThread::exit() { - { - Mutex::Autolock l(mLock); - mTasks.clear(); - } requestExit(); mSignal.signal(); } diff --git a/libs/hwui/thread/TaskManager.h b/libs/hwui/thread/TaskManager.h index 5a933ab..10e8b9e 100644 --- a/libs/hwui/thread/TaskManager.h +++ b/libs/hwui/thread/TaskManager.h @@ -84,8 +84,8 @@ private: void exit(); private: - virtual status_t readyToRun(); - virtual bool threadLoop(); + virtual status_t readyToRun() override; + virtual bool threadLoop() override; // Lock for the list of tasks mutable Mutex mLock; diff --git a/libs/hwui/thread/TaskProcessor.h b/libs/hwui/thread/TaskProcessor.h index d1269f0..82538e9 100644 --- a/libs/hwui/thread/TaskProcessor.h +++ b/libs/hwui/thread/TaskProcessor.h @@ -30,9 +30,6 @@ public: TaskProcessorBase() { } virtual ~TaskProcessorBase() { }; -private: - friend class TaskManager; - virtual void process(const sp<TaskBase>& task) = 0; }; @@ -42,12 +39,19 @@ public: TaskProcessor(TaskManager* manager): mManager(manager) { } virtual ~TaskProcessor() { } - bool add(const sp<Task<T> >& task); + void add(const sp<Task<T> >& task) { + if (!addImpl(task)) { + // fall back to immediate execution + process(task); + } + } virtual void onProcess(const sp<Task<T> >& task) = 0; private: - virtual void process(const sp<TaskBase>& task) { + bool addImpl(const sp<Task<T> >& task); + + virtual void process(const sp<TaskBase>& task) override { sp<Task<T> > realTask = static_cast<Task<T>* >(task.get()); // This is the right way to do it but sp<> doesn't play nice // sp<Task<T> > realTask = static_cast<sp<Task<T> > >(task); @@ -58,7 +62,7 @@ private: }; template<typename T> -bool TaskProcessor<T>::add(const sp<Task<T> >& task) { +bool TaskProcessor<T>::addImpl(const sp<Task<T> >& task) { if (mManager) { sp<TaskProcessor<T> > self(this); return mManager->addTask(task, self); diff --git a/libs/hwui/unit_tests/Android.mk b/libs/hwui/unit_tests/Android.mk new file mode 100644 index 0000000..51601b0 --- /dev/null +++ b/libs/hwui/unit_tests/Android.mk @@ -0,0 +1,34 @@ +# +# 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. +# + +local_target_dir := $(TARGET_OUT_DATA)/local/tmp +LOCAL_PATH:= $(call my-dir)/.. + +include $(CLEAR_VARS) + +LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.common.mk +LOCAL_MODULE := hwui_unit_tests +LOCAL_MODULE_TAGS := tests + +include $(LOCAL_PATH)/Android.common.mk + +LOCAL_SRC_FILES += \ + unit_tests/ClipAreaTests.cpp \ + unit_tests/LinearAllocatorTests.cpp \ + unit_tests/main.cpp + + +include $(BUILD_NATIVE_TEST) diff --git a/libs/hwui/unit_tests/ClipAreaTests.cpp b/libs/hwui/unit_tests/ClipAreaTests.cpp new file mode 100644 index 0000000..166d5b6 --- /dev/null +++ b/libs/hwui/unit_tests/ClipAreaTests.cpp @@ -0,0 +1,116 @@ +/* + * 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 <gtest/gtest.h> +#include <SkPath.h> +#include <SkRegion.h> + +#include "ClipArea.h" + +#include "Matrix.h" +#include "Rect.h" +#include "utils/LinearAllocator.h" + +namespace android { +namespace uirenderer { + +static Rect kViewportBounds(0, 0, 2048, 2048); + +static ClipArea createClipArea() { + ClipArea area; + area.setViewportDimensions(kViewportBounds.getWidth(), kViewportBounds.getHeight()); + return area; +} + +TEST(TransformedRectangle, basics) { + Rect r(0, 0, 100, 100); + Matrix4 minus90; + minus90.loadRotate(-90); + minus90.mapRect(r); + Rect r2(20, 40, 120, 60); + + Matrix4 m90; + m90.loadRotate(90); + TransformedRectangle tr(r, m90); + EXPECT_TRUE(tr.canSimplyIntersectWith(tr)); + + Matrix4 m0; + TransformedRectangle tr0(r2, m0); + EXPECT_FALSE(tr.canSimplyIntersectWith(tr0)); + + Matrix4 m45; + m45.loadRotate(45); + TransformedRectangle tr2(r, m45); + EXPECT_FALSE(tr2.canSimplyIntersectWith(tr)); +} + +TEST(RectangleList, basics) { + RectangleList list; + EXPECT_TRUE(list.isEmpty()); + + Rect r(0, 0, 100, 100); + Matrix4 m45; + m45.loadRotate(45); + list.set(r, m45); + EXPECT_FALSE(list.isEmpty()); + + Rect r2(20, 20, 200, 200); + list.intersectWith(r2, m45); + EXPECT_FALSE(list.isEmpty()); + EXPECT_EQ(1, list.getTransformedRectanglesCount()); + + Rect r3(20, 20, 200, 200); + Matrix4 m30; + m30.loadRotate(30); + list.intersectWith(r2, m30); + EXPECT_FALSE(list.isEmpty()); + EXPECT_EQ(2, list.getTransformedRectanglesCount()); + + SkRegion clip; + clip.setRect(0, 0, 2000, 2000); + SkRegion rgn(list.convertToRegion(clip)); + EXPECT_FALSE(rgn.isEmpty()); +} + +TEST(ClipArea, basics) { + ClipArea area(createClipArea()); + EXPECT_FALSE(area.isEmpty()); +} + +TEST(ClipArea, paths) { + ClipArea area(createClipArea()); + Matrix4 transform; + transform.loadIdentity(); + SkPath path; + SkScalar r = 100; + path.addCircle(r, r, r); + area.clipPathWithTransform(path, &transform, SkRegion::kIntersect_Op); + EXPECT_FALSE(area.isEmpty()); + EXPECT_FALSE(area.isSimple()); + EXPECT_FALSE(area.isRectangleList()); + Rect clipRect(area.getClipRect()); + clipRect.dump("clipRect"); + Rect expected(0, 0, r * 2, r * 2); + expected.dump("expected"); + EXPECT_EQ(expected, clipRect); + SkRegion clipRegion(area.getClipRegion()); + auto skRect(clipRegion.getBounds()); + Rect regionBounds; + regionBounds.set(skRect); + EXPECT_EQ(expected, regionBounds); +} +} +} diff --git a/libs/hwui/unit_tests/LinearAllocatorTests.cpp b/libs/hwui/unit_tests/LinearAllocatorTests.cpp new file mode 100644 index 0000000..b3959d1 --- /dev/null +++ b/libs/hwui/unit_tests/LinearAllocatorTests.cpp @@ -0,0 +1,108 @@ +/* + * 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 <gtest/gtest.h> +#include <utils/LinearAllocator.h> + +using namespace android; +using namespace android::uirenderer; + +struct SimplePair { + int one = 1; + int two = 2; +}; + +class SignalingDtor { +public: + SignalingDtor() { + mDestroyed = nullptr; + } + SignalingDtor(bool* destroyedSignal) { + mDestroyed = destroyedSignal; + *mDestroyed = false; + } + virtual ~SignalingDtor() { + if (mDestroyed) { + *mDestroyed = true; + } + } + void setSignal(bool* destroyedSignal) { + mDestroyed = destroyedSignal; + } +private: + bool* mDestroyed; +}; + +TEST(LinearAllocator, alloc) { + LinearAllocator la; + EXPECT_EQ(0u, la.usedSize()); + la.alloc(64); + // There's some internal tracking as well as padding + // so the usedSize isn't strictly defined + EXPECT_LE(64u, la.usedSize()); + EXPECT_GT(80u, la.usedSize()); + auto pair = la.alloc<SimplePair>(); + EXPECT_LE(64u + sizeof(SimplePair), la.usedSize()); + EXPECT_GT(80u + sizeof(SimplePair), la.usedSize()); + EXPECT_EQ(1, pair->one); + EXPECT_EQ(2, pair->two); +} + +TEST(LinearAllocator, dtor) { + bool destroyed[10]; + { + LinearAllocator la; + for (int i = 0; i < 5; i++) { + la.alloc<SignalingDtor>()->setSignal(destroyed + i); + la.alloc<SimplePair>(); + } + la.alloc(100); + for (int i = 0; i < 5; i++) { + auto sd = new (la) SignalingDtor(destroyed + 5 + i); + la.autoDestroy(sd); + new (la) SimplePair(); + } + la.alloc(100); + for (int i = 0; i < 10; i++) { + EXPECT_FALSE(destroyed[i]); + } + } + for (int i = 0; i < 10; i++) { + EXPECT_TRUE(destroyed[i]); + } +} + +TEST(LinearAllocator, rewind) { + bool destroyed; + { + LinearAllocator la; + auto addr = la.alloc(100); + EXPECT_LE(100u, la.usedSize()); + la.rewindIfLastAlloc(addr, 100); + EXPECT_GT(16u, la.usedSize()); + size_t emptySize = la.usedSize(); + auto sigdtor = la.alloc<SignalingDtor>(); + sigdtor->setSignal(&destroyed); + EXPECT_FALSE(destroyed); + EXPECT_LE(emptySize, la.usedSize()); + la.rewindIfLastAlloc(sigdtor); + EXPECT_TRUE(destroyed); + EXPECT_EQ(emptySize, la.usedSize()); + destroyed = false; + } + // Checking for a double-destroy case + EXPECT_EQ(destroyed, false); +} diff --git a/libs/hwui/unit_tests/how_to_run.txt b/libs/hwui/unit_tests/how_to_run.txt new file mode 100755 index 0000000..a2d6a34 --- /dev/null +++ b/libs/hwui/unit_tests/how_to_run.txt @@ -0,0 +1,4 @@ +mmm -j8 $ANDROID_BUILD_TOP/frameworks/base/libs/hwui/unit_tests && +adb push $ANDROID_PRODUCT_OUT/data/nativetest/hwui_unit_tests/hwui_unit_tests \ + /data/nativetest/hwui_unit_tests/hwui_unit_tests && +adb shell /data/nativetest/hwui_unit_tests/hwui_unit_tests diff --git a/libs/hwui/unit_tests/main.cpp b/libs/hwui/unit_tests/main.cpp new file mode 100644 index 0000000..c9b9636 --- /dev/null +++ b/libs/hwui/unit_tests/main.cpp @@ -0,0 +1,22 @@ +/* + * 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 <gtest/gtest.h> + +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/libs/hwui/utils/LinearAllocator.cpp b/libs/hwui/utils/LinearAllocator.cpp new file mode 100644 index 0000000..59b12cf --- /dev/null +++ b/libs/hwui/utils/LinearAllocator.cpp @@ -0,0 +1,272 @@ +/* + * Copyright 2012, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define LOG_NDEBUG 1 + +#include "utils/LinearAllocator.h" + +#include <stdlib.h> +#include <utils/Log.h> + + +// The ideal size of a page allocation (these need to be multiples of 8) +#define INITIAL_PAGE_SIZE ((size_t)4096) // 4kb +#define MAX_PAGE_SIZE ((size_t)131072) // 128kb + +// The maximum amount of wasted space we can have per page +// Allocations exceeding this will have their own dedicated page +// If this is too low, we will malloc too much +// Too high, and we may waste too much space +// Must be smaller than INITIAL_PAGE_SIZE +#define MAX_WASTE_SIZE ((size_t)1024) + +#if ALIGN_DOUBLE +#define ALIGN_SZ (sizeof(double)) +#else +#define ALIGN_SZ (sizeof(int)) +#endif + +#define ALIGN(x) ((x + ALIGN_SZ - 1 ) & ~(ALIGN_SZ - 1)) +#define ALIGN_PTR(p) ((void*)(ALIGN((size_t)p))) + +#if LOG_NDEBUG +#define ADD_ALLOCATION(size) +#define RM_ALLOCATION(size) +#else +#include <utils/Thread.h> +#include <utils/Timers.h> +static size_t s_totalAllocations = 0; +static nsecs_t s_nextLog = 0; +static android::Mutex s_mutex; + +static void _logUsageLocked() { + nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); + if (now > s_nextLog) { + s_nextLog = now + milliseconds_to_nanoseconds(10); + ALOGV("Total memory usage: %zu kb", s_totalAllocations / 1024); + } +} + +static void _addAllocation(size_t size) { + android::AutoMutex lock(s_mutex); + s_totalAllocations += size; + _logUsageLocked(); +} + +#define ADD_ALLOCATION(size) _addAllocation(size); +#define RM_ALLOCATION(size) _addAllocation(-size); +#endif + +#define min(x,y) (((x) < (y)) ? (x) : (y)) + +void* operator new(std::size_t size, android::uirenderer::LinearAllocator& la) { + return la.alloc(size); +} + +namespace android { +namespace uirenderer { + +class LinearAllocator::Page { +public: + Page* next() { return mNextPage; } + void setNext(Page* next) { mNextPage = next; } + + Page() + : mNextPage(0) + {} + + void* operator new(size_t /*size*/, void* buf) { return buf; } + + void* start() { + return (void*) (((size_t)this) + sizeof(Page)); + } + + void* end(int pageSize) { + return (void*) (((size_t)start()) + pageSize); + } + +private: + Page(const Page& /*other*/) {} + Page* mNextPage; +}; + +LinearAllocator::LinearAllocator() + : mPageSize(INITIAL_PAGE_SIZE) + , mMaxAllocSize(MAX_WASTE_SIZE) + , mNext(0) + , mCurrentPage(0) + , mPages(0) + , mTotalAllocated(0) + , mWastedSpace(0) + , mPageCount(0) + , mDedicatedPageCount(0) {} + +LinearAllocator::~LinearAllocator(void) { + while (mDtorList) { + auto node = mDtorList; + mDtorList = node->next; + node->dtor(node->addr); + } + Page* p = mPages; + while (p) { + Page* next = p->next(); + p->~Page(); + free(p); + RM_ALLOCATION(mPageSize); + p = next; + } +} + +void* LinearAllocator::start(Page* p) { + return ALIGN_PTR(((size_t*)p) + sizeof(Page)); +} + +void* LinearAllocator::end(Page* p) { + return ((char*)p) + mPageSize; +} + +bool LinearAllocator::fitsInCurrentPage(size_t size) { + return mNext && ((char*)mNext + size) <= end(mCurrentPage); +} + +void LinearAllocator::ensureNext(size_t size) { + if (fitsInCurrentPage(size)) return; + + if (mCurrentPage && mPageSize < MAX_PAGE_SIZE) { + mPageSize = min(MAX_PAGE_SIZE, mPageSize * 2); + mPageSize = ALIGN(mPageSize); + } + mWastedSpace += mPageSize; + Page* p = newPage(mPageSize); + if (mCurrentPage) { + mCurrentPage->setNext(p); + } + mCurrentPage = p; + if (!mPages) { + mPages = mCurrentPage; + } + mNext = start(mCurrentPage); +} + +void* LinearAllocator::alloc(size_t size) { + size = ALIGN(size); + if (size > mMaxAllocSize && !fitsInCurrentPage(size)) { + ALOGV("Exceeded max size %zu > %zu", size, mMaxAllocSize); + // Allocation is too large, create a dedicated page for the allocation + Page* page = newPage(size); + mDedicatedPageCount++; + page->setNext(mPages); + mPages = page; + if (!mCurrentPage) + mCurrentPage = mPages; + return start(page); + } + ensureNext(size); + void* ptr = mNext; + mNext = ((char*)mNext) + size; + mWastedSpace -= size; + return ptr; +} + +void LinearAllocator::addToDestructionList(Destructor dtor, void* addr) { + static_assert(std::is_standard_layout<DestructorNode>::value, + "DestructorNode must have standard layout"); + static_assert(std::is_trivially_destructible<DestructorNode>::value, + "DestructorNode must be trivially destructable"); + auto node = new (*this) DestructorNode(); + node->dtor = dtor; + node->addr = addr; + node->next = mDtorList; + mDtorList = node; +} + +void LinearAllocator::runDestructorFor(void* addr) { + auto node = mDtorList; + DestructorNode* previous = nullptr; + while (node) { + if (node->addr == addr) { + if (previous) { + previous->next = node->next; + } else { + mDtorList = node->next; + } + node->dtor(node->addr); + rewindIfLastAlloc(node, sizeof(DestructorNode)); + break; + } + previous = node; + node = node->next; + } +} + +void LinearAllocator::rewindIfLastAlloc(void* ptr, size_t allocSize) { + // First run the destructor as running the destructor will + // also rewind for the DestructorNode allocation which will + // have been allocated after this void* if it has a destructor + runDestructorFor(ptr); + // Don't bother rewinding across pages + allocSize = ALIGN(allocSize); + if (ptr >= start(mCurrentPage) && ptr < end(mCurrentPage) + && ptr == ((char*)mNext - allocSize)) { + mWastedSpace += allocSize; + mNext = ptr; + } +} + +LinearAllocator::Page* LinearAllocator::newPage(size_t pageSize) { + pageSize = ALIGN(pageSize + sizeof(LinearAllocator::Page)); + ADD_ALLOCATION(pageSize); + mTotalAllocated += pageSize; + mPageCount++; + void* buf = malloc(pageSize); + return new (buf) Page(); +} + +static const char* toSize(size_t value, float& result) { + if (value < 2000) { + result = value; + return "B"; + } + if (value < 2000000) { + result = value / 1024.0f; + return "KB"; + } + result = value / 1048576.0f; + return "MB"; +} + +void LinearAllocator::dumpMemoryStats(const char* prefix) { + float prettySize; + const char* prettySuffix; + prettySuffix = toSize(mTotalAllocated, prettySize); + ALOGD("%sTotal allocated: %.2f%s", prefix, prettySize, prettySuffix); + prettySuffix = toSize(mWastedSpace, prettySize); + ALOGD("%sWasted space: %.2f%s (%.1f%%)", prefix, prettySize, prettySuffix, + (float) mWastedSpace / (float) mTotalAllocated * 100.0f); + ALOGD("%sPages %zu (dedicated %zu)", prefix, mPageCount, mDedicatedPageCount); +} + +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/utils/LinearAllocator.h b/libs/hwui/utils/LinearAllocator.h new file mode 100644 index 0000000..d90dd82 --- /dev/null +++ b/libs/hwui/utils/LinearAllocator.h @@ -0,0 +1,142 @@ +/* + * Copyright 2012, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ANDROID_LINEARALLOCATOR_H +#define ANDROID_LINEARALLOCATOR_H + +#include <stddef.h> +#include <type_traits> + +namespace android { +namespace uirenderer { + +/** + * A memory manager that internally allocates multi-kbyte buffers for placing objects in. It avoids + * the overhead of malloc when many objects are allocated. It is most useful when creating many + * small objects with a similar lifetime, and doesn't add significant overhead for large + * allocations. + */ +class LinearAllocator { +public: + LinearAllocator(); + ~LinearAllocator(); + + /** + * Reserves and returns a region of memory of at least size 'size', aligning as needed. + * Typically this is used in an object's overridden new() method or as a replacement for malloc. + * + * The lifetime of the returned buffers is tied to that of the LinearAllocator. If calling + * delete() on an object stored in a buffer is needed, it should be overridden to use + * rewindIfLastAlloc() + */ + void* alloc(size_t size); + + /** + * Allocates an instance of the template type with the default constructor + * and adds it to the automatic destruction list. + */ + template<class T> + T* alloc() { + T* ret = new (*this) T; + autoDestroy(ret); + return ret; + } + + /** + * Adds the pointer to the tracking list to have its destructor called + * when the LinearAllocator is destroyed. + */ + template<class T> + void autoDestroy(T* addr) { + if (!std::is_trivially_destructible<T>::value) { + auto dtor = [](void* addr) { ((T*)addr)->~T(); }; + addToDestructionList(dtor, addr); + } + } + + /** + * Attempt to deallocate the given buffer, with the LinearAllocator attempting to rewind its + * state if possible. + */ + void rewindIfLastAlloc(void* ptr, size_t allocSize); + + /** + * Same as rewindIfLastAlloc(void*, size_t) + */ + template<class T> + void rewindIfLastAlloc(T* ptr) { + rewindIfLastAlloc((void*)ptr, sizeof(T)); + } + + /** + * Dump memory usage statistics to the log (allocated and wasted space) + */ + void dumpMemoryStats(const char* prefix = ""); + + /** + * The number of bytes used for buffers allocated in the LinearAllocator (does not count space + * wasted) + */ + size_t usedSize() const { return mTotalAllocated - mWastedSpace; } + +private: + LinearAllocator(const LinearAllocator& other); + + class Page; + typedef void (*Destructor)(void* addr); + struct DestructorNode { + Destructor dtor; + void* addr; + DestructorNode* next = nullptr; + }; + + void addToDestructionList(Destructor, void* addr); + void runDestructorFor(void* addr); + Page* newPage(size_t pageSize); + bool fitsInCurrentPage(size_t size); + void ensureNext(size_t size); + void* start(Page *p); + void* end(Page* p); + + size_t mPageSize; + size_t mMaxAllocSize; + void* mNext; + Page* mCurrentPage; + Page* mPages; + DestructorNode* mDtorList = nullptr; + + // Memory usage tracking + size_t mTotalAllocated; + size_t mWastedSpace; + size_t mPageCount; + size_t mDedicatedPageCount; +}; + +}; // namespace uirenderer +}; // namespace android + +void* operator new(std::size_t size, android::uirenderer::LinearAllocator& la); + +#endif // ANDROID_LINEARALLOCATOR_H diff --git a/libs/hwui/utils/Macros.h b/libs/hwui/utils/Macros.h index 5b7c87c..1b31059 100644 --- a/libs/hwui/utils/Macros.h +++ b/libs/hwui/utils/Macros.h @@ -16,10 +16,12 @@ #ifndef MACROS_H #define MACROS_H +#include <type_traits> + #define PREVENT_COPY_AND_ASSIGN(Type) \ private: \ - Type(const Type&); \ - void operator=(const Type&) + Type(const Type&) = delete; \ + void operator=(const Type&) = delete #define DESCRIPTION_TYPE(Type) \ int compare(const Type& rhs) const { return memcmp(this, &rhs, sizeof(Type));} \ @@ -29,4 +31,34 @@ friend inline int compare_type(const Type& lhs, const Type& rhs) { return lhs.compare(rhs); } \ friend inline hash_t hash_type(const Type& entry) { return entry.hash(); } +#define REQUIRE_COMPATIBLE_LAYOUT(Type) \ + static_assert(std::is_standard_layout<Type>::value, \ + #Type " must have standard layout") + +#define MAKE_FLAGS_ENUM(enumType) \ + inline void operator|=(int& lhs, enumType rhs) { \ + lhs |= static_cast<int>(rhs); \ + } \ + inline int operator|(int lhs, enumType rhs) { \ + return lhs | static_cast<int>(rhs); \ + } \ + inline int operator|(enumType lhs, int rhs) { \ + return static_cast<int>(lhs) | rhs; \ + } \ + inline int operator|(enumType lhs, enumType rhs) { \ + return static_cast<int>(lhs) | static_cast<int>(rhs); \ + } \ + inline void operator&=(int& lhs, enumType rhs) { \ + lhs &= static_cast<int>(rhs); \ + } \ + inline int operator&(int lhs, enumType rhs) { \ + return lhs & static_cast<int>(rhs); \ + } \ + inline int operator&(enumType lhs, int rhs) { \ + return static_cast<int>(lhs) & rhs; \ + } \ + inline int operator&(enumType lhs, enumType rhs) { \ + return static_cast<int>(lhs) & static_cast<int>(rhs); \ + } + #endif /* MACROS_H */ diff --git a/libs/hwui/utils/PaintUtils.h b/libs/hwui/utils/PaintUtils.h new file mode 100644 index 0000000..ba02f5f --- /dev/null +++ b/libs/hwui/utils/PaintUtils.h @@ -0,0 +1,81 @@ +/* + * 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 PAINT_UTILS_H +#define PAINT_UTILS_H + +#include <SkColorFilter.h> +#include <SkXfermode.h> + +namespace android { +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; + } + return GL_NEAREST; + } + + // TODO: move to a method on android:Paint? replace with SkPaint::nothingToDraw()? + static inline bool paintWillNotDraw(const SkPaint& paint) { + return paint.getAlpha() == 0 + && !paint.getColorFilter() + && getXfermode(paint.getXfermode()) == SkXfermode::kSrcOver_Mode; + } + + // TODO: move to a method on android:Paint? replace with SkPaint::nothingToDraw()? + static inline bool paintWillNotDrawText(const SkPaint& paint) { + return paint.getAlpha() == 0 + && paint.getLooper() == nullptr + && !paint.getColorFilter() + && getXfermode(paint.getXfermode()) == SkXfermode::kSrcOver_Mode; + } + + static bool isBlendedShader(const SkShader* shader) { + if (shader == nullptr) { + return false; + } + return !shader->isOpaque(); + } + + static bool isBlendedColorFilter(const SkColorFilter* filter) { + if (filter == nullptr) { + return false; + } + return (filter->getFlags() & SkColorFilter::kAlphaUnchanged_Flag) == 0; + } + +}; // class PaintUtils + +} /* namespace uirenderer */ +} /* namespace android */ + +#endif /* PAINT_UTILS_H */ diff --git a/libs/hwui/utils/Pair.h b/libs/hwui/utils/Pair.h index 172606a..0db3aa3 100644 --- a/libs/hwui/utils/Pair.h +++ b/libs/hwui/utils/Pair.h @@ -17,6 +17,8 @@ #ifndef ANDROID_HWUI_PAIR_H #define ANDROID_HWUI_PAIR_H +#include <utils/TypeHelpers.h> + namespace android { namespace uirenderer { diff --git a/libs/hwui/utils/RingBuffer.h b/libs/hwui/utils/RingBuffer.h new file mode 100644 index 0000000..fc9aec0 --- /dev/null +++ b/libs/hwui/utils/RingBuffer.h @@ -0,0 +1,71 @@ +/* + * 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. + */ +#ifndef RINGBUFFER_H_ +#define RINGBUFFER_H_ + +#include "utils/Macros.h" + +#include <stddef.h> + +namespace android { +namespace uirenderer { + +template<class T, size_t SIZE> +class RingBuffer { + PREVENT_COPY_AND_ASSIGN(RingBuffer); + +public: + RingBuffer() {} + ~RingBuffer() {} + + size_t capacity() { return SIZE; } + size_t size() { return mCount; } + + T& next() { + mHead = (mHead + 1) % SIZE; + if (mCount < SIZE) { + mCount++; + } + return mBuffer[mHead]; + } + + T& front() { + return this[0]; + } + + T& back() { + return this[size() - 1]; + } + + T& operator[](size_t index) { + return mBuffer[(mHead + index + 1) % mCount]; + } + + void clear() { + mCount = 0; + mHead = -1; + } + +private: + T mBuffer[SIZE]; + int mHead = -1; + size_t mCount = 0; +}; + +}; // namespace uirenderer +}; // namespace android + +#endif /* RINGBUFFER_H_ */ diff --git a/libs/hwui/utils/SortedList.h b/libs/hwui/utils/SortedList.h index 2fa890a..a2c8c52 100644 --- a/libs/hwui/utils/SortedList.h +++ b/libs/hwui/utils/SortedList.h @@ -93,13 +93,13 @@ public: } protected: - virtual void do_construct(void* storage, size_t num) const; - virtual void do_destroy(void* storage, size_t num) const; - virtual void do_copy(void* dest, const void* from, size_t num) const; - virtual void do_splat(void* dest, const void* item, size_t num) const; - virtual void do_move_forward(void* dest, const void* from, size_t num) const; - virtual void do_move_backward(void* dest, const void* from, size_t num) const; - virtual int do_compare(const void* lhs, const void* rhs) const; + virtual void do_construct(void* storage, size_t num) const override; + virtual void do_destroy(void* storage, size_t num) const override; + virtual void do_copy(void* dest, const void* from, size_t num) const override; + virtual void do_splat(void* dest, const void* item, size_t num) const override; + virtual void do_move_forward(void* dest, const void* from, size_t num) const override; + virtual void do_move_backward(void* dest, const void* from, size_t num) const override; + virtual int do_compare(const void* lhs, const void* rhs) const override; }; // class SortedList /////////////////////////////////////////////////////////////////////////////// diff --git a/libs/hwui/utils/SortedListImpl.h b/libs/hwui/utils/SortedListImpl.h index dc385b5..b101826 100644 --- a/libs/hwui/utils/SortedListImpl.h +++ b/libs/hwui/utils/SortedListImpl.h @@ -41,7 +41,7 @@ protected: virtual int do_compare(const void* lhs, const void* rhs) const = 0; private: - ssize_t _indexOrderOf(const void* item, size_t* order = 0) const; + ssize_t _indexOrderOf(const void* item, size_t* order = nullptr) const; // these are made private, because they can't be used on a SortedVector // (they don't have an implementation either) diff --git a/libs/hwui/utils/Timing.h b/libs/hwui/utils/Timing.h index eced987..dd8847a 100644 --- a/libs/hwui/utils/Timing.h +++ b/libs/hwui/utils/Timing.h @@ -24,12 +24,12 @@ class MethodTimer { public: MethodTimer(const char* name) : mMethodName(name) { - gettimeofday(&mStart, NULL); + gettimeofday(&mStart, nullptr); } ~MethodTimer() { struct timeval stop; - gettimeofday(&stop, NULL); + gettimeofday(&stop, nullptr); long long elapsed = (stop.tv_sec * 1000000) - (mStart.tv_sec * 1000000) + (stop.tv_usec - mStart.tv_usec); ALOGD("%s took %.2fms", mMethodName, elapsed / 1000.0); diff --git a/libs/input/Android.mk b/libs/input/Android.mk index a7fb0e2..2bbfdcc 100644 --- a/libs/input/Android.mk +++ b/libs/input/Android.mk @@ -27,14 +27,14 @@ LOCAL_SHARED_LIBRARIES := \ libskia \ libgui \ libui \ - libinput \ - libinputflinger + libinput \ + libinputflinger LOCAL_C_INCLUDES := \ frameworks/native/services -LOCAL_CFLAGS += -Wno-unused-parameter +LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code LOCAL_MODULE:= libinputservice diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp index 9af521b..1152737 100644 --- a/libs/input/PointerController.cpp +++ b/libs/input/PointerController.cpp @@ -25,11 +25,14 @@ #include <cutils/log.h> +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" #include <SkBitmap.h> #include <SkCanvas.h> #include <SkColor.h> #include <SkPaint.h> #include <SkXfermode.h> +#pragma GCC diagnostic pop namespace android { diff --git a/libs/input/SpriteController.cpp b/libs/input/SpriteController.cpp index 5391393..0bc832a 100644 --- a/libs/input/SpriteController.cpp +++ b/libs/input/SpriteController.cpp @@ -24,11 +24,15 @@ #include <utils/String8.h> #include <gui/Surface.h> +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" #include <SkBitmap.h> #include <SkCanvas.h> #include <SkColor.h> #include <SkPaint.h> #include <SkXfermode.h> +#pragma GCC diagnostic pop + #include <android/native_window.h> namespace android { diff --git a/libs/storage/Android.mk b/libs/storage/Android.mk index 7a9dd6c..fae2bf7 100644 --- a/libs/storage/Android.mk +++ b/libs/storage/Android.mk @@ -9,4 +9,6 @@ LOCAL_SRC_FILES:= \ LOCAL_MODULE:= libstorage +LOCAL_CFLAGS += -Wall -Werror + include $(BUILD_STATIC_LIBRARY) diff --git a/libs/storage/IMountService.cpp b/libs/storage/IMountService.cpp index 621de18..c643ed0 100644 --- a/libs/storage/IMountService.cpp +++ b/libs/storage/IMountService.cpp @@ -64,7 +64,7 @@ public: { Parcel data, reply; data.writeInterfaceToken(IMountService::getInterfaceDescriptor()); - data.writeStrongBinder(listener->asBinder()); + data.writeStrongBinder(IInterface::asBinder(listener)); if (remote()->transact(TRANSACTION_registerListener, data, &reply) != NO_ERROR) { ALOGD("registerListener could not contact remote\n"); return; @@ -80,7 +80,7 @@ public: { Parcel data, reply; data.writeInterfaceToken(IMountService::getInterfaceDescriptor()); - data.writeStrongBinder(listener->asBinder()); + data.writeStrongBinder(IInterface::asBinder(listener)); if (remote()->transact(TRANSACTION_unregisterListener, data, &reply) != NO_ERROR) { ALOGD("unregisterListener could not contact remote\n"); return; @@ -207,12 +207,19 @@ public: ALOGD("getStorageUsers caught exception %d\n", err); return err; } - const int32_t numUsers = reply.readInt32(); + int32_t numUsersI = reply.readInt32(); + uint32_t numUsers; + if (numUsersI < 0) { + ALOGW("Number of users is negative: %d\n", numUsersI); + numUsers = 0; + } else { + numUsers = static_cast<uint32_t>(numUsersI); + } *users = (int32_t*)malloc(sizeof(int32_t)*numUsers); - for (int i = 0; i < numUsers; i++) { + for (size_t i = 0; i < numUsers; i++) { **users++ = reply.readInt32(); } - return numUsers; + return static_cast<int32_t>(numUsers); } int32_t getVolumeState(const String16& mountPoint) @@ -406,7 +413,7 @@ public: { Parcel data, reply; data.writeInterfaceToken(IMountService::getInterfaceDescriptor()); - data.writeStrongBinder(observer->asBinder()); + data.writeStrongBinder(IInterface::asBinder(observer)); if (remote()->transact(TRANSACTION_shutdown, data, &reply) != NO_ERROR) { ALOGD("shutdown could not contact remote\n"); return; @@ -443,7 +450,7 @@ public: data.writeString16(rawPath); data.writeString16(canonicalPath); data.writeString16(key); - data.writeStrongBinder(token->asBinder()); + data.writeStrongBinder(IInterface::asBinder(token)); data.writeInt32(nonce); if (remote()->transact(TRANSACTION_mountObb, data, &reply) != NO_ERROR) { ALOGD("mountObb could not contact remote\n"); @@ -463,7 +470,7 @@ public: data.writeInterfaceToken(IMountService::getInterfaceDescriptor()); data.writeString16(filename); data.writeInt32(force ? 1 : 0); - data.writeStrongBinder(token->asBinder()); + data.writeStrongBinder(IInterface::asBinder(token)); data.writeInt32(nonce); if (remote()->transact(TRANSACTION_unmountObb, data, &reply) != NO_ERROR) { ALOGD("unmountObb could not contact remote\n"); @@ -546,8 +553,8 @@ public: } }; -IMPLEMENT_META_INTERFACE(MountService, "IMountService"); +IMPLEMENT_META_INTERFACE(MountService, "IMountService") // ---------------------------------------------------------------------- -}; +} diff --git a/libs/storage/IMountServiceListener.cpp b/libs/storage/IMountServiceListener.cpp index c98a424..11b53fd 100644 --- a/libs/storage/IMountServiceListener.cpp +++ b/libs/storage/IMountServiceListener.cpp @@ -34,7 +34,7 @@ status_t BnMountServiceListener::onTransact( onUsbMassStorageConnectionChanged(connected); reply->writeNoException(); return NO_ERROR; - } break; + } case TRANSACTION_onStorageStateChanged: { CHECK_INTERFACE(IMountServiceListener, data, reply); String16 path = data.readString16(); @@ -50,4 +50,4 @@ status_t BnMountServiceListener::onTransact( } // ---------------------------------------------------------------------- -}; +} diff --git a/libs/storage/IMountShutdownObserver.cpp b/libs/storage/IMountShutdownObserver.cpp index 1a6fdee..a74a768 100644 --- a/libs/storage/IMountShutdownObserver.cpp +++ b/libs/storage/IMountShutdownObserver.cpp @@ -33,11 +33,11 @@ status_t BnMountShutdownObserver::onTransact( onShutDownComplete(statusCode); reply->writeNoException(); return NO_ERROR; - } break; + } default: return BBinder::onTransact(code, data, reply, flags); } } // ---------------------------------------------------------------------- -}; +} diff --git a/libs/storage/IObbActionListener.cpp b/libs/storage/IObbActionListener.cpp index eaa211e..9656e65 100644 --- a/libs/storage/IObbActionListener.cpp +++ b/libs/storage/IObbActionListener.cpp @@ -30,10 +30,11 @@ public: : BpInterface<IObbActionListener>(impl) { } - virtual void onObbResult(const String16& filename, const int32_t nonce, const int32_t state) { } + virtual void onObbResult(const String16& /* filename */, const int32_t /* nonce */, + const int32_t /* state */) { } }; -IMPLEMENT_META_INTERFACE(ObbActionListener, "IObbActionListener"); +IMPLEMENT_META_INTERFACE(ObbActionListener, "IObbActionListener") // ---------------------------------------------------------------------- @@ -49,7 +50,7 @@ status_t BnObbActionListener::onTransact( onObbResult(filename, nonce, state); reply->writeNoException(); return NO_ERROR; - } break; + } default: return BBinder::onTransact(code, data, reply, flags); } @@ -57,4 +58,4 @@ status_t BnObbActionListener::onTransact( // ---------------------------------------------------------------------- -}; +} diff --git a/libs/usb/tests/AccessoryChat/accessorychat/Android.mk b/libs/usb/tests/AccessoryChat/accessorychat/Android.mk index 3e07155..51f2111 100644 --- a/libs/usb/tests/AccessoryChat/accessorychat/Android.mk +++ b/libs/usb/tests/AccessoryChat/accessorychat/Android.mk @@ -4,6 +4,7 @@ LOCAL_PATH:= $(call my-dir) ifeq ($(HOST_OS),linux) include $(CLEAR_VARS) +LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk LOCAL_MODULE_TAGS := optional @@ -21,6 +22,7 @@ endif # Build for device include $(CLEAR_VARS) +LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk LOCAL_MODULE_TAGS := optional |