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