03240107a5
Test: none, it's unused code Change-Id: If25f4f22d8f3a9819f673bacce9ad4c213dd73ed
265 lines
7.4 KiB
C++
265 lines
7.4 KiB
C++
/*
|
|
* Copyright (C) 2010 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include <utils/JenkinsHash.h>
|
|
#include <utils/Log.h>
|
|
|
|
#include "Caches.h"
|
|
#include "Patch.h"
|
|
#include "PatchCache.h"
|
|
#include "Properties.h"
|
|
#include "renderstate/RenderState.h"
|
|
|
|
namespace android {
|
|
namespace uirenderer {
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Constructors/destructor
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
PatchCache::PatchCache(RenderState& renderState)
|
|
: mRenderState(renderState)
|
|
, mMaxSize(Properties::patchCacheSize)
|
|
, mSize(0)
|
|
, mCache(LruCache<PatchDescription, Patch*>::kUnlimitedCapacity)
|
|
, mMeshBuffer(0)
|
|
, mFreeBlocks(nullptr) {}
|
|
|
|
PatchCache::~PatchCache() {
|
|
clear();
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Caching
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
hash_t PatchCache::PatchDescription::hash() const {
|
|
uint32_t hash = JenkinsHashMix(0, android::hash_type(mPatch));
|
|
hash = JenkinsHashMix(hash, mBitmapWidth);
|
|
hash = JenkinsHashMix(hash, mBitmapHeight);
|
|
hash = JenkinsHashMix(hash, mPixelWidth);
|
|
hash = JenkinsHashMix(hash, mPixelHeight);
|
|
return JenkinsHashWhiten(hash);
|
|
}
|
|
|
|
int PatchCache::PatchDescription::compare(const PatchCache::PatchDescription& lhs,
|
|
const PatchCache::PatchDescription& rhs) {
|
|
return memcmp(&lhs, &rhs, sizeof(PatchDescription));
|
|
}
|
|
|
|
void PatchCache::clear() {
|
|
clearCache();
|
|
|
|
if (mMeshBuffer) {
|
|
mRenderState.meshState().deleteMeshBuffer(mMeshBuffer);
|
|
mMeshBuffer = 0;
|
|
mSize = 0;
|
|
}
|
|
}
|
|
|
|
void PatchCache::clearCache() {
|
|
LruCache<PatchDescription, Patch*>::Iterator i(mCache);
|
|
while (i.next()) {
|
|
delete i.value();
|
|
}
|
|
mCache.clear();
|
|
|
|
BufferBlock* block = mFreeBlocks;
|
|
while (block) {
|
|
BufferBlock* next = block->next;
|
|
delete block;
|
|
block = next;
|
|
}
|
|
mFreeBlocks = nullptr;
|
|
}
|
|
|
|
void PatchCache::remove(Vector<patch_pair_t>& patchesToRemove, Res_png_9patch* patch) {
|
|
LruCache<PatchDescription, Patch*>::Iterator i(mCache);
|
|
while (i.next()) {
|
|
const PatchDescription& key = i.key();
|
|
if (key.getPatch() == patch) {
|
|
patchesToRemove.push(patch_pair_t(&key, i.value()));
|
|
}
|
|
}
|
|
}
|
|
|
|
void PatchCache::removeDeferred(Res_png_9patch* patch) {
|
|
Mutex::Autolock _l(mLock);
|
|
|
|
// Assert that patch is not already garbage
|
|
size_t count = mGarbage.size();
|
|
for (size_t i = 0; i < count; i++) {
|
|
if (patch == mGarbage[i]) {
|
|
patch = nullptr;
|
|
break;
|
|
}
|
|
}
|
|
LOG_ALWAYS_FATAL_IF(patch == nullptr);
|
|
|
|
mGarbage.push(patch);
|
|
}
|
|
|
|
void PatchCache::clearGarbage() {
|
|
Vector<patch_pair_t> patchesToRemove;
|
|
|
|
{ // scope for the mutex
|
|
Mutex::Autolock _l(mLock);
|
|
size_t count = mGarbage.size();
|
|
for (size_t i = 0; i < count; i++) {
|
|
Res_png_9patch* patch = mGarbage[i];
|
|
remove(patchesToRemove, patch);
|
|
// A Res_png_9patch is actually an array of byte that's larger
|
|
// than sizeof(Res_png_9patch). It must be freed as an array.
|
|
delete[] (int8_t*) patch;
|
|
}
|
|
mGarbage.clear();
|
|
}
|
|
|
|
// TODO: We could sort patchesToRemove by offset to merge
|
|
// adjacent free blocks
|
|
for (size_t i = 0; i < patchesToRemove.size(); i++) {
|
|
const patch_pair_t& pair = patchesToRemove[i];
|
|
|
|
// Release the patch and mark the space in the free list
|
|
Patch* patch = pair.getSecond();
|
|
BufferBlock* block = new BufferBlock(patch->positionOffset, patch->getSize());
|
|
block->next = mFreeBlocks;
|
|
mFreeBlocks = block;
|
|
|
|
mSize -= patch->getSize();
|
|
|
|
mCache.remove(*pair.getFirst());
|
|
delete patch;
|
|
}
|
|
|
|
#if DEBUG_PATCHES
|
|
if (patchesToRemove.size() > 0) {
|
|
dumpFreeBlocks("Removed garbage");
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void PatchCache::createVertexBuffer() {
|
|
mRenderState.meshState().genOrUpdateMeshBuffer(&mMeshBuffer,
|
|
mMaxSize, nullptr, GL_DYNAMIC_DRAW);
|
|
mSize = 0;
|
|
mFreeBlocks = new BufferBlock(0, mMaxSize);
|
|
}
|
|
|
|
/**
|
|
* Sets the mesh's offsets and copies its associated vertices into
|
|
* the mesh buffer (VBO).
|
|
*/
|
|
void PatchCache::setupMesh(Patch* newMesh) {
|
|
// This call ensures the VBO exists and that it is bound
|
|
if (!mMeshBuffer) {
|
|
createVertexBuffer();
|
|
}
|
|
|
|
// If we're running out of space, let's clear the entire cache
|
|
uint32_t size = newMesh->getSize();
|
|
if (mSize + size > mMaxSize) {
|
|
clearCache();
|
|
createVertexBuffer();
|
|
}
|
|
|
|
// Find a block where we can fit the mesh
|
|
BufferBlock* previous = nullptr;
|
|
BufferBlock* block = mFreeBlocks;
|
|
while (block) {
|
|
// The mesh fits
|
|
if (block->size >= size) {
|
|
break;
|
|
}
|
|
previous = block;
|
|
block = block->next;
|
|
}
|
|
|
|
// We have enough space left in the buffer, but it's
|
|
// too fragmented, let's clear the cache
|
|
if (!block) {
|
|
clearCache();
|
|
createVertexBuffer();
|
|
previous = nullptr;
|
|
block = mFreeBlocks;
|
|
}
|
|
|
|
// Copy the 9patch mesh in the VBO
|
|
newMesh->positionOffset = (GLintptr) (block->offset);
|
|
newMesh->textureOffset = newMesh->positionOffset + kMeshTextureOffset;
|
|
|
|
mRenderState.meshState().updateMeshBufferSubData(mMeshBuffer, newMesh->positionOffset, size,
|
|
newMesh->vertices.get());
|
|
|
|
// Remove the block since we've used it entirely
|
|
if (block->size == size) {
|
|
if (previous) {
|
|
previous->next = block->next;
|
|
} else {
|
|
mFreeBlocks = block->next;
|
|
}
|
|
delete block;
|
|
} else {
|
|
// Resize the block now that it's occupied
|
|
block->offset += size;
|
|
block->size -= size;
|
|
}
|
|
|
|
mSize += size;
|
|
}
|
|
|
|
static const UvMapper sIdentity;
|
|
|
|
const Patch* PatchCache::get( const uint32_t bitmapWidth, const uint32_t bitmapHeight,
|
|
const float pixelWidth, const float pixelHeight, const Res_png_9patch* patch) {
|
|
|
|
const PatchDescription description(bitmapWidth, bitmapHeight, pixelWidth, pixelHeight, patch);
|
|
const Patch* mesh = mCache.get(description);
|
|
|
|
if (!mesh) {
|
|
Patch* newMesh = new Patch(bitmapWidth, bitmapHeight,
|
|
pixelWidth, pixelHeight, sIdentity, patch);
|
|
|
|
if (newMesh->vertices) {
|
|
setupMesh(newMesh);
|
|
}
|
|
|
|
#if DEBUG_PATCHES
|
|
dumpFreeBlocks("Adding patch");
|
|
#endif
|
|
|
|
mCache.put(description, newMesh);
|
|
return newMesh;
|
|
}
|
|
|
|
return mesh;
|
|
}
|
|
|
|
#if DEBUG_PATCHES
|
|
void PatchCache::dumpFreeBlocks(const char* prefix) {
|
|
String8 dump;
|
|
BufferBlock* block = mFreeBlocks;
|
|
while (block) {
|
|
dump.appendFormat("->(%d, %d)", block->positionOffset, block->size);
|
|
block = block->next;
|
|
}
|
|
ALOGD("%s: Free blocks%s", prefix, dump.string());
|
|
}
|
|
#endif
|
|
|
|
}; // namespace uirenderer
|
|
}; // namespace android
|