blob: 98812805259e278ed29e7b25db0da8b27cf2f8b4 [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
Romain Guy3b748a42013-04-17 18:54:38 -070017#include <utils/JenkinsHash.h>
Romain Guyf7f93552010-07-08 19:17:03 -070018#include <utils/Log.h>
Romain Guyf7f93552010-07-08 19:17:03 -070019
Romain Guy3b748a42013-04-17 18:54:38 -070020#include "Caches.h"
Tom Hudson2dc236b2014-10-15 15:46:42 -040021#include "Patch.h"
Romain Guyf7f93552010-07-08 19:17:03 -070022#include "PatchCache.h"
Romain Guyfb8b7632010-08-23 21:05:08 -070023#include "Properties.h"
Chris Craik96a5c4c2015-01-27 15:46:35 -080024#include "renderstate/RenderState.h"
Romain Guyf7f93552010-07-08 19:17:03 -070025
26namespace android {
27namespace uirenderer {
28
29///////////////////////////////////////////////////////////////////////////////
30// Constructors/destructor
31///////////////////////////////////////////////////////////////////////////////
32
Chris Craik96a5c4c2015-01-27 15:46:35 -080033PatchCache::PatchCache(RenderState& renderState)
34 : mRenderState(renderState)
35 , mSize(0)
36 , mCache(LruCache<PatchDescription, Patch*>::kUnlimitedCapacity)
37 , mMeshBuffer(0)
38 , mFreeBlocks(nullptr)
39 , mGenerationId(0) {
Romain Guy3b748a42013-04-17 18:54:38 -070040 char property[PROPERTY_VALUE_MAX];
Chris Craikd41c4d82015-01-05 15:51:13 -080041 if (property_get(PROPERTY_PATCH_CACHE_SIZE, property, nullptr) > 0) {
Romain Guy3b748a42013-04-17 18:54:38 -070042 INIT_LOGD(" Setting patch cache size to %skB", property);
43 mMaxSize = KB(atoi(property));
44 } else {
45 INIT_LOGD(" Using default patch cache size of %.2fkB", DEFAULT_PATCH_CACHE_SIZE);
46 mMaxSize = KB(DEFAULT_PATCH_CACHE_SIZE);
47 }
Romain Guyf7f93552010-07-08 19:17:03 -070048}
49
50PatchCache::~PatchCache() {
51 clear();
52}
53
Chris Craik96a5c4c2015-01-27 15:46:35 -080054void PatchCache::init() {
Romain Guy7d9b1b32013-05-28 14:25:09 -070055 bool created = false;
56 if (!mMeshBuffer) {
57 glGenBuffers(1, &mMeshBuffer);
58 created = true;
59 }
60
Chris Craik96a5c4c2015-01-27 15:46:35 -080061 mRenderState.meshState().bindMeshBuffer(mMeshBuffer);
62 mRenderState.meshState().resetVertexPointers();
Romain Guy3b748a42013-04-17 18:54:38 -070063
Romain Guy7d9b1b32013-05-28 14:25:09 -070064 if (created) {
Romain Guy4c2547f2013-06-11 16:19:24 -070065 createVertexBuffer();
Romain Guy7d9b1b32013-05-28 14:25:09 -070066 }
Romain Guy3b748a42013-04-17 18:54:38 -070067}
68
Romain Guyf7f93552010-07-08 19:17:03 -070069///////////////////////////////////////////////////////////////////////////////
Romain Guyf7f93552010-07-08 19:17:03 -070070// Caching
71///////////////////////////////////////////////////////////////////////////////
72
Romain Guy3b748a42013-04-17 18:54:38 -070073hash_t PatchCache::PatchDescription::hash() const {
74 uint32_t hash = JenkinsHashMix(0, android::hash_type(mPatch));
75 hash = JenkinsHashMix(hash, mBitmapWidth);
76 hash = JenkinsHashMix(hash, mBitmapHeight);
77 hash = JenkinsHashMix(hash, mPixelWidth);
78 hash = JenkinsHashMix(hash, mPixelHeight);
79 return JenkinsHashWhiten(hash);
80}
Romain Guy13ba0052013-02-15 12:47:26 -080081
Romain Guy3b748a42013-04-17 18:54:38 -070082int PatchCache::PatchDescription::compare(const PatchCache::PatchDescription& lhs,
83 const PatchCache::PatchDescription& rhs) {
84 return memcmp(&lhs, &rhs, sizeof(PatchDescription));
Romain Guy13ba0052013-02-15 12:47:26 -080085}
86
Romain Guyf7f93552010-07-08 19:17:03 -070087void PatchCache::clear() {
Romain Guy3b748a42013-04-17 18:54:38 -070088 clearCache();
Romain Guy7d9b1b32013-05-28 14:25:09 -070089
90 if (mMeshBuffer) {
Chris Craik96a5c4c2015-01-27 15:46:35 -080091 mRenderState.meshState().unbindMeshBuffer();
Romain Guy7d9b1b32013-05-28 14:25:09 -070092 glDeleteBuffers(1, &mMeshBuffer);
93 mMeshBuffer = 0;
94 mSize = 0;
95 }
Romain Guy3b748a42013-04-17 18:54:38 -070096}
97
98void PatchCache::clearCache() {
99 LruCache<PatchDescription, Patch*>::Iterator i(mCache);
100 while (i.next()) {
Romain Guy3b748a42013-04-17 18:54:38 -0700101 delete i.value();
Romain Guy2728f962010-10-08 18:36:15 -0700102 }
Romain Guyf7f93552010-07-08 19:17:03 -0700103 mCache.clear();
Romain Guye3b0a012013-06-26 15:45:41 -0700104
105 BufferBlock* block = mFreeBlocks;
106 while (block) {
107 BufferBlock* next = block->next;
108 delete block;
109 block = next;
110 }
Chris Craikd41c4d82015-01-05 15:51:13 -0800111 mFreeBlocks = nullptr;
Romain Guye3b0a012013-06-26 15:45:41 -0700112}
113
114void PatchCache::remove(Vector<patch_pair_t>& patchesToRemove, Res_png_9patch* patch) {
115 LruCache<PatchDescription, Patch*>::Iterator i(mCache);
116 while (i.next()) {
117 const PatchDescription& key = i.key();
118 if (key.getPatch() == patch) {
119 patchesToRemove.push(patch_pair_t(&key, i.value()));
120 }
121 }
122}
123
124void PatchCache::removeDeferred(Res_png_9patch* patch) {
125 Mutex::Autolock _l(mLock);
Jens Gulin6056e102014-02-04 17:38:02 +0100126
127 // Assert that patch is not already garbage
128 size_t count = mGarbage.size();
129 for (size_t i = 0; i < count; i++) {
130 if (patch == mGarbage[i]) {
Chris Craikd41c4d82015-01-05 15:51:13 -0800131 patch = nullptr;
Jens Gulin6056e102014-02-04 17:38:02 +0100132 break;
133 }
134 }
Chris Craikd41c4d82015-01-05 15:51:13 -0800135 LOG_ALWAYS_FATAL_IF(patch == nullptr);
Jens Gulin6056e102014-02-04 17:38:02 +0100136
Romain Guye3b0a012013-06-26 15:45:41 -0700137 mGarbage.push(patch);
138}
139
140void PatchCache::clearGarbage() {
141 Vector<patch_pair_t> patchesToRemove;
142
143 { // scope for the mutex
144 Mutex::Autolock _l(mLock);
145 size_t count = mGarbage.size();
146 for (size_t i = 0; i < count; i++) {
Sangkyu Lee36fad8f2014-01-09 14:11:57 +0900147 Res_png_9patch* patch = mGarbage[i];
148 remove(patchesToRemove, patch);
149 // A Res_png_9patch is actually an array of byte that's larger
150 // than sizeof(Res_png_9patch). It must be freed as an array.
151 delete[] (int8_t*) patch;
Romain Guye3b0a012013-06-26 15:45:41 -0700152 }
153 mGarbage.clear();
154 }
155
156 // TODO: We could sort patchesToRemove by offset to merge
157 // adjacent free blocks
158 for (size_t i = 0; i < patchesToRemove.size(); i++) {
159 const patch_pair_t& pair = patchesToRemove[i];
160
Jens Gulin6056e102014-02-04 17:38:02 +0100161 // Release the patch and mark the space in the free list
162 Patch* patch = pair.getSecond();
Chris Craik8820fd12015-03-03 14:20:47 -0800163 BufferBlock* block = new BufferBlock(patch->positionOffset, patch->getSize());
Romain Guye3b0a012013-06-26 15:45:41 -0700164 block->next = mFreeBlocks;
165 mFreeBlocks = block;
166
167 mSize -= patch->getSize();
168
169 mCache.remove(*pair.getFirst());
Jens Gulin6056e102014-02-04 17:38:02 +0100170 delete patch;
Romain Guye3b0a012013-06-26 15:45:41 -0700171 }
172
173#if DEBUG_PATCHES
174 if (patchesToRemove.size() > 0) {
175 dumpFreeBlocks("Removed garbage");
176 }
177#endif
Romain Guyf7f93552010-07-08 19:17:03 -0700178}
179
Romain Guy4c2547f2013-06-11 16:19:24 -0700180void PatchCache::createVertexBuffer() {
Chris Craikd41c4d82015-01-05 15:51:13 -0800181 glBufferData(GL_ARRAY_BUFFER, mMaxSize, nullptr, GL_DYNAMIC_DRAW);
Romain Guy4c2547f2013-06-11 16:19:24 -0700182 mSize = 0;
Romain Guye3b0a012013-06-26 15:45:41 -0700183 mFreeBlocks = new BufferBlock(0, mMaxSize);
Romain Guy4c2547f2013-06-11 16:19:24 -0700184 mGenerationId++;
185}
186
Romain Guye3b0a012013-06-26 15:45:41 -0700187/**
188 * Sets the mesh's offsets and copies its associated vertices into
189 * the mesh buffer (VBO).
190 */
Chris Craik8820fd12015-03-03 14:20:47 -0800191void PatchCache::setupMesh(Patch* newMesh) {
Romain Guye3b0a012013-06-26 15:45:41 -0700192 // This call ensures the VBO exists and that it is bound
Chris Craik96a5c4c2015-01-27 15:46:35 -0800193 init();
Romain Guye3b0a012013-06-26 15:45:41 -0700194
195 // If we're running out of space, let's clear the entire cache
196 uint32_t size = newMesh->getSize();
197 if (mSize + size > mMaxSize) {
198 clearCache();
199 createVertexBuffer();
200 }
201
202 // Find a block where we can fit the mesh
Chris Craikd41c4d82015-01-05 15:51:13 -0800203 BufferBlock* previous = nullptr;
Romain Guye3b0a012013-06-26 15:45:41 -0700204 BufferBlock* block = mFreeBlocks;
205 while (block) {
206 // The mesh fits
207 if (block->size >= size) {
208 break;
209 }
210 previous = block;
211 block = block->next;
212 }
213
214 // We have enough space left in the buffer, but it's
215 // too fragmented, let's clear the cache
216 if (!block) {
217 clearCache();
218 createVertexBuffer();
Chris Craikd41c4d82015-01-05 15:51:13 -0800219 previous = nullptr;
Romain Guye3b0a012013-06-26 15:45:41 -0700220 block = mFreeBlocks;
221 }
222
223 // Copy the 9patch mesh in the VBO
Chris Craik8820fd12015-03-03 14:20:47 -0800224 newMesh->positionOffset = (GLintptr) (block->offset);
225 newMesh->textureOffset = newMesh->positionOffset + kMeshTextureOffset;
226 glBufferSubData(GL_ARRAY_BUFFER, newMesh->positionOffset, size, newMesh->vertices.get());
Romain Guye3b0a012013-06-26 15:45:41 -0700227
228 // Remove the block since we've used it entirely
229 if (block->size == size) {
230 if (previous) {
231 previous->next = block->next;
232 } else {
233 mFreeBlocks = block->next;
234 }
Jens Gulin6056e102014-02-04 17:38:02 +0100235 delete block;
Romain Guye3b0a012013-06-26 15:45:41 -0700236 } else {
237 // Resize the block now that it's occupied
238 block->offset += size;
239 block->size -= size;
240 }
241
242 mSize += size;
243}
244
Chris Craik8820fd12015-03-03 14:20:47 -0800245static const UvMapper sIdentity;
246
Romain Guy3b748a42013-04-17 18:54:38 -0700247const Patch* PatchCache::get(const AssetAtlas::Entry* entry,
248 const uint32_t bitmapWidth, const uint32_t bitmapHeight,
249 const float pixelWidth, const float pixelHeight, const Res_png_9patch* patch) {
Romain Guy4bb94202010-10-12 15:59:26 -0700250
Romain Guy3b748a42013-04-17 18:54:38 -0700251 const PatchDescription description(bitmapWidth, bitmapHeight, pixelWidth, pixelHeight, patch);
252 const Patch* mesh = mCache.get(description);
Romain Guy2728f962010-10-08 18:36:15 -0700253
Romain Guyf7f93552010-07-08 19:17:03 -0700254 if (!mesh) {
Chris Craik8820fd12015-03-03 14:20:47 -0800255 const UvMapper& mapper = entry ? entry->uvMapper : sIdentity;
256 Patch* newMesh = new Patch(bitmapWidth, bitmapHeight,
257 pixelWidth, pixelHeight, mapper, patch);
Romain Guy2728f962010-10-08 18:36:15 -0700258
Chris Craik8820fd12015-03-03 14:20:47 -0800259 if (newMesh->vertices) {
260 setupMesh(newMesh);
Romain Guy3b748a42013-04-17 18:54:38 -0700261 }
262
Romain Guye3b0a012013-06-26 15:45:41 -0700263#if DEBUG_PATCHES
264 dumpFreeBlocks("Adding patch");
265#endif
266
Romain Guy3b748a42013-04-17 18:54:38 -0700267 mCache.put(description, newMesh);
268 return newMesh;
Romain Guyf7f93552010-07-08 19:17:03 -0700269 }
270
271 return mesh;
272}
273
Romain Guye3b0a012013-06-26 15:45:41 -0700274#if DEBUG_PATCHES
275void PatchCache::dumpFreeBlocks(const char* prefix) {
276 String8 dump;
277 BufferBlock* block = mFreeBlocks;
278 while (block) {
Chris Craik8820fd12015-03-03 14:20:47 -0800279 dump.appendFormat("->(%d, %d)", block->positionOffset, block->size);
Romain Guye3b0a012013-06-26 15:45:41 -0700280 block = block->next;
281 }
282 ALOGD("%s: Free blocks%s", prefix, dump.string());
283}
284#endif
285
Romain Guyf7f93552010-07-08 19:17:03 -0700286}; // namespace uirenderer
287}; // namespace android