diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2013-11-22 11:18:57 -0800 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2013-11-22 11:18:57 -0800 |
commit | dbccd44a638ae8705a5b14bff8b2dd74abc26045 (patch) | |
tree | 14bfabaf3f3c7be86dfc064e919e00433a0cf2bb /libs/hwui/PatchCache.cpp | |
parent | ecfae4f899873f224e1aeed076dc8a41f8884487 (diff) | |
parent | b873a17ce7be0a9771c24999adca6964431728f6 (diff) | |
download | frameworks_base-dbccd44a638ae8705a5b14bff8b2dd74abc26045.zip frameworks_base-dbccd44a638ae8705a5b14bff8b2dd74abc26045.tar.gz frameworks_base-dbccd44a638ae8705a5b14bff8b2dd74abc26045.tar.bz2 |
Merge commit 'b873a17ce7be0a9771c24999adca6964431728f6' into HEAD
Change-Id: I938755073e70602cc8f51ce9bd420fdcf870cecd
Diffstat (limited to 'libs/hwui/PatchCache.cpp')
-rw-r--r-- | libs/hwui/PatchCache.cpp | 266 |
1 files changed, 200 insertions, 66 deletions
diff --git a/libs/hwui/PatchCache.cpp b/libs/hwui/PatchCache.cpp index 62e38d3..dc0d98c 100644 --- a/libs/hwui/PatchCache.cpp +++ b/libs/hwui/PatchCache.cpp @@ -16,8 +16,10 @@ #define LOG_TAG "OpenGLRenderer" +#include <utils/JenkinsHash.h> #include <utils/Log.h> +#include "Caches.h" #include "PatchCache.h" #include "Properties.h" @@ -28,111 +30,243 @@ namespace uirenderer { // Constructors/destructor /////////////////////////////////////////////////////////////////////////////// -PatchCache::PatchCache(): mMaxEntries(DEFAULT_PATCH_CACHE_SIZE) { -} - -PatchCache::PatchCache(uint32_t maxEntries): mMaxEntries(maxEntries) { +PatchCache::PatchCache(): + mSize(0), mCache(LruCache<PatchDescription, Patch*>::kUnlimitedCapacity), + mMeshBuffer(0), mFreeBlocks(NULL), mGenerationId(0) { + char property[PROPERTY_VALUE_MAX]; + if (property_get(PROPERTY_PATCH_CACHE_SIZE, property, NULL) > 0) { + INIT_LOGD(" Setting patch cache size to %skB", property); + mMaxSize = KB(atoi(property)); + } else { + INIT_LOGD(" Using default patch cache size of %.2fkB", DEFAULT_PATCH_CACHE_SIZE); + mMaxSize = KB(DEFAULT_PATCH_CACHE_SIZE); + } } PatchCache::~PatchCache() { clear(); } +void PatchCache::init(Caches& caches) { + bool created = false; + if (!mMeshBuffer) { + glGenBuffers(1, &mMeshBuffer); + created = true; + } + + caches.bindMeshBuffer(mMeshBuffer); + caches.resetVertexPointers(); + + if (created) { + createVertexBuffer(); + } +} + /////////////////////////////////////////////////////////////////////////////// // Caching /////////////////////////////////////////////////////////////////////////////// -int PatchCache::PatchDescription::compare( - const PatchCache::PatchDescription& lhs, const PatchCache::PatchDescription& rhs) { - int deltaInt = lhs.bitmapWidth - rhs.bitmapWidth; - if (deltaInt != 0) return deltaInt; - - deltaInt = lhs.bitmapHeight - rhs.bitmapHeight; - if (deltaInt != 0) return deltaInt; +hash_t PatchCache::PatchDescription::hash() const { + uint32_t hash = JenkinsHashMix(0, android::hash_type(mPatch)); + hash = JenkinsHashMix(hash, mBitmapWidth); + hash = JenkinsHashMix(hash, mBitmapHeight); + hash = JenkinsHashMix(hash, mPixelWidth); + hash = JenkinsHashMix(hash, mPixelHeight); + return JenkinsHashWhiten(hash); +} - if (lhs.pixelWidth < rhs.pixelWidth) return -1; - if (lhs.pixelWidth > rhs.pixelWidth) return +1; +int PatchCache::PatchDescription::compare(const PatchCache::PatchDescription& lhs, + const PatchCache::PatchDescription& rhs) { + return memcmp(&lhs, &rhs, sizeof(PatchDescription)); +} - if (lhs.pixelHeight < rhs.pixelHeight) return -1; - if (lhs.pixelHeight > rhs.pixelHeight) return +1; +void PatchCache::clear() { + clearCache(); - deltaInt = lhs.xCount - rhs.xCount; - if (deltaInt != 0) return deltaInt; + if (mMeshBuffer) { + Caches::getInstance().unbindMeshBuffer(); + glDeleteBuffers(1, &mMeshBuffer); + mMeshBuffer = 0; + mSize = 0; + } +} - deltaInt = lhs.yCount - rhs.yCount; - if (deltaInt != 0) return deltaInt; +void PatchCache::clearCache() { + LruCache<PatchDescription, Patch*>::Iterator i(mCache); + while (i.next()) { + delete i.value(); + } + mCache.clear(); - deltaInt = lhs.emptyCount - rhs.emptyCount; - if (deltaInt != 0) return deltaInt; + BufferBlock* block = mFreeBlocks; + while (block) { + BufferBlock* next = block->next; + delete block; + block = next; + } + mFreeBlocks = NULL; +} - deltaInt = lhs.colorKey - rhs.colorKey; - if (deltaInt != 0) return deltaInt; +void PatchCache::remove(Vector<patch_pair_t>& patchesToRemove, Res_png_9patch* patch) { + LruCache<PatchDescription, Patch*>::Iterator i(mCache); + while (i.next()) { + const PatchDescription& key = i.key(); + if (key.getPatch() == patch) { + patchesToRemove.push(patch_pair_t(&key, i.value())); + } + } +} - return 0; +void PatchCache::removeDeferred(Res_png_9patch* patch) { + Mutex::Autolock _l(mLock); + mGarbage.push(patch); } -void PatchCache::clear() { - size_t count = mCache.size(); - for (size_t i = 0; i < count; i++) { - delete mCache.valueAt(i); +void PatchCache::clearGarbage() { + Vector<patch_pair_t> patchesToRemove; + + { // scope for the mutex + Mutex::Autolock _l(mLock); + size_t count = mGarbage.size(); + for (size_t i = 0; i < count; i++) { + remove(patchesToRemove, mGarbage[i]); + } + mGarbage.clear(); } - mCache.clear(); + + // TODO: We could sort patchesToRemove by offset to merge + // adjacent free blocks + for (size_t i = 0; i < patchesToRemove.size(); i++) { + const patch_pair_t& pair = patchesToRemove[i]; + + // Add a new free block to the list + const Patch* patch = pair.getSecond(); + BufferBlock* block = new BufferBlock(patch->offset, patch->getSize()); + block->next = mFreeBlocks; + mFreeBlocks = block; + + mSize -= patch->getSize(); + + mCache.remove(*pair.getFirst()); + } + +#if DEBUG_PATCHES + if (patchesToRemove.size() > 0) { + dumpFreeBlocks("Removed garbage"); + } +#endif +} + +void PatchCache::createVertexBuffer() { + glBufferData(GL_ARRAY_BUFFER, mMaxSize, NULL, GL_DYNAMIC_DRAW); + mSize = 0; + mFreeBlocks = new BufferBlock(0, mMaxSize); + mGenerationId++; } -Patch* PatchCache::get(const uint32_t bitmapWidth, const uint32_t bitmapHeight, - const float pixelWidth, const float pixelHeight, - const int32_t* xDivs, const int32_t* yDivs, const uint32_t* colors, - const uint32_t width, const uint32_t height, const int8_t numColors) { +/** + * Sets the mesh's offsets and copies its associated vertices into + * the mesh buffer (VBO). + */ +void PatchCache::setupMesh(Patch* newMesh, TextureVertex* vertices) { + // This call ensures the VBO exists and that it is bound + init(Caches::getInstance()); - int8_t transparentQuads = 0; - uint32_t colorKey = 0; + // If we're running out of space, let's clear the entire cache + uint32_t size = newMesh->getSize(); + if (mSize + size > mMaxSize) { + clearCache(); + createVertexBuffer(); + } - if (uint8_t(numColors) < sizeof(uint32_t) * 4) { - for (int8_t i = 0; i < numColors; i++) { - if (colors[i] == 0x0) { - transparentQuads++; - colorKey |= 0x1 << i; - } + // Find a block where we can fit the mesh + BufferBlock* previous = NULL; + BufferBlock* block = mFreeBlocks; + while (block) { + // The mesh fits + if (block->size >= size) { + break; } + previous = block; + block = block->next; } - // If the 9patch is made of only transparent quads - if (transparentQuads == int8_t((width + 1) * (height + 1))) { - return NULL; + // We have enough space left in the buffer, but it's + // too fragmented, let's clear the cache + if (!block) { + clearCache(); + createVertexBuffer(); + previous = NULL; + block = mFreeBlocks; } - const PatchDescription description(bitmapWidth, bitmapHeight, - pixelWidth, pixelHeight, width, height, transparentQuads, colorKey); + // 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); - ssize_t index = mCache.indexOfKey(description); - Patch* mesh = NULL; - if (index >= 0) { - mesh = mCache.valueAt(index); + // Remove the block since we've used it entirely + if (block->size == size) { + if (previous) { + previous->next = block->next; + } else { + mFreeBlocks = block->next; + } + } else { + // Resize the block now that it's occupied + block->offset += size; + block->size -= size; } + mSize += size; +} + +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) { + + const PatchDescription description(bitmapWidth, bitmapHeight, pixelWidth, pixelHeight, patch); + const Patch* mesh = mCache.get(description); + if (!mesh) { - PATCH_LOGD("New patch mesh " - "xCount=%d yCount=%d, w=%.2f h=%.2f, bw=%.2f bh=%.2f", - width, height, pixelWidth, pixelHeight, bitmapWidth, bitmapHeight); - - mesh = new Patch(width, height, transparentQuads); - mesh->updateColorKey(colorKey); - mesh->copy(xDivs, yDivs); - mesh->updateVertices(bitmapWidth, bitmapHeight, 0.0f, 0.0f, pixelWidth, pixelHeight); - - if (mCache.size() >= mMaxEntries) { - delete mCache.valueAt(mCache.size() - 1); - mCache.removeItemsAt(mCache.size() - 1, 1); + 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); } - mCache.add(description, mesh); - } else if (!mesh->matches(xDivs, yDivs, colorKey, transparentQuads)) { - PATCH_LOGD("Patch mesh does not match, refreshing vertices"); - mesh->updateVertices(bitmapWidth, bitmapHeight, 0.0f, 0.0f, pixelWidth, pixelHeight); + if (vertices) { + setupMesh(newMesh, vertices); + } + +#if DEBUG_PATCHES + dumpFreeBlocks("Adding patch"); +#endif + + mCache.put(description, newMesh); + return newMesh; } return mesh; } +#if DEBUG_PATCHES +void PatchCache::dumpFreeBlocks(const char* prefix) { + String8 dump; + BufferBlock* block = mFreeBlocks; + while (block) { + dump.appendFormat("->(%d, %d)", block->offset, block->size); + block = block->next; + } + ALOGD("%s: Free blocks%s", prefix, dump.string()); +} +#endif + }; // namespace uirenderer }; // namespace android |