PatchCache.cpp revision 36fad8f6fcfbc2087b910600ed5a6f9741177d00
1/* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#define LOG_TAG "OpenGLRenderer" 18 19#include <utils/JenkinsHash.h> 20#include <utils/Log.h> 21 22#include "Caches.h" 23#include "PatchCache.h" 24#include "Properties.h" 25 26namespace android { 27namespace uirenderer { 28 29/////////////////////////////////////////////////////////////////////////////// 30// Constructors/destructor 31/////////////////////////////////////////////////////////////////////////////// 32 33PatchCache::PatchCache(): 34 mSize(0), mCache(LruCache<PatchDescription, Patch*>::kUnlimitedCapacity), 35 mMeshBuffer(0), mFreeBlocks(NULL), mGenerationId(0) { 36 char property[PROPERTY_VALUE_MAX]; 37 if (property_get(PROPERTY_PATCH_CACHE_SIZE, property, NULL) > 0) { 38 INIT_LOGD(" Setting patch cache size to %skB", property); 39 mMaxSize = KB(atoi(property)); 40 } else { 41 INIT_LOGD(" Using default patch cache size of %.2fkB", DEFAULT_PATCH_CACHE_SIZE); 42 mMaxSize = KB(DEFAULT_PATCH_CACHE_SIZE); 43 } 44} 45 46PatchCache::~PatchCache() { 47 clear(); 48} 49 50void PatchCache::init(Caches& caches) { 51 bool created = false; 52 if (!mMeshBuffer) { 53 glGenBuffers(1, &mMeshBuffer); 54 created = true; 55 } 56 57 caches.bindMeshBuffer(mMeshBuffer); 58 caches.resetVertexPointers(); 59 60 if (created) { 61 createVertexBuffer(); 62 } 63} 64 65/////////////////////////////////////////////////////////////////////////////// 66// Caching 67/////////////////////////////////////////////////////////////////////////////// 68 69hash_t PatchCache::PatchDescription::hash() const { 70 uint32_t hash = JenkinsHashMix(0, android::hash_type(mPatch)); 71 hash = JenkinsHashMix(hash, mBitmapWidth); 72 hash = JenkinsHashMix(hash, mBitmapHeight); 73 hash = JenkinsHashMix(hash, mPixelWidth); 74 hash = JenkinsHashMix(hash, mPixelHeight); 75 return JenkinsHashWhiten(hash); 76} 77 78int PatchCache::PatchDescription::compare(const PatchCache::PatchDescription& lhs, 79 const PatchCache::PatchDescription& rhs) { 80 return memcmp(&lhs, &rhs, sizeof(PatchDescription)); 81} 82 83void PatchCache::clear() { 84 clearCache(); 85 86 if (mMeshBuffer) { 87 Caches::getInstance().unbindMeshBuffer(); 88 glDeleteBuffers(1, &mMeshBuffer); 89 mMeshBuffer = 0; 90 mSize = 0; 91 } 92} 93 94void PatchCache::clearCache() { 95 LruCache<PatchDescription, Patch*>::Iterator i(mCache); 96 while (i.next()) { 97 delete i.value(); 98 } 99 mCache.clear(); 100 101 BufferBlock* block = mFreeBlocks; 102 while (block) { 103 BufferBlock* next = block->next; 104 delete block; 105 block = next; 106 } 107 mFreeBlocks = NULL; 108} 109 110void PatchCache::remove(Vector<patch_pair_t>& patchesToRemove, Res_png_9patch* patch) { 111 LruCache<PatchDescription, Patch*>::Iterator i(mCache); 112 while (i.next()) { 113 const PatchDescription& key = i.key(); 114 if (key.getPatch() == patch) { 115 patchesToRemove.push(patch_pair_t(&key, i.value())); 116 } 117 } 118} 119 120void PatchCache::removeDeferred(Res_png_9patch* patch) { 121 Mutex::Autolock _l(mLock); 122 mGarbage.push(patch); 123} 124 125void PatchCache::clearGarbage() { 126 Vector<patch_pair_t> patchesToRemove; 127 128 { // scope for the mutex 129 Mutex::Autolock _l(mLock); 130 size_t count = mGarbage.size(); 131 for (size_t i = 0; i < count; i++) { 132 Res_png_9patch* patch = mGarbage[i]; 133 remove(patchesToRemove, patch); 134 // A Res_png_9patch is actually an array of byte that's larger 135 // than sizeof(Res_png_9patch). It must be freed as an array. 136 delete[] (int8_t*) patch; 137 } 138 mGarbage.clear(); 139 } 140 141 // TODO: We could sort patchesToRemove by offset to merge 142 // adjacent free blocks 143 for (size_t i = 0; i < patchesToRemove.size(); i++) { 144 const patch_pair_t& pair = patchesToRemove[i]; 145 146 // Add a new free block to the list 147 const Patch* patch = pair.getSecond(); 148 BufferBlock* block = new BufferBlock(patch->offset, patch->getSize()); 149 block->next = mFreeBlocks; 150 mFreeBlocks = block; 151 152 mSize -= patch->getSize(); 153 154 mCache.remove(*pair.getFirst()); 155 } 156 157#if DEBUG_PATCHES 158 if (patchesToRemove.size() > 0) { 159 dumpFreeBlocks("Removed garbage"); 160 } 161#endif 162} 163 164void PatchCache::createVertexBuffer() { 165 glBufferData(GL_ARRAY_BUFFER, mMaxSize, NULL, GL_DYNAMIC_DRAW); 166 mSize = 0; 167 mFreeBlocks = new BufferBlock(0, mMaxSize); 168 mGenerationId++; 169} 170 171/** 172 * Sets the mesh's offsets and copies its associated vertices into 173 * the mesh buffer (VBO). 174 */ 175void PatchCache::setupMesh(Patch* newMesh, TextureVertex* vertices) { 176 // This call ensures the VBO exists and that it is bound 177 init(Caches::getInstance()); 178 179 // If we're running out of space, let's clear the entire cache 180 uint32_t size = newMesh->getSize(); 181 if (mSize + size > mMaxSize) { 182 clearCache(); 183 createVertexBuffer(); 184 } 185 186 // Find a block where we can fit the mesh 187 BufferBlock* previous = NULL; 188 BufferBlock* block = mFreeBlocks; 189 while (block) { 190 // The mesh fits 191 if (block->size >= size) { 192 break; 193 } 194 previous = block; 195 block = block->next; 196 } 197 198 // We have enough space left in the buffer, but it's 199 // too fragmented, let's clear the cache 200 if (!block) { 201 clearCache(); 202 createVertexBuffer(); 203 previous = NULL; 204 block = mFreeBlocks; 205 } 206 207 // Copy the 9patch mesh in the VBO 208 newMesh->offset = (GLintptr) (block->offset); 209 newMesh->textureOffset = newMesh->offset + gMeshTextureOffset; 210 glBufferSubData(GL_ARRAY_BUFFER, newMesh->offset, size, vertices); 211 212 // Remove the block since we've used it entirely 213 if (block->size == size) { 214 if (previous) { 215 previous->next = block->next; 216 } else { 217 mFreeBlocks = block->next; 218 } 219 } else { 220 // Resize the block now that it's occupied 221 block->offset += size; 222 block->size -= size; 223 } 224 225 mSize += size; 226} 227 228const Patch* PatchCache::get(const AssetAtlas::Entry* entry, 229 const uint32_t bitmapWidth, const uint32_t bitmapHeight, 230 const float pixelWidth, const float pixelHeight, const Res_png_9patch* patch) { 231 232 const PatchDescription description(bitmapWidth, bitmapHeight, pixelWidth, pixelHeight, patch); 233 const Patch* mesh = mCache.get(description); 234 235 if (!mesh) { 236 Patch* newMesh = new Patch(); 237 TextureVertex* vertices; 238 239 if (entry) { 240 // An atlas entry has a UV mapper 241 vertices = newMesh->createMesh(bitmapWidth, bitmapHeight, 242 pixelWidth, pixelHeight, entry->uvMapper, patch); 243 } else { 244 vertices = newMesh->createMesh(bitmapWidth, bitmapHeight, 245 pixelWidth, pixelHeight, patch); 246 } 247 248 if (vertices) { 249 setupMesh(newMesh, vertices); 250 } 251 252#if DEBUG_PATCHES 253 dumpFreeBlocks("Adding patch"); 254#endif 255 256 mCache.put(description, newMesh); 257 return newMesh; 258 } 259 260 return mesh; 261} 262 263#if DEBUG_PATCHES 264void PatchCache::dumpFreeBlocks(const char* prefix) { 265 String8 dump; 266 BufferBlock* block = mFreeBlocks; 267 while (block) { 268 dump.appendFormat("->(%d, %d)", block->offset, block->size); 269 block = block->next; 270 } 271 ALOGD("%s: Free blocks%s", prefix, dump.string()); 272} 273#endif 274 275}; // namespace uirenderer 276}; // namespace android 277