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