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