blob: 52c62cc34670b948f301dc3fd8532f753d036392 [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)
Chris Craik48a8f432016-02-05 15:59:29 -080035 , mMaxSize(Properties::patchCacheSize)
Chris Craik96a5c4c2015-01-27 15:46:35 -080036 , mSize(0)
37 , mCache(LruCache<PatchDescription, Patch*>::kUnlimitedCapacity)
38 , mMeshBuffer(0)
39 , mFreeBlocks(nullptr)
Chris Craik48a8f432016-02-05 15:59:29 -080040 , mGenerationId(0) {}
Romain Guyf7f93552010-07-08 19:17:03 -070041
42PatchCache::~PatchCache() {
43 clear();
44}
45
46///////////////////////////////////////////////////////////////////////////////
Romain Guyf7f93552010-07-08 19:17:03 -070047// Caching
48///////////////////////////////////////////////////////////////////////////////
49
Romain Guy3b748a42013-04-17 18:54:38 -070050hash_t PatchCache::PatchDescription::hash() const {
51 uint32_t hash = JenkinsHashMix(0, android::hash_type(mPatch));
52 hash = JenkinsHashMix(hash, mBitmapWidth);
53 hash = JenkinsHashMix(hash, mBitmapHeight);
54 hash = JenkinsHashMix(hash, mPixelWidth);
55 hash = JenkinsHashMix(hash, mPixelHeight);
56 return JenkinsHashWhiten(hash);
57}
Romain Guy13ba0052013-02-15 12:47:26 -080058
Romain Guy3b748a42013-04-17 18:54:38 -070059int PatchCache::PatchDescription::compare(const PatchCache::PatchDescription& lhs,
60 const PatchCache::PatchDescription& rhs) {
61 return memcmp(&lhs, &rhs, sizeof(PatchDescription));
Romain Guy13ba0052013-02-15 12:47:26 -080062}
63
Romain Guyf7f93552010-07-08 19:17:03 -070064void PatchCache::clear() {
Romain Guy3b748a42013-04-17 18:54:38 -070065 clearCache();
Romain Guy7d9b1b32013-05-28 14:25:09 -070066
67 if (mMeshBuffer) {
sergeyvfd3744b2016-05-11 16:52:33 -070068 mRenderState.meshState().deleteMeshBuffer(mMeshBuffer);
Romain Guy7d9b1b32013-05-28 14:25:09 -070069 mMeshBuffer = 0;
70 mSize = 0;
71 }
Romain Guy3b748a42013-04-17 18:54:38 -070072}
73
74void PatchCache::clearCache() {
75 LruCache<PatchDescription, Patch*>::Iterator i(mCache);
76 while (i.next()) {
Romain Guy3b748a42013-04-17 18:54:38 -070077 delete i.value();
Romain Guy2728f962010-10-08 18:36:15 -070078 }
Romain Guyf7f93552010-07-08 19:17:03 -070079 mCache.clear();
Romain Guye3b0a012013-06-26 15:45:41 -070080
81 BufferBlock* block = mFreeBlocks;
82 while (block) {
83 BufferBlock* next = block->next;
84 delete block;
85 block = next;
86 }
Chris Craikd41c4d82015-01-05 15:51:13 -080087 mFreeBlocks = nullptr;
Romain Guye3b0a012013-06-26 15:45:41 -070088}
89
90void PatchCache::remove(Vector<patch_pair_t>& patchesToRemove, Res_png_9patch* patch) {
91 LruCache<PatchDescription, Patch*>::Iterator i(mCache);
92 while (i.next()) {
93 const PatchDescription& key = i.key();
94 if (key.getPatch() == patch) {
95 patchesToRemove.push(patch_pair_t(&key, i.value()));
96 }
97 }
98}
99
100void PatchCache::removeDeferred(Res_png_9patch* patch) {
101 Mutex::Autolock _l(mLock);
Jens Gulin6056e102014-02-04 17:38:02 +0100102
103 // Assert that patch is not already garbage
104 size_t count = mGarbage.size();
105 for (size_t i = 0; i < count; i++) {
106 if (patch == mGarbage[i]) {
Chris Craikd41c4d82015-01-05 15:51:13 -0800107 patch = nullptr;
Jens Gulin6056e102014-02-04 17:38:02 +0100108 break;
109 }
110 }
Chris Craikd41c4d82015-01-05 15:51:13 -0800111 LOG_ALWAYS_FATAL_IF(patch == nullptr);
Jens Gulin6056e102014-02-04 17:38:02 +0100112
Romain Guye3b0a012013-06-26 15:45:41 -0700113 mGarbage.push(patch);
114}
115
116void PatchCache::clearGarbage() {
117 Vector<patch_pair_t> patchesToRemove;
118
119 { // scope for the mutex
120 Mutex::Autolock _l(mLock);
121 size_t count = mGarbage.size();
122 for (size_t i = 0; i < count; i++) {
Sangkyu Lee36fad8f2014-01-09 14:11:57 +0900123 Res_png_9patch* patch = mGarbage[i];
124 remove(patchesToRemove, patch);
125 // A Res_png_9patch is actually an array of byte that's larger
126 // than sizeof(Res_png_9patch). It must be freed as an array.
127 delete[] (int8_t*) patch;
Romain Guye3b0a012013-06-26 15:45:41 -0700128 }
129 mGarbage.clear();
130 }
131
132 // TODO: We could sort patchesToRemove by offset to merge
133 // adjacent free blocks
134 for (size_t i = 0; i < patchesToRemove.size(); i++) {
135 const patch_pair_t& pair = patchesToRemove[i];
136
Jens Gulin6056e102014-02-04 17:38:02 +0100137 // Release the patch and mark the space in the free list
138 Patch* patch = pair.getSecond();
Chris Craik8820fd12015-03-03 14:20:47 -0800139 BufferBlock* block = new BufferBlock(patch->positionOffset, patch->getSize());
Romain Guye3b0a012013-06-26 15:45:41 -0700140 block->next = mFreeBlocks;
141 mFreeBlocks = block;
142
143 mSize -= patch->getSize();
144
145 mCache.remove(*pair.getFirst());
Jens Gulin6056e102014-02-04 17:38:02 +0100146 delete patch;
Romain Guye3b0a012013-06-26 15:45:41 -0700147 }
148
149#if DEBUG_PATCHES
150 if (patchesToRemove.size() > 0) {
151 dumpFreeBlocks("Removed garbage");
152 }
153#endif
Romain Guyf7f93552010-07-08 19:17:03 -0700154}
155
Romain Guy4c2547f2013-06-11 16:19:24 -0700156void PatchCache::createVertexBuffer() {
sergeyvfd3744b2016-05-11 16:52:33 -0700157 mRenderState.meshState().genOrUpdateMeshBuffer(&mMeshBuffer,
158 mMaxSize, nullptr, GL_DYNAMIC_DRAW);
Romain Guy4c2547f2013-06-11 16:19:24 -0700159 mSize = 0;
Romain Guye3b0a012013-06-26 15:45:41 -0700160 mFreeBlocks = new BufferBlock(0, mMaxSize);
Romain Guy4c2547f2013-06-11 16:19:24 -0700161 mGenerationId++;
162}
163
Romain Guye3b0a012013-06-26 15:45:41 -0700164/**
165 * Sets the mesh's offsets and copies its associated vertices into
166 * the mesh buffer (VBO).
167 */
Chris Craik8820fd12015-03-03 14:20:47 -0800168void PatchCache::setupMesh(Patch* newMesh) {
Romain Guye3b0a012013-06-26 15:45:41 -0700169 // This call ensures the VBO exists and that it is bound
sergeyvfd3744b2016-05-11 16:52:33 -0700170 if (!mMeshBuffer) {
171 createVertexBuffer();
172 }
Romain Guye3b0a012013-06-26 15:45:41 -0700173
174 // If we're running out of space, let's clear the entire cache
175 uint32_t size = newMesh->getSize();
176 if (mSize + size > mMaxSize) {
177 clearCache();
178 createVertexBuffer();
179 }
180
181 // Find a block where we can fit the mesh
Chris Craikd41c4d82015-01-05 15:51:13 -0800182 BufferBlock* previous = nullptr;
Romain Guye3b0a012013-06-26 15:45:41 -0700183 BufferBlock* block = mFreeBlocks;
184 while (block) {
185 // The mesh fits
186 if (block->size >= size) {
187 break;
188 }
189 previous = block;
190 block = block->next;
191 }
192
193 // We have enough space left in the buffer, but it's
194 // too fragmented, let's clear the cache
195 if (!block) {
196 clearCache();
197 createVertexBuffer();
Chris Craikd41c4d82015-01-05 15:51:13 -0800198 previous = nullptr;
Romain Guye3b0a012013-06-26 15:45:41 -0700199 block = mFreeBlocks;
200 }
201
202 // Copy the 9patch mesh in the VBO
Chris Craik8820fd12015-03-03 14:20:47 -0800203 newMesh->positionOffset = (GLintptr) (block->offset);
204 newMesh->textureOffset = newMesh->positionOffset + kMeshTextureOffset;
sergeyvfd3744b2016-05-11 16:52:33 -0700205
206 mRenderState.meshState().updateMeshBufferSubData(mMeshBuffer, newMesh->positionOffset, size,
207 newMesh->vertices.get());
Romain Guye3b0a012013-06-26 15:45:41 -0700208
209 // Remove the block since we've used it entirely
210 if (block->size == size) {
211 if (previous) {
212 previous->next = block->next;
213 } else {
214 mFreeBlocks = block->next;
215 }
Jens Gulin6056e102014-02-04 17:38:02 +0100216 delete block;
Romain Guye3b0a012013-06-26 15:45:41 -0700217 } else {
218 // Resize the block now that it's occupied
219 block->offset += size;
220 block->size -= size;
221 }
222
223 mSize += size;
224}
225
Chris Craik8820fd12015-03-03 14:20:47 -0800226static const UvMapper sIdentity;
227
Romain Guy253f2c22016-09-28 17:34:42 -0700228const Patch* PatchCache::get( const uint32_t bitmapWidth, const uint32_t bitmapHeight,
Romain Guy3b748a42013-04-17 18:54:38 -0700229 const float pixelWidth, const float pixelHeight, const Res_png_9patch* patch) {
Romain Guy4bb94202010-10-12 15:59:26 -0700230
Romain Guy3b748a42013-04-17 18:54:38 -0700231 const PatchDescription description(bitmapWidth, bitmapHeight, pixelWidth, pixelHeight, patch);
232 const Patch* mesh = mCache.get(description);
Romain Guy2728f962010-10-08 18:36:15 -0700233
Romain Guyf7f93552010-07-08 19:17:03 -0700234 if (!mesh) {
Chris Craik8820fd12015-03-03 14:20:47 -0800235 Patch* newMesh = new Patch(bitmapWidth, bitmapHeight,
Romain Guy253f2c22016-09-28 17:34:42 -0700236 pixelWidth, pixelHeight, sIdentity, patch);
Romain Guy2728f962010-10-08 18:36:15 -0700237
Chris Craik8820fd12015-03-03 14:20:47 -0800238 if (newMesh->vertices) {
239 setupMesh(newMesh);
Romain Guy3b748a42013-04-17 18:54:38 -0700240 }
241
Romain Guye3b0a012013-06-26 15:45:41 -0700242#if DEBUG_PATCHES
243 dumpFreeBlocks("Adding patch");
244#endif
245
Romain Guy3b748a42013-04-17 18:54:38 -0700246 mCache.put(description, newMesh);
247 return newMesh;
Romain Guyf7f93552010-07-08 19:17:03 -0700248 }
249
250 return mesh;
251}
252
Romain Guye3b0a012013-06-26 15:45:41 -0700253#if DEBUG_PATCHES
254void PatchCache::dumpFreeBlocks(const char* prefix) {
255 String8 dump;
256 BufferBlock* block = mFreeBlocks;
257 while (block) {
Chris Craik8820fd12015-03-03 14:20:47 -0800258 dump.appendFormat("->(%d, %d)", block->positionOffset, block->size);
Romain Guye3b0a012013-06-26 15:45:41 -0700259 block = block->next;
260 }
261 ALOGD("%s: Free blocks%s", prefix, dump.string());
262}
263#endif
264
Romain Guyf7f93552010-07-08 19:17:03 -0700265}; // namespace uirenderer
266}; // namespace android