blob: 27652624b498163fd06921d46f3b32d4c20eded7 [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"
Chris Craik96a5c4c2015-01-27 15:46:35 -080026#include "renderstate/RenderState.h"
Romain Guyf7f93552010-07-08 19:17:03 -070027
28namespace android {
29namespace uirenderer {
30
31///////////////////////////////////////////////////////////////////////////////
32// Constructors/destructor
33///////////////////////////////////////////////////////////////////////////////
34
Chris Craik96a5c4c2015-01-27 15:46:35 -080035PatchCache::PatchCache(RenderState& renderState)
36 : mRenderState(renderState)
37 , mSize(0)
38 , mCache(LruCache<PatchDescription, Patch*>::kUnlimitedCapacity)
39 , mMeshBuffer(0)
40 , mFreeBlocks(nullptr)
41 , mGenerationId(0) {
Romain Guy3b748a42013-04-17 18:54:38 -070042 char property[PROPERTY_VALUE_MAX];
Chris Craikd41c4d82015-01-05 15:51:13 -080043 if (property_get(PROPERTY_PATCH_CACHE_SIZE, property, nullptr) > 0) {
Romain Guy3b748a42013-04-17 18:54:38 -070044 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 }
Romain Guyf7f93552010-07-08 19:17:03 -070050}
51
52PatchCache::~PatchCache() {
53 clear();
54}
55
Chris Craik96a5c4c2015-01-27 15:46:35 -080056void PatchCache::init() {
Romain Guy7d9b1b32013-05-28 14:25:09 -070057 bool created = false;
58 if (!mMeshBuffer) {
59 glGenBuffers(1, &mMeshBuffer);
60 created = true;
61 }
62
Chris Craik96a5c4c2015-01-27 15:46:35 -080063 mRenderState.meshState().bindMeshBuffer(mMeshBuffer);
64 mRenderState.meshState().resetVertexPointers();
Romain Guy3b748a42013-04-17 18:54:38 -070065
Romain Guy7d9b1b32013-05-28 14:25:09 -070066 if (created) {
Romain Guy4c2547f2013-06-11 16:19:24 -070067 createVertexBuffer();
Romain Guy7d9b1b32013-05-28 14:25:09 -070068 }
Romain Guy3b748a42013-04-17 18:54:38 -070069}
70
Romain Guyf7f93552010-07-08 19:17:03 -070071///////////////////////////////////////////////////////////////////////////////
Romain Guyf7f93552010-07-08 19:17:03 -070072// Caching
73///////////////////////////////////////////////////////////////////////////////
74
Romain Guy3b748a42013-04-17 18:54:38 -070075hash_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}
Romain Guy13ba0052013-02-15 12:47:26 -080083
Romain Guy3b748a42013-04-17 18:54:38 -070084int PatchCache::PatchDescription::compare(const PatchCache::PatchDescription& lhs,
85 const PatchCache::PatchDescription& rhs) {
86 return memcmp(&lhs, &rhs, sizeof(PatchDescription));
Romain Guy13ba0052013-02-15 12:47:26 -080087}
88
Romain Guyf7f93552010-07-08 19:17:03 -070089void PatchCache::clear() {
Romain Guy3b748a42013-04-17 18:54:38 -070090 clearCache();
Romain Guy7d9b1b32013-05-28 14:25:09 -070091
92 if (mMeshBuffer) {
Chris Craik96a5c4c2015-01-27 15:46:35 -080093 mRenderState.meshState().unbindMeshBuffer();
Romain Guy7d9b1b32013-05-28 14:25:09 -070094 glDeleteBuffers(1, &mMeshBuffer);
95 mMeshBuffer = 0;
96 mSize = 0;
97 }
Romain Guy3b748a42013-04-17 18:54:38 -070098}
99
100void PatchCache::clearCache() {
101 LruCache<PatchDescription, Patch*>::Iterator i(mCache);
102 while (i.next()) {
Romain Guy3b748a42013-04-17 18:54:38 -0700103 delete i.value();
Romain Guy2728f962010-10-08 18:36:15 -0700104 }
Romain Guyf7f93552010-07-08 19:17:03 -0700105 mCache.clear();
Romain Guye3b0a012013-06-26 15:45:41 -0700106
107 BufferBlock* block = mFreeBlocks;
108 while (block) {
109 BufferBlock* next = block->next;
110 delete block;
111 block = next;
112 }
Chris Craikd41c4d82015-01-05 15:51:13 -0800113 mFreeBlocks = nullptr;
Romain Guye3b0a012013-06-26 15:45:41 -0700114}
115
116void 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
126void PatchCache::removeDeferred(Res_png_9patch* patch) {
127 Mutex::Autolock _l(mLock);
Jens Gulin6056e102014-02-04 17:38:02 +0100128
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]) {
Chris Craikd41c4d82015-01-05 15:51:13 -0800133 patch = nullptr;
Jens Gulin6056e102014-02-04 17:38:02 +0100134 break;
135 }
136 }
Chris Craikd41c4d82015-01-05 15:51:13 -0800137 LOG_ALWAYS_FATAL_IF(patch == nullptr);
Jens Gulin6056e102014-02-04 17:38:02 +0100138
Romain Guye3b0a012013-06-26 15:45:41 -0700139 mGarbage.push(patch);
140}
141
142void 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++) {
Sangkyu Lee36fad8f2014-01-09 14:11:57 +0900149 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;
Romain Guye3b0a012013-06-26 15:45:41 -0700154 }
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
Jens Gulin6056e102014-02-04 17:38:02 +0100163 // Release the patch and mark the space in the free list
164 Patch* patch = pair.getSecond();
Chris Craik8820fd12015-03-03 14:20:47 -0800165 BufferBlock* block = new BufferBlock(patch->positionOffset, patch->getSize());
Romain Guye3b0a012013-06-26 15:45:41 -0700166 block->next = mFreeBlocks;
167 mFreeBlocks = block;
168
169 mSize -= patch->getSize();
170
171 mCache.remove(*pair.getFirst());
Jens Gulin6056e102014-02-04 17:38:02 +0100172 delete patch;
Romain Guye3b0a012013-06-26 15:45:41 -0700173 }
174
175#if DEBUG_PATCHES
176 if (patchesToRemove.size() > 0) {
177 dumpFreeBlocks("Removed garbage");
178 }
179#endif
Romain Guyf7f93552010-07-08 19:17:03 -0700180}
181
Romain Guy4c2547f2013-06-11 16:19:24 -0700182void PatchCache::createVertexBuffer() {
Chris Craikd41c4d82015-01-05 15:51:13 -0800183 glBufferData(GL_ARRAY_BUFFER, mMaxSize, nullptr, GL_DYNAMIC_DRAW);
Romain Guy4c2547f2013-06-11 16:19:24 -0700184 mSize = 0;
Romain Guye3b0a012013-06-26 15:45:41 -0700185 mFreeBlocks = new BufferBlock(0, mMaxSize);
Romain Guy4c2547f2013-06-11 16:19:24 -0700186 mGenerationId++;
187}
188
Romain Guye3b0a012013-06-26 15:45:41 -0700189/**
190 * Sets the mesh's offsets and copies its associated vertices into
191 * the mesh buffer (VBO).
192 */
Chris Craik8820fd12015-03-03 14:20:47 -0800193void PatchCache::setupMesh(Patch* newMesh) {
Romain Guye3b0a012013-06-26 15:45:41 -0700194 // This call ensures the VBO exists and that it is bound
Chris Craik96a5c4c2015-01-27 15:46:35 -0800195 init();
Romain Guye3b0a012013-06-26 15:45:41 -0700196
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
Chris Craikd41c4d82015-01-05 15:51:13 -0800205 BufferBlock* previous = nullptr;
Romain Guye3b0a012013-06-26 15:45:41 -0700206 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();
Chris Craikd41c4d82015-01-05 15:51:13 -0800221 previous = nullptr;
Romain Guye3b0a012013-06-26 15:45:41 -0700222 block = mFreeBlocks;
223 }
224
225 // Copy the 9patch mesh in the VBO
Chris Craik8820fd12015-03-03 14:20:47 -0800226 newMesh->positionOffset = (GLintptr) (block->offset);
227 newMesh->textureOffset = newMesh->positionOffset + kMeshTextureOffset;
228 glBufferSubData(GL_ARRAY_BUFFER, newMesh->positionOffset, size, newMesh->vertices.get());
Romain Guye3b0a012013-06-26 15:45:41 -0700229
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 }
Jens Gulin6056e102014-02-04 17:38:02 +0100237 delete block;
Romain Guye3b0a012013-06-26 15:45:41 -0700238 } 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
Chris Craik8820fd12015-03-03 14:20:47 -0800247static const UvMapper sIdentity;
248
Romain Guy3b748a42013-04-17 18:54:38 -0700249const 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) {
Romain Guy4bb94202010-10-12 15:59:26 -0700252
Romain Guy3b748a42013-04-17 18:54:38 -0700253 const PatchDescription description(bitmapWidth, bitmapHeight, pixelWidth, pixelHeight, patch);
254 const Patch* mesh = mCache.get(description);
Romain Guy2728f962010-10-08 18:36:15 -0700255
Romain Guyf7f93552010-07-08 19:17:03 -0700256 if (!mesh) {
Chris Craik8820fd12015-03-03 14:20:47 -0800257 const UvMapper& mapper = entry ? entry->uvMapper : sIdentity;
258 Patch* newMesh = new Patch(bitmapWidth, bitmapHeight,
259 pixelWidth, pixelHeight, mapper, patch);
Romain Guy2728f962010-10-08 18:36:15 -0700260
Chris Craik8820fd12015-03-03 14:20:47 -0800261 if (newMesh->vertices) {
262 setupMesh(newMesh);
Romain Guy3b748a42013-04-17 18:54:38 -0700263 }
264
Romain Guye3b0a012013-06-26 15:45:41 -0700265#if DEBUG_PATCHES
266 dumpFreeBlocks("Adding patch");
267#endif
268
Romain Guy3b748a42013-04-17 18:54:38 -0700269 mCache.put(description, newMesh);
270 return newMesh;
Romain Guyf7f93552010-07-08 19:17:03 -0700271 }
272
273 return mesh;
274}
275
Romain Guye3b0a012013-06-26 15:45:41 -0700276#if DEBUG_PATCHES
277void PatchCache::dumpFreeBlocks(const char* prefix) {
278 String8 dump;
279 BufferBlock* block = mFreeBlocks;
280 while (block) {
Chris Craik8820fd12015-03-03 14:20:47 -0800281 dump.appendFormat("->(%d, %d)", block->positionOffset, block->size);
Romain Guye3b0a012013-06-26 15:45:41 -0700282 block = block->next;
283 }
284 ALOGD("%s: Free blocks%s", prefix, dump.string());
285}
286#endif
287
Romain Guyf7f93552010-07-08 19:17:03 -0700288}; // namespace uirenderer
289}; // namespace android