• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
PatchCache()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 
~PatchCache()46 PatchCache::~PatchCache() {
47     clear();
48 }
49 
init(Caches & caches)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 
hash() const69 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 
compare(const PatchCache::PatchDescription & lhs,const PatchCache::PatchDescription & rhs)78 int PatchCache::PatchDescription::compare(const PatchCache::PatchDescription& lhs,
79             const PatchCache::PatchDescription& rhs) {
80     return memcmp(&lhs, &rhs, sizeof(PatchDescription));
81 }
82 
clear()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 
clearCache()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 
remove(Vector<patch_pair_t> & patchesToRemove,Res_png_9patch * patch)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 
removeDeferred(Res_png_9patch * patch)120 void PatchCache::removeDeferred(Res_png_9patch* patch) {
121     Mutex::Autolock _l(mLock);
122 
123     // Assert that patch is not already garbage
124     size_t count = mGarbage.size();
125     for (size_t i = 0; i < count; i++) {
126         if (patch == mGarbage[i]) {
127             patch = NULL;
128             break;
129         }
130     }
131     LOG_ALWAYS_FATAL_IF(patch == NULL);
132 
133     mGarbage.push(patch);
134 }
135 
clearGarbage()136 void PatchCache::clearGarbage() {
137     Vector<patch_pair_t> patchesToRemove;
138 
139     { // scope for the mutex
140         Mutex::Autolock _l(mLock);
141         size_t count = mGarbage.size();
142         for (size_t i = 0; i < count; i++) {
143             Res_png_9patch* patch = mGarbage[i];
144             remove(patchesToRemove, patch);
145             // A Res_png_9patch is actually an array of byte that's larger
146             // than sizeof(Res_png_9patch). It must be freed as an array.
147             delete[] (int8_t*) patch;
148         }
149         mGarbage.clear();
150     }
151 
152     // TODO: We could sort patchesToRemove by offset to merge
153     // adjacent free blocks
154     for (size_t i = 0; i < patchesToRemove.size(); i++) {
155         const patch_pair_t& pair = patchesToRemove[i];
156 
157         // Release the patch and mark the space in the free list
158         Patch* patch = pair.getSecond();
159         BufferBlock* block = new BufferBlock(patch->offset, patch->getSize());
160         block->next = mFreeBlocks;
161         mFreeBlocks = block;
162 
163         mSize -= patch->getSize();
164 
165         mCache.remove(*pair.getFirst());
166         delete patch;
167     }
168 
169 #if DEBUG_PATCHES
170     if (patchesToRemove.size() > 0) {
171         dumpFreeBlocks("Removed garbage");
172     }
173 #endif
174 }
175 
createVertexBuffer()176 void PatchCache::createVertexBuffer() {
177     glBufferData(GL_ARRAY_BUFFER, mMaxSize, NULL, GL_DYNAMIC_DRAW);
178     mSize = 0;
179     mFreeBlocks = new BufferBlock(0, mMaxSize);
180     mGenerationId++;
181 }
182 
183 /**
184  * Sets the mesh's offsets and copies its associated vertices into
185  * the mesh buffer (VBO).
186  */
setupMesh(Patch * newMesh,TextureVertex * vertices)187 void PatchCache::setupMesh(Patch* newMesh, TextureVertex* vertices) {
188     // This call ensures the VBO exists and that it is bound
189     init(Caches::getInstance());
190 
191     // If we're running out of space, let's clear the entire cache
192     uint32_t size = newMesh->getSize();
193     if (mSize + size > mMaxSize) {
194         clearCache();
195         createVertexBuffer();
196     }
197 
198     // Find a block where we can fit the mesh
199     BufferBlock* previous = NULL;
200     BufferBlock* block = mFreeBlocks;
201     while (block) {
202         // The mesh fits
203         if (block->size >= size) {
204             break;
205         }
206         previous = block;
207         block = block->next;
208     }
209 
210     // We have enough space left in the buffer, but it's
211     // too fragmented, let's clear the cache
212     if (!block) {
213         clearCache();
214         createVertexBuffer();
215         previous = NULL;
216         block = mFreeBlocks;
217     }
218 
219     // Copy the 9patch mesh in the VBO
220     newMesh->offset = (GLintptr) (block->offset);
221     newMesh->textureOffset = newMesh->offset + gMeshTextureOffset;
222     glBufferSubData(GL_ARRAY_BUFFER, newMesh->offset, size, vertices);
223 
224     // Remove the block since we've used it entirely
225     if (block->size == size) {
226         if (previous) {
227             previous->next = block->next;
228         } else {
229             mFreeBlocks = block->next;
230         }
231         delete block;
232     } else {
233         // Resize the block now that it's occupied
234         block->offset += size;
235         block->size -= size;
236     }
237 
238     mSize += size;
239 }
240 
get(const AssetAtlas::Entry * entry,const uint32_t bitmapWidth,const uint32_t bitmapHeight,const float pixelWidth,const float pixelHeight,const Res_png_9patch * patch)241 const Patch* PatchCache::get(const AssetAtlas::Entry* entry,
242         const uint32_t bitmapWidth, const uint32_t bitmapHeight,
243         const float pixelWidth, const float pixelHeight, const Res_png_9patch* patch) {
244 
245     const PatchDescription description(bitmapWidth, bitmapHeight, pixelWidth, pixelHeight, patch);
246     const Patch* mesh = mCache.get(description);
247 
248     if (!mesh) {
249         Patch* newMesh = new Patch();
250         TextureVertex* vertices;
251 
252         if (entry) {
253             // An atlas entry has a UV mapper
254             vertices = newMesh->createMesh(bitmapWidth, bitmapHeight,
255                     pixelWidth, pixelHeight, entry->uvMapper, patch);
256         } else {
257             vertices = newMesh->createMesh(bitmapWidth, bitmapHeight,
258                     pixelWidth, pixelHeight, patch);
259         }
260 
261         if (vertices) {
262             setupMesh(newMesh, vertices);
263         }
264 
265 #if DEBUG_PATCHES
266         dumpFreeBlocks("Adding patch");
267 #endif
268 
269         mCache.put(description, newMesh);
270         return newMesh;
271     }
272 
273     return mesh;
274 }
275 
276 #if DEBUG_PATCHES
dumpFreeBlocks(const char * prefix)277 void PatchCache::dumpFreeBlocks(const char* prefix) {
278     String8 dump;
279     BufferBlock* block = mFreeBlocks;
280     while (block) {
281         dump.appendFormat("->(%d, %d)", block->offset, block->size);
282         block = block->next;
283     }
284     ALOGD("%s: Free blocks%s", prefix, dump.string());
285 }
286 #endif
287 
288 }; // namespace uirenderer
289 }; // namespace android
290