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