2 * Copyright (C) 2010 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 #include <utils/JenkinsHash.h>
18 #include <utils/Log.h>
22 #include "PatchCache.h"
23 #include "Properties.h"
24 #include "renderstate/RenderState.h"
27 namespace uirenderer {
29 ///////////////////////////////////////////////////////////////////////////////
30 // Constructors/destructor
31 ///////////////////////////////////////////////////////////////////////////////
33 PatchCache::PatchCache(RenderState& renderState)
34 : mRenderState(renderState)
37 , mCache(LruCache<PatchDescription, Patch*>::kUnlimitedCapacity)
39 , mFreeBlocks(nullptr) {}
41 PatchCache::~PatchCache() {
45 ///////////////////////////////////////////////////////////////////////////////
47 ///////////////////////////////////////////////////////////////////////////////
49 hash_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);
58 int PatchCache::PatchDescription::compare(const PatchCache::PatchDescription& lhs,
59 const PatchCache::PatchDescription& rhs) {
60 return memcmp(&lhs, &rhs, sizeof(PatchDescription));
63 void PatchCache::clear() {
67 mRenderState.meshState().deleteMeshBuffer(mMeshBuffer);
73 void PatchCache::clearCache() {
74 LruCache<PatchDescription, Patch*>::Iterator i(mCache);
80 BufferBlock* block = mFreeBlocks;
82 BufferBlock* next = block->next;
86 mFreeBlocks = nullptr;
89 void PatchCache::remove(Vector<patch_pair_t>& patchesToRemove, Res_png_9patch* patch) {
90 LruCache<PatchDescription, Patch*>::Iterator i(mCache);
92 const PatchDescription& key = i.key();
93 if (key.getPatch() == patch) {
94 patchesToRemove.push(patch_pair_t(&key, i.value()));
99 void PatchCache::removeDeferred(Res_png_9patch* patch) {
100 Mutex::Autolock _l(mLock);
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]) {
110 LOG_ALWAYS_FATAL_IF(patch == nullptr);
112 mGarbage.push(patch);
115 void PatchCache::clearGarbage() {
116 Vector<patch_pair_t> patchesToRemove;
118 { // scope for the mutex
119 Mutex::Autolock _l(mLock);
120 size_t count = mGarbage.size();
121 for (size_t i = 0; i < count; i++) {
122 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.
126 delete[] (int8_t*) patch;
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];
136 // Release the patch and mark the space in the free list
137 Patch* patch = pair.getSecond();
138 BufferBlock* block = new BufferBlock(patch->positionOffset, patch->getSize());
139 block->next = mFreeBlocks;
142 mSize -= patch->getSize();
144 mCache.remove(*pair.getFirst());
149 if (patchesToRemove.size() > 0) {
150 dumpFreeBlocks("Removed garbage");
155 void PatchCache::createVertexBuffer() {
156 mRenderState.meshState().genOrUpdateMeshBuffer(&mMeshBuffer,
157 mMaxSize, nullptr, GL_DYNAMIC_DRAW);
159 mFreeBlocks = new BufferBlock(0, mMaxSize);
163 * Sets the mesh's offsets and copies its associated vertices into
164 * the mesh buffer (VBO).
166 void PatchCache::setupMesh(Patch* newMesh) {
167 // This call ensures the VBO exists and that it is bound
169 createVertexBuffer();
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) {
176 createVertexBuffer();
179 // Find a block where we can fit the mesh
180 BufferBlock* previous = nullptr;
181 BufferBlock* block = mFreeBlocks;
184 if (block->size >= size) {
191 // We have enough space left in the buffer, but it's
192 // too fragmented, let's clear the cache
195 createVertexBuffer();
200 // Copy the 9patch mesh in the VBO
201 newMesh->positionOffset = (GLintptr) (block->offset);
202 newMesh->textureOffset = newMesh->positionOffset + kMeshTextureOffset;
204 mRenderState.meshState().updateMeshBufferSubData(mMeshBuffer, newMesh->positionOffset, size,
205 newMesh->vertices.get());
207 // Remove the block since we've used it entirely
208 if (block->size == size) {
210 previous->next = block->next;
212 mFreeBlocks = block->next;
216 // Resize the block now that it's occupied
217 block->offset += size;
224 static const UvMapper sIdentity;
226 const Patch* PatchCache::get( const uint32_t bitmapWidth, const uint32_t bitmapHeight,
227 const float pixelWidth, const float pixelHeight, const Res_png_9patch* patch) {
229 const PatchDescription description(bitmapWidth, bitmapHeight, pixelWidth, pixelHeight, patch);
230 const Patch* mesh = mCache.get(description);
233 Patch* newMesh = new Patch(bitmapWidth, bitmapHeight,
234 pixelWidth, pixelHeight, sIdentity, patch);
236 if (newMesh->vertices) {
241 dumpFreeBlocks("Adding patch");
244 mCache.put(description, newMesh);
252 void PatchCache::dumpFreeBlocks(const char* prefix) {
254 BufferBlock* block = mFreeBlocks;
256 dump.appendFormat("->(%d, %d)", block->positionOffset, block->size);
259 ALOGD("%s: Free blocks%s", prefix, dump.string());
263 }; // namespace uirenderer
264 }; // namespace android