OSDN Git Service

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