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 mGarbage.push(patch); 123 } 124 125 void 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 remove(patchesToRemove, mGarbage[i]); 133 } 134 mGarbage.clear(); 135 } 136 137 // TODO: We could sort patchesToRemove by offset to merge 138 // adjacent free blocks 139 for (size_t i = 0; i < patchesToRemove.size(); i++) { 140 const patch_pair_t& pair = patchesToRemove[i]; 141 142 // Add a new free block to the list 143 const Patch* patch = pair.getSecond(); 144 BufferBlock* block = new BufferBlock(patch->offset, patch->getSize()); 145 block->next = mFreeBlocks; 146 mFreeBlocks = block; 147 148 mSize -= patch->getSize(); 149 150 mCache.remove(*pair.getFirst()); 151 } 152 153 #if DEBUG_PATCHES 154 if (patchesToRemove.size() > 0) { 155 dumpFreeBlocks("Removed garbage"); 156 } 157 #endif 158 } 159 160 void PatchCache::createVertexBuffer() { 161 glBufferData(GL_ARRAY_BUFFER, mMaxSize, NULL, GL_DYNAMIC_DRAW); 162 mSize = 0; 163 mFreeBlocks = new BufferBlock(0, mMaxSize); 164 mGenerationId++; 165 } 166 167 /** 168 * Sets the mesh's offsets and copies its associated vertices into 169 * the mesh buffer (VBO). 170 */ 171 void PatchCache::setupMesh(Patch* newMesh, TextureVertex* vertices) { 172 // This call ensures the VBO exists and that it is bound 173 init(Caches::getInstance()); 174 175 // If we're running out of space, let's clear the entire cache 176 uint32_t size = newMesh->getSize(); 177 if (mSize + size > mMaxSize) { 178 clearCache(); 179 createVertexBuffer(); 180 } 181 182 // Find a block where we can fit the mesh 183 BufferBlock* previous = NULL; 184 BufferBlock* block = mFreeBlocks; 185 while (block) { 186 // The mesh fits 187 if (block->size >= size) { 188 break; 189 } 190 previous = block; 191 block = block->next; 192 } 193 194 // We have enough space left in the buffer, but it's 195 // too fragmented, let's clear the cache 196 if (!block) { 197 clearCache(); 198 createVertexBuffer(); 199 previous = NULL; 200 block = mFreeBlocks; 201 } 202 203 // Copy the 9patch mesh in the VBO 204 newMesh->offset = (GLintptr) (block->offset); 205 newMesh->textureOffset = newMesh->offset + gMeshTextureOffset; 206 glBufferSubData(GL_ARRAY_BUFFER, newMesh->offset, size, vertices); 207 208 // Remove the block since we've used it entirely 209 if (block->size == size) { 210 if (previous) { 211 previous->next = block->next; 212 } else { 213 mFreeBlocks = block->next; 214 } 215 } else { 216 // Resize the block now that it's occupied 217 block->offset += size; 218 block->size -= size; 219 } 220 221 mSize += size; 222 } 223 224 const Patch* PatchCache::get(const AssetAtlas::Entry* entry, 225 const uint32_t bitmapWidth, const uint32_t bitmapHeight, 226 const float pixelWidth, const float pixelHeight, const Res_png_9patch* patch) { 227 228 const PatchDescription description(bitmapWidth, bitmapHeight, pixelWidth, pixelHeight, patch); 229 const Patch* mesh = mCache.get(description); 230 231 if (!mesh) { 232 Patch* newMesh = new Patch(); 233 TextureVertex* vertices; 234 235 if (entry) { 236 // An atlas entry has a UV mapper 237 vertices = newMesh->createMesh(bitmapWidth, bitmapHeight, 238 pixelWidth, pixelHeight, entry->uvMapper, patch); 239 } else { 240 vertices = newMesh->createMesh(bitmapWidth, bitmapHeight, 241 pixelWidth, pixelHeight, patch); 242 } 243 244 if (vertices) { 245 setupMesh(newMesh, vertices); 246 } 247 248 #if DEBUG_PATCHES 249 dumpFreeBlocks("Adding patch"); 250 #endif 251 252 mCache.put(description, newMesh); 253 return newMesh; 254 } 255 256 return mesh; 257 } 258 259 #if DEBUG_PATCHES 260 void PatchCache::dumpFreeBlocks(const char* prefix) { 261 String8 dump; 262 BufferBlock* block = mFreeBlocks; 263 while (block) { 264 dump.appendFormat("->(%d, %d)", block->offset, block->size); 265 block = block->next; 266 } 267 ALOGD("%s: Free blocks%s", prefix, dump.string()); 268 } 269 #endif 270 271 }; // namespace uirenderer 272 }; // namespace android 273