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 26 namespace android { 27 namespace uirenderer { 28 29 /////////////////////////////////////////////////////////////////////////////// 30 // Constructors/destructor 31 /////////////////////////////////////////////////////////////////////////////// 32 33 PatchCache::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 46 PatchCache::~PatchCache() { 47 clear(); 48 } 49 50 void 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 69 hash_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 78 int PatchCache::PatchDescription::compare(const PatchCache::PatchDescription& lhs, 79 const PatchCache::PatchDescription& rhs) { 80 return memcmp(&lhs, &rhs, sizeof(PatchDescription)); 81 } 82 83 void 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 94 void 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 110 void 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 120 void PatchCache::removeDeferred(Res_png_9patch* patch) { 121 Mutex::Autolock _l(mLock); 122 123 // Assert that patch is not already garbage 124 size_t count = mGarbage.size(); 125 for (size_t i = 0; i < count; i++) { 126 if (patch == mGarbage[i]) { 127 patch = NULL; 128 break; 129 } 130 } 131 LOG_ALWAYS_FATAL_IF(patch == NULL); 132 133 mGarbage.push(patch); 134 } 135 136 void PatchCache::clearGarbage() { 137 Vector<patch_pair_t> patchesToRemove; 138 139 { // scope for the mutex 140 Mutex::Autolock _l(mLock); 141 size_t count = mGarbage.size(); 142 for (size_t i = 0; i < count; i++) { 143 Res_png_9patch* patch = mGarbage[i]; 144 remove(patchesToRemove, patch); 145 // A Res_png_9patch is actually an array of byte that's larger 146 // than sizeof(Res_png_9patch). It must be freed as an array. 147 delete[] (int8_t*) patch; 148 } 149 mGarbage.clear(); 150 } 151 152 // TODO: We could sort patchesToRemove by offset to merge 153 // adjacent free blocks 154 for (size_t i = 0; i < patchesToRemove.size(); i++) { 155 const patch_pair_t& pair = patchesToRemove[i]; 156 157 // Release the patch and mark the space in the free list 158 Patch* patch = pair.getSecond(); 159 BufferBlock* block = new BufferBlock(patch->offset, patch->getSize()); 160 block->next = mFreeBlocks; 161 mFreeBlocks = block; 162 163 mSize -= patch->getSize(); 164 165 mCache.remove(*pair.getFirst()); 166 delete patch; 167 } 168 169 #if DEBUG_PATCHES 170 if (patchesToRemove.size() > 0) { 171 dumpFreeBlocks("Removed garbage"); 172 } 173 #endif 174 } 175 176 void PatchCache::createVertexBuffer() { 177 glBufferData(GL_ARRAY_BUFFER, mMaxSize, NULL, GL_DYNAMIC_DRAW); 178 mSize = 0; 179 mFreeBlocks = new BufferBlock(0, mMaxSize); 180 mGenerationId++; 181 } 182 183 /** 184 * Sets the mesh's offsets and copies its associated vertices into 185 * the mesh buffer (VBO). 186 */ 187 void PatchCache::setupMesh(Patch* newMesh, TextureVertex* vertices) { 188 // This call ensures the VBO exists and that it is bound 189 init(Caches::getInstance()); 190 191 // If we're running out of space, let's clear the entire cache 192 uint32_t size = newMesh->getSize(); 193 if (mSize + size > mMaxSize) { 194 clearCache(); 195 createVertexBuffer(); 196 } 197 198 // Find a block where we can fit the mesh 199 BufferBlock* previous = NULL; 200 BufferBlock* block = mFreeBlocks; 201 while (block) { 202 // The mesh fits 203 if (block->size >= size) { 204 break; 205 } 206 previous = block; 207 block = block->next; 208 } 209 210 // We have enough space left in the buffer, but it's 211 // too fragmented, let's clear the cache 212 if (!block) { 213 clearCache(); 214 createVertexBuffer(); 215 previous = NULL; 216 block = mFreeBlocks; 217 } 218 219 // Copy the 9patch mesh in the VBO 220 newMesh->offset = (GLintptr) (block->offset); 221 newMesh->textureOffset = newMesh->offset + gMeshTextureOffset; 222 glBufferSubData(GL_ARRAY_BUFFER, newMesh->offset, size, vertices); 223 224 // Remove the block since we've used it entirely 225 if (block->size == size) { 226 if (previous) { 227 previous->next = block->next; 228 } else { 229 mFreeBlocks = block->next; 230 } 231 delete block; 232 } else { 233 // Resize the block now that it's occupied 234 block->offset += size; 235 block->size -= size; 236 } 237 238 mSize += size; 239 } 240 241 const Patch* PatchCache::get(const AssetAtlas::Entry* entry, 242 const uint32_t bitmapWidth, const uint32_t bitmapHeight, 243 const float pixelWidth, const float pixelHeight, const Res_png_9patch* patch) { 244 245 const PatchDescription description(bitmapWidth, bitmapHeight, pixelWidth, pixelHeight, patch); 246 const Patch* mesh = mCache.get(description); 247 248 if (!mesh) { 249 Patch* newMesh = new Patch(); 250 TextureVertex* vertices; 251 252 if (entry) { 253 // An atlas entry has a UV mapper 254 vertices = newMesh->createMesh(bitmapWidth, bitmapHeight, 255 pixelWidth, pixelHeight, entry->uvMapper, patch); 256 } else { 257 vertices = newMesh->createMesh(bitmapWidth, bitmapHeight, 258 pixelWidth, pixelHeight, patch); 259 } 260 261 if (vertices) { 262 setupMesh(newMesh, vertices); 263 } 264 265 #if DEBUG_PATCHES 266 dumpFreeBlocks("Adding patch"); 267 #endif 268 269 mCache.put(description, newMesh); 270 return newMesh; 271 } 272 273 return mesh; 274 } 275 276 #if DEBUG_PATCHES 277 void PatchCache::dumpFreeBlocks(const char* prefix) { 278 String8 dump; 279 BufferBlock* block = mFreeBlocks; 280 while (block) { 281 dump.appendFormat("->(%d, %d)", block->offset, block->size); 282 block = block->next; 283 } 284 ALOGD("%s: Free blocks%s", prefix, dump.string()); 285 } 286 #endif 287 288 }; // namespace uirenderer 289 }; // namespace android 290