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