blob: 1920fcfeebc970567ddca332368ed39aedc0dc6a [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"
Tom Hudson2dc236b2014-10-15 15:46:42 -040023#include "Patch.h"
Romain Guyf7f93552010-07-08 19:17:03 -070024#include "PatchCache.h"
Romain Guyfb8b7632010-08-23 21:05:08 -070025#include "Properties.h"
Romain Guyf7f93552010-07-08 19:17:03 -070026
27namespace android {
28namespace uirenderer {
29
30///////////////////////////////////////////////////////////////////////////////
31// Constructors/destructor
32///////////////////////////////////////////////////////////////////////////////
33
Romain Guy4c2547f2013-06-11 16:19:24 -070034PatchCache::PatchCache():
35 mSize(0), mCache(LruCache<PatchDescription, Patch*>::kUnlimitedCapacity),
Romain Guye3b0a012013-06-26 15:45:41 -070036 mMeshBuffer(0), mFreeBlocks(NULL), mGenerationId(0) {
Romain Guy3b748a42013-04-17 18:54:38 -070037 char property[PROPERTY_VALUE_MAX];
38 if (property_get(PROPERTY_PATCH_CACHE_SIZE, property, NULL) > 0) {
39 INIT_LOGD(" Setting patch cache size to %skB", property);
40 mMaxSize = KB(atoi(property));
41 } else {
42 INIT_LOGD(" Using default patch cache size of %.2fkB", DEFAULT_PATCH_CACHE_SIZE);
43 mMaxSize = KB(DEFAULT_PATCH_CACHE_SIZE);
44 }
Romain Guyf7f93552010-07-08 19:17:03 -070045}
46
47PatchCache::~PatchCache() {
48 clear();
49}
50
Romain Guy3b748a42013-04-17 18:54:38 -070051void PatchCache::init(Caches& caches) {
Romain Guy7d9b1b32013-05-28 14:25:09 -070052 bool created = false;
53 if (!mMeshBuffer) {
54 glGenBuffers(1, &mMeshBuffer);
55 created = true;
56 }
57
Romain Guy3b748a42013-04-17 18:54:38 -070058 caches.bindMeshBuffer(mMeshBuffer);
59 caches.resetVertexPointers();
60
Romain Guy7d9b1b32013-05-28 14:25:09 -070061 if (created) {
Romain Guy4c2547f2013-06-11 16:19:24 -070062 createVertexBuffer();
Romain Guy7d9b1b32013-05-28 14:25:09 -070063 }
Romain Guy3b748a42013-04-17 18:54:38 -070064}
65
Romain Guyf7f93552010-07-08 19:17:03 -070066///////////////////////////////////////////////////////////////////////////////
Romain Guyf7f93552010-07-08 19:17:03 -070067// Caching
68///////////////////////////////////////////////////////////////////////////////
69
Romain Guy3b748a42013-04-17 18:54:38 -070070hash_t PatchCache::PatchDescription::hash() const {
71 uint32_t hash = JenkinsHashMix(0, android::hash_type(mPatch));
72 hash = JenkinsHashMix(hash, mBitmapWidth);
73 hash = JenkinsHashMix(hash, mBitmapHeight);
74 hash = JenkinsHashMix(hash, mPixelWidth);
75 hash = JenkinsHashMix(hash, mPixelHeight);
76 return JenkinsHashWhiten(hash);
77}
Romain Guy13ba0052013-02-15 12:47:26 -080078
Romain Guy3b748a42013-04-17 18:54:38 -070079int PatchCache::PatchDescription::compare(const PatchCache::PatchDescription& lhs,
80 const PatchCache::PatchDescription& rhs) {
81 return memcmp(&lhs, &rhs, sizeof(PatchDescription));
Romain Guy13ba0052013-02-15 12:47:26 -080082}
83
Romain Guyf7f93552010-07-08 19:17:03 -070084void PatchCache::clear() {
Romain Guy3b748a42013-04-17 18:54:38 -070085 clearCache();
Romain Guy7d9b1b32013-05-28 14:25:09 -070086
87 if (mMeshBuffer) {
88 Caches::getInstance().unbindMeshBuffer();
89 glDeleteBuffers(1, &mMeshBuffer);
90 mMeshBuffer = 0;
91 mSize = 0;
92 }
Romain Guy3b748a42013-04-17 18:54:38 -070093}
94
95void PatchCache::clearCache() {
96 LruCache<PatchDescription, Patch*>::Iterator i(mCache);
97 while (i.next()) {
Romain Guy3b748a42013-04-17 18:54:38 -070098 delete i.value();
Romain Guy2728f962010-10-08 18:36:15 -070099 }
Romain Guyf7f93552010-07-08 19:17:03 -0700100 mCache.clear();
Romain Guye3b0a012013-06-26 15:45:41 -0700101
102 BufferBlock* block = mFreeBlocks;
103 while (block) {
104 BufferBlock* next = block->next;
105 delete block;
106 block = next;
107 }
108 mFreeBlocks = NULL;
109}
110
111void PatchCache::remove(Vector<patch_pair_t>& patchesToRemove, Res_png_9patch* patch) {
112 LruCache<PatchDescription, Patch*>::Iterator i(mCache);
113 while (i.next()) {
114 const PatchDescription& key = i.key();
115 if (key.getPatch() == patch) {
116 patchesToRemove.push(patch_pair_t(&key, i.value()));
117 }
118 }
119}
120
121void PatchCache::removeDeferred(Res_png_9patch* patch) {
122 Mutex::Autolock _l(mLock);
Jens Gulin6056e102014-02-04 17:38:02 +0100123
124 // Assert that patch is not already garbage
125 size_t count = mGarbage.size();
126 for (size_t i = 0; i < count; i++) {
127 if (patch == mGarbage[i]) {
128 patch = NULL;
129 break;
130 }
131 }
132 LOG_ALWAYS_FATAL_IF(patch == NULL);
133
Romain Guye3b0a012013-06-26 15:45:41 -0700134 mGarbage.push(patch);
135}
136
137void PatchCache::clearGarbage() {
138 Vector<patch_pair_t> patchesToRemove;
139
140 { // scope for the mutex
141 Mutex::Autolock _l(mLock);
142 size_t count = mGarbage.size();
143 for (size_t i = 0; i < count; i++) {
Sangkyu Lee36fad8f2014-01-09 14:11:57 +0900144 Res_png_9patch* patch = mGarbage[i];
145 remove(patchesToRemove, patch);
146 // A Res_png_9patch is actually an array of byte that's larger
147 // than sizeof(Res_png_9patch). It must be freed as an array.
148 delete[] (int8_t*) patch;
Romain Guye3b0a012013-06-26 15:45:41 -0700149 }
150 mGarbage.clear();
151 }
152
153 // TODO: We could sort patchesToRemove by offset to merge
154 // adjacent free blocks
155 for (size_t i = 0; i < patchesToRemove.size(); i++) {
156 const patch_pair_t& pair = patchesToRemove[i];
157
Jens Gulin6056e102014-02-04 17:38:02 +0100158 // Release the patch and mark the space in the free list
159 Patch* patch = pair.getSecond();
Romain Guye3b0a012013-06-26 15:45:41 -0700160 BufferBlock* block = new BufferBlock(patch->offset, patch->getSize());
161 block->next = mFreeBlocks;
162 mFreeBlocks = block;
163
164 mSize -= patch->getSize();
165
166 mCache.remove(*pair.getFirst());
Jens Gulin6056e102014-02-04 17:38:02 +0100167 delete patch;
Romain Guye3b0a012013-06-26 15:45:41 -0700168 }
169
170#if DEBUG_PATCHES
171 if (patchesToRemove.size() > 0) {
172 dumpFreeBlocks("Removed garbage");
173 }
174#endif
Romain Guyf7f93552010-07-08 19:17:03 -0700175}
176
Romain Guy4c2547f2013-06-11 16:19:24 -0700177void PatchCache::createVertexBuffer() {
178 glBufferData(GL_ARRAY_BUFFER, mMaxSize, NULL, GL_DYNAMIC_DRAW);
179 mSize = 0;
Romain Guye3b0a012013-06-26 15:45:41 -0700180 mFreeBlocks = new BufferBlock(0, mMaxSize);
Romain Guy4c2547f2013-06-11 16:19:24 -0700181 mGenerationId++;
182}
183
Romain Guye3b0a012013-06-26 15:45:41 -0700184/**
185 * Sets the mesh's offsets and copies its associated vertices into
186 * the mesh buffer (VBO).
187 */
188void PatchCache::setupMesh(Patch* newMesh, TextureVertex* vertices) {
189 // This call ensures the VBO exists and that it is bound
190 init(Caches::getInstance());
191
192 // If we're running out of space, let's clear the entire cache
193 uint32_t size = newMesh->getSize();
194 if (mSize + size > mMaxSize) {
195 clearCache();
196 createVertexBuffer();
197 }
198
199 // Find a block where we can fit the mesh
200 BufferBlock* previous = NULL;
201 BufferBlock* block = mFreeBlocks;
202 while (block) {
203 // The mesh fits
204 if (block->size >= size) {
205 break;
206 }
207 previous = block;
208 block = block->next;
209 }
210
211 // We have enough space left in the buffer, but it's
212 // too fragmented, let's clear the cache
213 if (!block) {
214 clearCache();
215 createVertexBuffer();
216 previous = NULL;
217 block = mFreeBlocks;
218 }
219
220 // Copy the 9patch mesh in the VBO
221 newMesh->offset = (GLintptr) (block->offset);
222 newMesh->textureOffset = newMesh->offset + gMeshTextureOffset;
223 glBufferSubData(GL_ARRAY_BUFFER, newMesh->offset, size, vertices);
224
225 // Remove the block since we've used it entirely
226 if (block->size == size) {
227 if (previous) {
228 previous->next = block->next;
229 } else {
230 mFreeBlocks = block->next;
231 }
Jens Gulin6056e102014-02-04 17:38:02 +0100232 delete block;
Romain Guye3b0a012013-06-26 15:45:41 -0700233 } else {
234 // Resize the block now that it's occupied
235 block->offset += size;
236 block->size -= size;
237 }
238
239 mSize += size;
240}
241
Romain Guy3b748a42013-04-17 18:54:38 -0700242const Patch* PatchCache::get(const AssetAtlas::Entry* entry,
243 const uint32_t bitmapWidth, const uint32_t bitmapHeight,
244 const float pixelWidth, const float pixelHeight, const Res_png_9patch* patch) {
Romain Guy4bb94202010-10-12 15:59:26 -0700245
Romain Guy3b748a42013-04-17 18:54:38 -0700246 const PatchDescription description(bitmapWidth, bitmapHeight, pixelWidth, pixelHeight, patch);
247 const Patch* mesh = mCache.get(description);
Romain Guy2728f962010-10-08 18:36:15 -0700248
Romain Guyf7f93552010-07-08 19:17:03 -0700249 if (!mesh) {
Romain Guy3b748a42013-04-17 18:54:38 -0700250 Patch* newMesh = new Patch();
251 TextureVertex* vertices;
Romain Guy2728f962010-10-08 18:36:15 -0700252
Romain Guy3b748a42013-04-17 18:54:38 -0700253 if (entry) {
Romain Guye3b0a012013-06-26 15:45:41 -0700254 // An atlas entry has a UV mapper
Romain Guy3b748a42013-04-17 18:54:38 -0700255 vertices = newMesh->createMesh(bitmapWidth, bitmapHeight,
Romain Guy03c00b52013-06-20 18:30:28 -0700256 pixelWidth, pixelHeight, entry->uvMapper, patch);
Romain Guy3b748a42013-04-17 18:54:38 -0700257 } else {
258 vertices = newMesh->createMesh(bitmapWidth, bitmapHeight,
Romain Guy03c00b52013-06-20 18:30:28 -0700259 pixelWidth, pixelHeight, patch);
Romain Guy2728f962010-10-08 18:36:15 -0700260 }
261
Romain Guy3b748a42013-04-17 18:54:38 -0700262 if (vertices) {
Romain Guye3b0a012013-06-26 15:45:41 -0700263 setupMesh(newMesh, vertices);
Romain Guy3b748a42013-04-17 18:54:38 -0700264 }
265
Romain Guye3b0a012013-06-26 15:45:41 -0700266#if DEBUG_PATCHES
267 dumpFreeBlocks("Adding patch");
268#endif
269
Romain Guy3b748a42013-04-17 18:54:38 -0700270 mCache.put(description, newMesh);
271 return newMesh;
Romain Guyf7f93552010-07-08 19:17:03 -0700272 }
273
274 return mesh;
275}
276
Romain Guye3b0a012013-06-26 15:45:41 -0700277#if DEBUG_PATCHES
278void PatchCache::dumpFreeBlocks(const char* prefix) {
279 String8 dump;
280 BufferBlock* block = mFreeBlocks;
281 while (block) {
282 dump.appendFormat("->(%d, %d)", block->offset, block->size);
283 block = block->next;
284 }
285 ALOGD("%s: Free blocks%s", prefix, dump.string());
286}
287#endif
288
Romain Guyf7f93552010-07-08 19:17:03 -0700289}; // namespace uirenderer
290}; // namespace android