blob: 673d73c5475dd1694191eb0e36aaf45c45314211 [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)
John Reck8dc02f92017-07-17 09:55:02 -070035 , mMaxSize(KB(128))
Chris Craik96a5c4c2015-01-27 15:46:35 -080036 , mSize(0)
37 , mCache(LruCache<PatchDescription, Patch*>::kUnlimitedCapacity)
38 , mMeshBuffer(0)
John Reck03240102016-12-02 10:22:57 -080039 , mFreeBlocks(nullptr) {}
Romain Guyf7f93552010-07-08 19:17:03 -070040
41PatchCache::~PatchCache() {
42 clear();
43}
44
45///////////////////////////////////////////////////////////////////////////////
Romain Guyf7f93552010-07-08 19:17:03 -070046// Caching
47///////////////////////////////////////////////////////////////////////////////
48
Romain Guy3b748a42013-04-17 18:54:38 -070049hash_t PatchCache::PatchDescription::hash() const {
50 uint32_t hash = JenkinsHashMix(0, android::hash_type(mPatch));
51 hash = JenkinsHashMix(hash, mBitmapWidth);
52 hash = JenkinsHashMix(hash, mBitmapHeight);
53 hash = JenkinsHashMix(hash, mPixelWidth);
54 hash = JenkinsHashMix(hash, mPixelHeight);
55 return JenkinsHashWhiten(hash);
56}
Romain Guy13ba0052013-02-15 12:47:26 -080057
Romain Guy3b748a42013-04-17 18:54:38 -070058int PatchCache::PatchDescription::compare(const PatchCache::PatchDescription& lhs,
John Reck1bcacfd2017-11-03 10:12:19 -070059 const PatchCache::PatchDescription& rhs) {
Romain Guy3b748a42013-04-17 18:54:38 -070060 return memcmp(&lhs, &rhs, sizeof(PatchDescription));
Romain Guy13ba0052013-02-15 12:47:26 -080061}
62
Romain Guyf7f93552010-07-08 19:17:03 -070063void PatchCache::clear() {
Romain Guy3b748a42013-04-17 18:54:38 -070064 clearCache();
Romain Guy7d9b1b32013-05-28 14:25:09 -070065
66 if (mMeshBuffer) {
sergeyvfd3744b2016-05-11 16:52:33 -070067 mRenderState.meshState().deleteMeshBuffer(mMeshBuffer);
Romain Guy7d9b1b32013-05-28 14:25:09 -070068 mMeshBuffer = 0;
69 mSize = 0;
70 }
Romain Guy3b748a42013-04-17 18:54:38 -070071}
72
73void PatchCache::clearCache() {
74 LruCache<PatchDescription, Patch*>::Iterator i(mCache);
75 while (i.next()) {
Romain Guy3b748a42013-04-17 18:54:38 -070076 delete i.value();
Romain Guy2728f962010-10-08 18:36:15 -070077 }
Romain Guyf7f93552010-07-08 19:17:03 -070078 mCache.clear();
Romain Guye3b0a012013-06-26 15:45:41 -070079
80 BufferBlock* block = mFreeBlocks;
81 while (block) {
82 BufferBlock* next = block->next;
83 delete block;
84 block = next;
85 }
Chris Craikd41c4d82015-01-05 15:51:13 -080086 mFreeBlocks = nullptr;
Romain Guye3b0a012013-06-26 15:45:41 -070087}
88
89void PatchCache::remove(Vector<patch_pair_t>& patchesToRemove, Res_png_9patch* patch) {
90 LruCache<PatchDescription, Patch*>::Iterator i(mCache);
91 while (i.next()) {
92 const PatchDescription& key = i.key();
93 if (key.getPatch() == patch) {
94 patchesToRemove.push(patch_pair_t(&key, i.value()));
95 }
96 }
97}
98
99void PatchCache::removeDeferred(Res_png_9patch* patch) {
100 Mutex::Autolock _l(mLock);
Jens Gulin6056e102014-02-04 17:38:02 +0100101
102 // Assert that patch is not already garbage
103 size_t count = mGarbage.size();
104 for (size_t i = 0; i < count; i++) {
105 if (patch == mGarbage[i]) {
Chris Craikd41c4d82015-01-05 15:51:13 -0800106 patch = nullptr;
Jens Gulin6056e102014-02-04 17:38:02 +0100107 break;
108 }
109 }
Chris Craikd41c4d82015-01-05 15:51:13 -0800110 LOG_ALWAYS_FATAL_IF(patch == nullptr);
Jens Gulin6056e102014-02-04 17:38:02 +0100111
Romain Guye3b0a012013-06-26 15:45:41 -0700112 mGarbage.push(patch);
113}
114
115void PatchCache::clearGarbage() {
116 Vector<patch_pair_t> patchesToRemove;
117
John Reck1bcacfd2017-11-03 10:12:19 -0700118 { // scope for the mutex
Romain Guye3b0a012013-06-26 15:45:41 -0700119 Mutex::Autolock _l(mLock);
120 size_t count = mGarbage.size();
121 for (size_t i = 0; i < count; i++) {
Sangkyu Lee36fad8f2014-01-09 14:11:57 +0900122 Res_png_9patch* patch = mGarbage[i];
123 remove(patchesToRemove, patch);
124 // A Res_png_9patch is actually an array of byte that's larger
125 // than sizeof(Res_png_9patch). It must be freed as an array.
John Reck1bcacfd2017-11-03 10:12:19 -0700126 delete[](int8_t*) patch;
Romain Guye3b0a012013-06-26 15:45:41 -0700127 }
128 mGarbage.clear();
129 }
130
131 // TODO: We could sort patchesToRemove by offset to merge
132 // adjacent free blocks
133 for (size_t i = 0; i < patchesToRemove.size(); i++) {
134 const patch_pair_t& pair = patchesToRemove[i];
135
Jens Gulin6056e102014-02-04 17:38:02 +0100136 // Release the patch and mark the space in the free list
137 Patch* patch = pair.getSecond();
Chris Craik8820fd12015-03-03 14:20:47 -0800138 BufferBlock* block = new BufferBlock(patch->positionOffset, patch->getSize());
Romain Guye3b0a012013-06-26 15:45:41 -0700139 block->next = mFreeBlocks;
140 mFreeBlocks = block;
141
142 mSize -= patch->getSize();
143
144 mCache.remove(*pair.getFirst());
Jens Gulin6056e102014-02-04 17:38:02 +0100145 delete patch;
Romain Guye3b0a012013-06-26 15:45:41 -0700146 }
147
148#if DEBUG_PATCHES
149 if (patchesToRemove.size() > 0) {
150 dumpFreeBlocks("Removed garbage");
151 }
152#endif
Romain Guyf7f93552010-07-08 19:17:03 -0700153}
154
Romain Guy4c2547f2013-06-11 16:19:24 -0700155void PatchCache::createVertexBuffer() {
John Reck1bcacfd2017-11-03 10:12:19 -0700156 mRenderState.meshState().genOrUpdateMeshBuffer(&mMeshBuffer, mMaxSize, nullptr,
157 GL_DYNAMIC_DRAW);
Romain Guy4c2547f2013-06-11 16:19:24 -0700158 mSize = 0;
Romain Guye3b0a012013-06-26 15:45:41 -0700159 mFreeBlocks = new BufferBlock(0, mMaxSize);
Romain Guy4c2547f2013-06-11 16:19:24 -0700160}
161
Romain Guye3b0a012013-06-26 15:45:41 -0700162/**
163 * Sets the mesh's offsets and copies its associated vertices into
164 * the mesh buffer (VBO).
165 */
Chris Craik8820fd12015-03-03 14:20:47 -0800166void PatchCache::setupMesh(Patch* newMesh) {
Romain Guye3b0a012013-06-26 15:45:41 -0700167 // This call ensures the VBO exists and that it is bound
sergeyvfd3744b2016-05-11 16:52:33 -0700168 if (!mMeshBuffer) {
169 createVertexBuffer();
170 }
Romain Guye3b0a012013-06-26 15:45:41 -0700171
172 // If we're running out of space, let's clear the entire cache
173 uint32_t size = newMesh->getSize();
174 if (mSize + size > mMaxSize) {
175 clearCache();
176 createVertexBuffer();
177 }
178
179 // Find a block where we can fit the mesh
Chris Craikd41c4d82015-01-05 15:51:13 -0800180 BufferBlock* previous = nullptr;
Romain Guye3b0a012013-06-26 15:45:41 -0700181 BufferBlock* block = mFreeBlocks;
182 while (block) {
183 // The mesh fits
184 if (block->size >= size) {
185 break;
186 }
187 previous = block;
188 block = block->next;
189 }
190
191 // We have enough space left in the buffer, but it's
192 // too fragmented, let's clear the cache
193 if (!block) {
194 clearCache();
195 createVertexBuffer();
Chris Craikd41c4d82015-01-05 15:51:13 -0800196 previous = nullptr;
Romain Guye3b0a012013-06-26 15:45:41 -0700197 block = mFreeBlocks;
198 }
199
200 // Copy the 9patch mesh in the VBO
John Reck1bcacfd2017-11-03 10:12:19 -0700201 newMesh->positionOffset = (GLintptr)(block->offset);
Chris Craik8820fd12015-03-03 14:20:47 -0800202 newMesh->textureOffset = newMesh->positionOffset + kMeshTextureOffset;
sergeyvfd3744b2016-05-11 16:52:33 -0700203
204 mRenderState.meshState().updateMeshBufferSubData(mMeshBuffer, newMesh->positionOffset, size,
John Reck1bcacfd2017-11-03 10:12:19 -0700205 newMesh->vertices.get());
Romain Guye3b0a012013-06-26 15:45:41 -0700206
207 // Remove the block since we've used it entirely
208 if (block->size == size) {
209 if (previous) {
210 previous->next = block->next;
211 } else {
212 mFreeBlocks = block->next;
213 }
Jens Gulin6056e102014-02-04 17:38:02 +0100214 delete block;
Romain Guye3b0a012013-06-26 15:45:41 -0700215 } else {
216 // Resize the block now that it's occupied
217 block->offset += size;
218 block->size -= size;
219 }
220
221 mSize += size;
222}
223
Chris Craik8820fd12015-03-03 14:20:47 -0800224static const UvMapper sIdentity;
225
John Reck1bcacfd2017-11-03 10:12:19 -0700226const Patch* PatchCache::get(const uint32_t bitmapWidth, const uint32_t bitmapHeight,
227 const float pixelWidth, const float pixelHeight,
228 const Res_png_9patch* patch) {
Romain Guy3b748a42013-04-17 18:54:38 -0700229 const PatchDescription description(bitmapWidth, bitmapHeight, pixelWidth, pixelHeight, patch);
230 const Patch* mesh = mCache.get(description);
Romain Guy2728f962010-10-08 18:36:15 -0700231
Romain Guyf7f93552010-07-08 19:17:03 -0700232 if (!mesh) {
John Reck1bcacfd2017-11-03 10:12:19 -0700233 Patch* newMesh =
234 new Patch(bitmapWidth, bitmapHeight, pixelWidth, pixelHeight, sIdentity, patch);
Romain Guy2728f962010-10-08 18:36:15 -0700235
Chris Craik8820fd12015-03-03 14:20:47 -0800236 if (newMesh->vertices) {
237 setupMesh(newMesh);
Romain Guy3b748a42013-04-17 18:54:38 -0700238 }
239
Romain Guye3b0a012013-06-26 15:45:41 -0700240#if DEBUG_PATCHES
241 dumpFreeBlocks("Adding patch");
242#endif
243
Romain Guy3b748a42013-04-17 18:54:38 -0700244 mCache.put(description, newMesh);
245 return newMesh;
Romain Guyf7f93552010-07-08 19:17:03 -0700246 }
247
248 return mesh;
249}
250
Romain Guye3b0a012013-06-26 15:45:41 -0700251#if DEBUG_PATCHES
252void PatchCache::dumpFreeBlocks(const char* prefix) {
253 String8 dump;
254 BufferBlock* block = mFreeBlocks;
255 while (block) {
Chris Craik8820fd12015-03-03 14:20:47 -0800256 dump.appendFormat("->(%d, %d)", block->positionOffset, block->size);
Romain Guye3b0a012013-06-26 15:45:41 -0700257 block = block->next;
258 }
259 ALOGD("%s: Free blocks%s", prefix, dump.string());
260}
261#endif
262
John Reck1bcacfd2017-11-03 10:12:19 -0700263}; // namespace uirenderer
264}; // namespace android