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