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
26 namespace android {
27 namespace uirenderer {
28
29 ///////////////////////////////////////////////////////////////////////////////
30 // Constructors/destructor
31 ///////////////////////////////////////////////////////////////////////////////
32
PatchCache(RenderState & renderState)33 PatchCache::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
~PatchCache()41 PatchCache::~PatchCache() {
42 clear();
43 }
44
45 ///////////////////////////////////////////////////////////////////////////////
46 // Caching
47 ///////////////////////////////////////////////////////////////////////////////
48
hash() const49 hash_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
compare(const PatchCache::PatchDescription & lhs,const PatchCache::PatchDescription & rhs)58 int PatchCache::PatchDescription::compare(const PatchCache::PatchDescription& lhs,
59 const PatchCache::PatchDescription& rhs) {
60 return memcmp(&lhs, &rhs, sizeof(PatchDescription));
61 }
62
clear()63 void PatchCache::clear() {
64 clearCache();
65
66 if (mMeshBuffer) {
67 mRenderState.meshState().deleteMeshBuffer(mMeshBuffer);
68 mMeshBuffer = 0;
69 mSize = 0;
70 }
71 }
72
clearCache()73 void 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
remove(Vector<patch_pair_t> & patchesToRemove,Res_png_9patch * patch)89 void 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
removeDeferred(Res_png_9patch * patch)99 void 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
clearGarbage()115 void 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
createVertexBuffer()155 void 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 */
setupMesh(Patch * newMesh)166 void 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
224 static const UvMapper sIdentity;
225
get(const uint32_t bitmapWidth,const uint32_t bitmapHeight,const float pixelWidth,const float pixelHeight,const Res_png_9patch * patch)226 const 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
dumpFreeBlocks(const char * prefix)252 void 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