• 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 #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