Home | History | Annotate | Download | only in hwui
      1 /*
      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 
     19 #include <utils/JenkinsHash.h>
     20 #include <utils/Log.h>
     21 
     22 #include "Caches.h"
     23 #include "PatchCache.h"
     24 #include "Properties.h"
     25 
     26 namespace android {
     27 namespace uirenderer {
     28 
     29 ///////////////////////////////////////////////////////////////////////////////
     30 // Constructors/destructor
     31 ///////////////////////////////////////////////////////////////////////////////
     32 
     33 PatchCache::PatchCache():
     34         mSize(0), mCache(LruCache<PatchDescription, Patch*>::kUnlimitedCapacity),
     35         mMeshBuffer(0), mFreeBlocks(NULL), mGenerationId(0) {
     36     char property[PROPERTY_VALUE_MAX];
     37     if (property_get(PROPERTY_PATCH_CACHE_SIZE, property, NULL) > 0) {
     38         INIT_LOGD("  Setting patch cache size to %skB", property);
     39         mMaxSize = KB(atoi(property));
     40     } else {
     41         INIT_LOGD("  Using default patch cache size of %.2fkB", DEFAULT_PATCH_CACHE_SIZE);
     42         mMaxSize = KB(DEFAULT_PATCH_CACHE_SIZE);
     43     }
     44 }
     45 
     46 PatchCache::~PatchCache() {
     47     clear();
     48 }
     49 
     50 void PatchCache::init(Caches& caches) {
     51     bool created = false;
     52     if (!mMeshBuffer) {
     53         glGenBuffers(1, &mMeshBuffer);
     54         created = true;
     55     }
     56 
     57     caches.bindMeshBuffer(mMeshBuffer);
     58     caches.resetVertexPointers();
     59 
     60     if (created) {
     61         createVertexBuffer();
     62     }
     63 }
     64 
     65 ///////////////////////////////////////////////////////////////////////////////
     66 // Caching
     67 ///////////////////////////////////////////////////////////////////////////////
     68 
     69 hash_t PatchCache::PatchDescription::hash() const {
     70     uint32_t hash = JenkinsHashMix(0, android::hash_type(mPatch));
     71     hash = JenkinsHashMix(hash, mBitmapWidth);
     72     hash = JenkinsHashMix(hash, mBitmapHeight);
     73     hash = JenkinsHashMix(hash, mPixelWidth);
     74     hash = JenkinsHashMix(hash, mPixelHeight);
     75     return JenkinsHashWhiten(hash);
     76 }
     77 
     78 int PatchCache::PatchDescription::compare(const PatchCache::PatchDescription& lhs,
     79             const PatchCache::PatchDescription& rhs) {
     80     return memcmp(&lhs, &rhs, sizeof(PatchDescription));
     81 }
     82 
     83 void PatchCache::clear() {
     84     clearCache();
     85 
     86     if (mMeshBuffer) {
     87         Caches::getInstance().unbindMeshBuffer();
     88         glDeleteBuffers(1, &mMeshBuffer);
     89         mMeshBuffer = 0;
     90         mSize = 0;
     91     }
     92 }
     93 
     94 void PatchCache::clearCache() {
     95     LruCache<PatchDescription, Patch*>::Iterator i(mCache);
     96     while (i.next()) {
     97         delete i.value();
     98     }
     99     mCache.clear();
    100 
    101     BufferBlock* block = mFreeBlocks;
    102     while (block) {
    103         BufferBlock* next = block->next;
    104         delete block;
    105         block = next;
    106     }
    107     mFreeBlocks = NULL;
    108 }
    109 
    110 void PatchCache::remove(Vector<patch_pair_t>& patchesToRemove, Res_png_9patch* patch) {
    111     LruCache<PatchDescription, Patch*>::Iterator i(mCache);
    112     while (i.next()) {
    113         const PatchDescription& key = i.key();
    114         if (key.getPatch() == patch) {
    115             patchesToRemove.push(patch_pair_t(&key, i.value()));
    116         }
    117     }
    118 }
    119 
    120 void PatchCache::removeDeferred(Res_png_9patch* patch) {
    121     Mutex::Autolock _l(mLock);
    122     mGarbage.push(patch);
    123 }
    124 
    125 void PatchCache::clearGarbage() {
    126     Vector<patch_pair_t> patchesToRemove;
    127 
    128     { // scope for the mutex
    129         Mutex::Autolock _l(mLock);
    130         size_t count = mGarbage.size();
    131         for (size_t i = 0; i < count; i++) {
    132             remove(patchesToRemove, mGarbage[i]);
    133         }
    134         mGarbage.clear();
    135     }
    136 
    137     // TODO: We could sort patchesToRemove by offset to merge
    138     // adjacent free blocks
    139     for (size_t i = 0; i < patchesToRemove.size(); i++) {
    140         const patch_pair_t& pair = patchesToRemove[i];
    141 
    142         // Add a new free block to the list
    143         const Patch* patch = pair.getSecond();
    144         BufferBlock* block = new BufferBlock(patch->offset, patch->getSize());
    145         block->next = mFreeBlocks;
    146         mFreeBlocks = block;
    147 
    148         mSize -= patch->getSize();
    149 
    150         mCache.remove(*pair.getFirst());
    151     }
    152 
    153 #if DEBUG_PATCHES
    154     if (patchesToRemove.size() > 0) {
    155         dumpFreeBlocks("Removed garbage");
    156     }
    157 #endif
    158 }
    159 
    160 void PatchCache::createVertexBuffer() {
    161     glBufferData(GL_ARRAY_BUFFER, mMaxSize, NULL, GL_DYNAMIC_DRAW);
    162     mSize = 0;
    163     mFreeBlocks = new BufferBlock(0, mMaxSize);
    164     mGenerationId++;
    165 }
    166 
    167 /**
    168  * Sets the mesh's offsets and copies its associated vertices into
    169  * the mesh buffer (VBO).
    170  */
    171 void PatchCache::setupMesh(Patch* newMesh, TextureVertex* vertices) {
    172     // This call ensures the VBO exists and that it is bound
    173     init(Caches::getInstance());
    174 
    175     // If we're running out of space, let's clear the entire cache
    176     uint32_t size = newMesh->getSize();
    177     if (mSize + size > mMaxSize) {
    178         clearCache();
    179         createVertexBuffer();
    180     }
    181 
    182     // Find a block where we can fit the mesh
    183     BufferBlock* previous = NULL;
    184     BufferBlock* block = mFreeBlocks;
    185     while (block) {
    186         // The mesh fits
    187         if (block->size >= size) {
    188             break;
    189         }
    190         previous = block;
    191         block = block->next;
    192     }
    193 
    194     // We have enough space left in the buffer, but it's
    195     // too fragmented, let's clear the cache
    196     if (!block) {
    197         clearCache();
    198         createVertexBuffer();
    199         previous = NULL;
    200         block = mFreeBlocks;
    201     }
    202 
    203     // Copy the 9patch mesh in the VBO
    204     newMesh->offset = (GLintptr) (block->offset);
    205     newMesh->textureOffset = newMesh->offset + gMeshTextureOffset;
    206     glBufferSubData(GL_ARRAY_BUFFER, newMesh->offset, size, vertices);
    207 
    208     // Remove the block since we've used it entirely
    209     if (block->size == size) {
    210         if (previous) {
    211             previous->next = block->next;
    212         } else {
    213             mFreeBlocks = block->next;
    214         }
    215     } 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 
    224 const Patch* PatchCache::get(const AssetAtlas::Entry* entry,
    225         const uint32_t bitmapWidth, const uint32_t bitmapHeight,
    226         const float pixelWidth, const float pixelHeight, const Res_png_9patch* patch) {
    227 
    228     const PatchDescription description(bitmapWidth, bitmapHeight, pixelWidth, pixelHeight, patch);
    229     const Patch* mesh = mCache.get(description);
    230 
    231     if (!mesh) {
    232         Patch* newMesh = new Patch();
    233         TextureVertex* vertices;
    234 
    235         if (entry) {
    236             // An atlas entry has a UV mapper
    237             vertices = newMesh->createMesh(bitmapWidth, bitmapHeight,
    238                     pixelWidth, pixelHeight, entry->uvMapper, patch);
    239         } else {
    240             vertices = newMesh->createMesh(bitmapWidth, bitmapHeight,
    241                     pixelWidth, pixelHeight, patch);
    242         }
    243 
    244         if (vertices) {
    245             setupMesh(newMesh, vertices);
    246         }
    247 
    248 #if DEBUG_PATCHES
    249         dumpFreeBlocks("Adding patch");
    250 #endif
    251 
    252         mCache.put(description, newMesh);
    253         return newMesh;
    254     }
    255 
    256     return mesh;
    257 }
    258 
    259 #if DEBUG_PATCHES
    260 void PatchCache::dumpFreeBlocks(const char* prefix) {
    261     String8 dump;
    262     BufferBlock* block = mFreeBlocks;
    263     while (block) {
    264         dump.appendFormat("->(%d, %d)", block->offset, block->size);
    265         block = block->next;
    266     }
    267     ALOGD("%s: Free blocks%s", prefix, dump.string());
    268 }
    269 #endif
    270 
    271 }; // namespace uirenderer
    272 }; // namespace android
    273