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 
    123     // Assert that patch is not already garbage
    124     size_t count = mGarbage.size();
    125     for (size_t i = 0; i < count; i++) {
    126         if (patch == mGarbage[i]) {
    127             patch = NULL;
    128             break;
    129         }
    130     }
    131     LOG_ALWAYS_FATAL_IF(patch == NULL);
    132 
    133     mGarbage.push(patch);
    134 }
    135 
    136 void PatchCache::clearGarbage() {
    137     Vector<patch_pair_t> patchesToRemove;
    138 
    139     { // scope for the mutex
    140         Mutex::Autolock _l(mLock);
    141         size_t count = mGarbage.size();
    142         for (size_t i = 0; i < count; i++) {
    143             Res_png_9patch* patch = mGarbage[i];
    144             remove(patchesToRemove, patch);
    145             // A Res_png_9patch is actually an array of byte that's larger
    146             // than sizeof(Res_png_9patch). It must be freed as an array.
    147             delete[] (int8_t*) patch;
    148         }
    149         mGarbage.clear();
    150     }
    151 
    152     // TODO: We could sort patchesToRemove by offset to merge
    153     // adjacent free blocks
    154     for (size_t i = 0; i < patchesToRemove.size(); i++) {
    155         const patch_pair_t& pair = patchesToRemove[i];
    156 
    157         // Release the patch and mark the space in the free list
    158         Patch* patch = pair.getSecond();
    159         BufferBlock* block = new BufferBlock(patch->offset, patch->getSize());
    160         block->next = mFreeBlocks;
    161         mFreeBlocks = block;
    162 
    163         mSize -= patch->getSize();
    164 
    165         mCache.remove(*pair.getFirst());
    166         delete patch;
    167     }
    168 
    169 #if DEBUG_PATCHES
    170     if (patchesToRemove.size() > 0) {
    171         dumpFreeBlocks("Removed garbage");
    172     }
    173 #endif
    174 }
    175 
    176 void PatchCache::createVertexBuffer() {
    177     glBufferData(GL_ARRAY_BUFFER, mMaxSize, NULL, GL_DYNAMIC_DRAW);
    178     mSize = 0;
    179     mFreeBlocks = new BufferBlock(0, mMaxSize);
    180     mGenerationId++;
    181 }
    182 
    183 /**
    184  * Sets the mesh's offsets and copies its associated vertices into
    185  * the mesh buffer (VBO).
    186  */
    187 void PatchCache::setupMesh(Patch* newMesh, TextureVertex* vertices) {
    188     // This call ensures the VBO exists and that it is bound
    189     init(Caches::getInstance());
    190 
    191     // If we're running out of space, let's clear the entire cache
    192     uint32_t size = newMesh->getSize();
    193     if (mSize + size > mMaxSize) {
    194         clearCache();
    195         createVertexBuffer();
    196     }
    197 
    198     // Find a block where we can fit the mesh
    199     BufferBlock* previous = NULL;
    200     BufferBlock* block = mFreeBlocks;
    201     while (block) {
    202         // The mesh fits
    203         if (block->size >= size) {
    204             break;
    205         }
    206         previous = block;
    207         block = block->next;
    208     }
    209 
    210     // We have enough space left in the buffer, but it's
    211     // too fragmented, let's clear the cache
    212     if (!block) {
    213         clearCache();
    214         createVertexBuffer();
    215         previous = NULL;
    216         block = mFreeBlocks;
    217     }
    218 
    219     // Copy the 9patch mesh in the VBO
    220     newMesh->offset = (GLintptr) (block->offset);
    221     newMesh->textureOffset = newMesh->offset + gMeshTextureOffset;
    222     glBufferSubData(GL_ARRAY_BUFFER, newMesh->offset, size, vertices);
    223 
    224     // Remove the block since we've used it entirely
    225     if (block->size == size) {
    226         if (previous) {
    227             previous->next = block->next;
    228         } else {
    229             mFreeBlocks = block->next;
    230         }
    231         delete block;
    232     } else {
    233         // Resize the block now that it's occupied
    234         block->offset += size;
    235         block->size -= size;
    236     }
    237 
    238     mSize += size;
    239 }
    240 
    241 const Patch* PatchCache::get(const AssetAtlas::Entry* entry,
    242         const uint32_t bitmapWidth, const uint32_t bitmapHeight,
    243         const float pixelWidth, const float pixelHeight, const Res_png_9patch* patch) {
    244 
    245     const PatchDescription description(bitmapWidth, bitmapHeight, pixelWidth, pixelHeight, patch);
    246     const Patch* mesh = mCache.get(description);
    247 
    248     if (!mesh) {
    249         Patch* newMesh = new Patch();
    250         TextureVertex* vertices;
    251 
    252         if (entry) {
    253             // An atlas entry has a UV mapper
    254             vertices = newMesh->createMesh(bitmapWidth, bitmapHeight,
    255                     pixelWidth, pixelHeight, entry->uvMapper, patch);
    256         } else {
    257             vertices = newMesh->createMesh(bitmapWidth, bitmapHeight,
    258                     pixelWidth, pixelHeight, patch);
    259         }
    260 
    261         if (vertices) {
    262             setupMesh(newMesh, vertices);
    263         }
    264 
    265 #if DEBUG_PATCHES
    266         dumpFreeBlocks("Adding patch");
    267 #endif
    268 
    269         mCache.put(description, newMesh);
    270         return newMesh;
    271     }
    272 
    273     return mesh;
    274 }
    275 
    276 #if DEBUG_PATCHES
    277 void PatchCache::dumpFreeBlocks(const char* prefix) {
    278     String8 dump;
    279     BufferBlock* block = mFreeBlocks;
    280     while (block) {
    281         dump.appendFormat("->(%d, %d)", block->offset, block->size);
    282         block = block->next;
    283     }
    284     ALOGD("%s: Free blocks%s", prefix, dump.string());
    285 }
    286 #endif
    287 
    288 }; // namespace uirenderer
    289 }; // namespace android
    290