blob: 8a446043b70f76b3d23534cd96bbeec788a95ef4 [file] [log] [blame]
Romain Guyf7f93552010-07-08 19:17:03 -07001/*
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
Romain Guy3b748a42013-04-17 18:54:38 -070019#include <utils/JenkinsHash.h>
Romain Guyf7f93552010-07-08 19:17:03 -070020#include <utils/Log.h>
Romain Guyf7f93552010-07-08 19:17:03 -070021
Romain Guy3b748a42013-04-17 18:54:38 -070022#include "Caches.h"
Romain Guyf7f93552010-07-08 19:17:03 -070023#include "PatchCache.h"
Romain Guyfb8b7632010-08-23 21:05:08 -070024#include "Properties.h"
Romain Guyf7f93552010-07-08 19:17:03 -070025
26namespace android {
27namespace uirenderer {
28
29///////////////////////////////////////////////////////////////////////////////
30// Constructors/destructor
31///////////////////////////////////////////////////////////////////////////////
32
Romain Guy4c2547f2013-06-11 16:19:24 -070033PatchCache::PatchCache():
34 mSize(0), mCache(LruCache<PatchDescription, Patch*>::kUnlimitedCapacity),
Romain Guye3b0a012013-06-26 15:45:41 -070035 mMeshBuffer(0), mFreeBlocks(NULL), mGenerationId(0) {
Romain Guy3b748a42013-04-17 18:54:38 -070036 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 }
Romain Guyf7f93552010-07-08 19:17:03 -070044}
45
46PatchCache::~PatchCache() {
47 clear();
48}
49
Romain Guy3b748a42013-04-17 18:54:38 -070050void PatchCache::init(Caches& caches) {
Romain Guy7d9b1b32013-05-28 14:25:09 -070051 bool created = false;
52 if (!mMeshBuffer) {
53 glGenBuffers(1, &mMeshBuffer);
54 created = true;
55 }
56
Romain Guy3b748a42013-04-17 18:54:38 -070057 caches.bindMeshBuffer(mMeshBuffer);
58 caches.resetVertexPointers();
59
Romain Guy7d9b1b32013-05-28 14:25:09 -070060 if (created) {
Romain Guy4c2547f2013-06-11 16:19:24 -070061 createVertexBuffer();
Romain Guy7d9b1b32013-05-28 14:25:09 -070062 }
Romain Guy3b748a42013-04-17 18:54:38 -070063}
64
Romain Guyf7f93552010-07-08 19:17:03 -070065///////////////////////////////////////////////////////////////////////////////
Romain Guyf7f93552010-07-08 19:17:03 -070066// Caching
67///////////////////////////////////////////////////////////////////////////////
68
Romain Guy3b748a42013-04-17 18:54:38 -070069hash_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}
Romain Guy13ba0052013-02-15 12:47:26 -080077
Romain Guy3b748a42013-04-17 18:54:38 -070078int PatchCache::PatchDescription::compare(const PatchCache::PatchDescription& lhs,
79 const PatchCache::PatchDescription& rhs) {
80 return memcmp(&lhs, &rhs, sizeof(PatchDescription));
Romain Guy13ba0052013-02-15 12:47:26 -080081}
82
Romain Guyf7f93552010-07-08 19:17:03 -070083void PatchCache::clear() {
Romain Guy3b748a42013-04-17 18:54:38 -070084 clearCache();
Romain Guy7d9b1b32013-05-28 14:25:09 -070085
86 if (mMeshBuffer) {
87 Caches::getInstance().unbindMeshBuffer();
88 glDeleteBuffers(1, &mMeshBuffer);
89 mMeshBuffer = 0;
90 mSize = 0;
91 }
Romain Guy3b748a42013-04-17 18:54:38 -070092}
93
94void PatchCache::clearCache() {
95 LruCache<PatchDescription, Patch*>::Iterator i(mCache);
96 while (i.next()) {
Romain Guy3b748a42013-04-17 18:54:38 -070097 delete i.value();
Romain Guy2728f962010-10-08 18:36:15 -070098 }
Romain Guyf7f93552010-07-08 19:17:03 -070099 mCache.clear();
Romain Guye3b0a012013-06-26 15:45:41 -0700100
101 BufferBlock* block = mFreeBlocks;
102 while (block) {
103 BufferBlock* next = block->next;
104 delete block;
105 block = next;
106 }
107 mFreeBlocks = NULL;
108}
109
110void 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
120void PatchCache::removeDeferred(Res_png_9patch* patch) {
121 Mutex::Autolock _l(mLock);
122 mGarbage.push(patch);
123}
124
125void PatchCache::clearGarbage() {
126 Vector<patch_pair_t> patchesToRemove;
127
128 { // scope for the mutex
129 Mutex::Autolock _l(mLock);
130 size_t count = mGarbage.size();
131 for (size_t i = 0; i < count; i++) {
Sangkyu Lee36fad8f2014-01-09 14:11:57 +0900132 Res_png_9patch* patch = mGarbage[i];
133 remove(patchesToRemove, patch);
134 // A Res_png_9patch is actually an array of byte that's larger
135 // than sizeof(Res_png_9patch). It must be freed as an array.
136 delete[] (int8_t*) patch;
Romain Guye3b0a012013-06-26 15:45:41 -0700137 }
138 mGarbage.clear();
139 }
140
141 // TODO: We could sort patchesToRemove by offset to merge
142 // adjacent free blocks
143 for (size_t i = 0; i < patchesToRemove.size(); i++) {
144 const patch_pair_t& pair = patchesToRemove[i];
145
146 // Add a new free block to the list
147 const Patch* patch = pair.getSecond();
148 BufferBlock* block = new BufferBlock(patch->offset, patch->getSize());
149 block->next = mFreeBlocks;
150 mFreeBlocks = block;
151
152 mSize -= patch->getSize();
153
154 mCache.remove(*pair.getFirst());
155 }
156
157#if DEBUG_PATCHES
158 if (patchesToRemove.size() > 0) {
159 dumpFreeBlocks("Removed garbage");
160 }
161#endif
Romain Guyf7f93552010-07-08 19:17:03 -0700162}
163
Romain Guy4c2547f2013-06-11 16:19:24 -0700164void PatchCache::createVertexBuffer() {
165 glBufferData(GL_ARRAY_BUFFER, mMaxSize, NULL, GL_DYNAMIC_DRAW);
166 mSize = 0;
Romain Guye3b0a012013-06-26 15:45:41 -0700167 mFreeBlocks = new BufferBlock(0, mMaxSize);
Romain Guy4c2547f2013-06-11 16:19:24 -0700168 mGenerationId++;
169}
170
Romain Guye3b0a012013-06-26 15:45:41 -0700171/**
172 * Sets the mesh's offsets and copies its associated vertices into
173 * the mesh buffer (VBO).
174 */
175void PatchCache::setupMesh(Patch* newMesh, TextureVertex* vertices) {
176 // This call ensures the VBO exists and that it is bound
177 init(Caches::getInstance());
178
179 // If we're running out of space, let's clear the entire cache
180 uint32_t size = newMesh->getSize();
181 if (mSize + size > mMaxSize) {
182 clearCache();
183 createVertexBuffer();
184 }
185
186 // Find a block where we can fit the mesh
187 BufferBlock* previous = NULL;
188 BufferBlock* block = mFreeBlocks;
189 while (block) {
190 // The mesh fits
191 if (block->size >= size) {
192 break;
193 }
194 previous = block;
195 block = block->next;
196 }
197
198 // We have enough space left in the buffer, but it's
199 // too fragmented, let's clear the cache
200 if (!block) {
201 clearCache();
202 createVertexBuffer();
203 previous = NULL;
204 block = mFreeBlocks;
205 }
206
207 // Copy the 9patch mesh in the VBO
208 newMesh->offset = (GLintptr) (block->offset);
209 newMesh->textureOffset = newMesh->offset + gMeshTextureOffset;
210 glBufferSubData(GL_ARRAY_BUFFER, newMesh->offset, size, vertices);
211
212 // Remove the block since we've used it entirely
213 if (block->size == size) {
214 if (previous) {
215 previous->next = block->next;
216 } else {
217 mFreeBlocks = block->next;
218 }
219 } else {
220 // Resize the block now that it's occupied
221 block->offset += size;
222 block->size -= size;
223 }
224
225 mSize += size;
226}
227
Romain Guy3b748a42013-04-17 18:54:38 -0700228const Patch* PatchCache::get(const AssetAtlas::Entry* entry,
229 const uint32_t bitmapWidth, const uint32_t bitmapHeight,
230 const float pixelWidth, const float pixelHeight, const Res_png_9patch* patch) {
Romain Guy4bb94202010-10-12 15:59:26 -0700231
Romain Guy3b748a42013-04-17 18:54:38 -0700232 const PatchDescription description(bitmapWidth, bitmapHeight, pixelWidth, pixelHeight, patch);
233 const Patch* mesh = mCache.get(description);
Romain Guy2728f962010-10-08 18:36:15 -0700234
Romain Guyf7f93552010-07-08 19:17:03 -0700235 if (!mesh) {
Romain Guy3b748a42013-04-17 18:54:38 -0700236 Patch* newMesh = new Patch();
237 TextureVertex* vertices;
Romain Guy2728f962010-10-08 18:36:15 -0700238
Romain Guy3b748a42013-04-17 18:54:38 -0700239 if (entry) {
Romain Guye3b0a012013-06-26 15:45:41 -0700240 // An atlas entry has a UV mapper
Romain Guy3b748a42013-04-17 18:54:38 -0700241 vertices = newMesh->createMesh(bitmapWidth, bitmapHeight,
Romain Guy03c00b52013-06-20 18:30:28 -0700242 pixelWidth, pixelHeight, entry->uvMapper, patch);
Romain Guy3b748a42013-04-17 18:54:38 -0700243 } else {
244 vertices = newMesh->createMesh(bitmapWidth, bitmapHeight,
Romain Guy03c00b52013-06-20 18:30:28 -0700245 pixelWidth, pixelHeight, patch);
Romain Guy2728f962010-10-08 18:36:15 -0700246 }
247
Romain Guy3b748a42013-04-17 18:54:38 -0700248 if (vertices) {
Romain Guye3b0a012013-06-26 15:45:41 -0700249 setupMesh(newMesh, vertices);
Romain Guy3b748a42013-04-17 18:54:38 -0700250 }
251
Romain Guye3b0a012013-06-26 15:45:41 -0700252#if DEBUG_PATCHES
253 dumpFreeBlocks("Adding patch");
254#endif
255
Romain Guy3b748a42013-04-17 18:54:38 -0700256 mCache.put(description, newMesh);
257 return newMesh;
Romain Guyf7f93552010-07-08 19:17:03 -0700258 }
259
260 return mesh;
261}
262
Romain Guye3b0a012013-06-26 15:45:41 -0700263#if DEBUG_PATCHES
264void PatchCache::dumpFreeBlocks(const char* prefix) {
265 String8 dump;
266 BufferBlock* block = mFreeBlocks;
267 while (block) {
268 dump.appendFormat("->(%d, %d)", block->offset, block->size);
269 block = block->next;
270 }
271 ALOGD("%s: Free blocks%s", prefix, dump.string());
272}
273#endif
274
Romain Guyf7f93552010-07-08 19:17:03 -0700275}; // namespace uirenderer
276}; // namespace android