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(KB(128))
     36         , mSize(0)
     37         , mCache(LruCache<PatchDescription, Patch*>::kUnlimitedCapacity)
     38         , mMeshBuffer(0)
     39         , mFreeBlocks(nullptr) {}
     40 
     41 PatchCache::~PatchCache() {
     42     clear();
     43 }
     44 
     45 ///////////////////////////////////////////////////////////////////////////////
     46 // Caching
     47 ///////////////////////////////////////////////////////////////////////////////
     48 
     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);
     56 }
     57 
     58 int PatchCache::PatchDescription::compare(const PatchCache::PatchDescription& lhs,
     59                                           const PatchCache::PatchDescription& rhs) {
     60     return memcmp(&lhs, &rhs, sizeof(PatchDescription));
     61 }
     62 
     63 void PatchCache::clear() {
     64     clearCache();
     65 
     66     if (mMeshBuffer) {
     67         mRenderState.meshState().deleteMeshBuffer(mMeshBuffer);
     68         mMeshBuffer = 0;
     69         mSize = 0;
     70     }
     71 }
     72 
     73 void PatchCache::clearCache() {
     74     LruCache<PatchDescription, Patch*>::Iterator i(mCache);
     75     while (i.next()) {
     76         delete i.value();
     77     }
     78     mCache.clear();
     79 
     80     BufferBlock* block = mFreeBlocks;
     81     while (block) {
     82         BufferBlock* next = block->next;
     83         delete block;
     84         block = next;
     85     }
     86     mFreeBlocks = nullptr;
     87 }
     88 
     89 void PatchCache::remove(Vector<patch_pair_t>& patchesToRemove, Res_png_9patch* patch) {
     90     LruCache<PatchDescription, Patch*>::Iterator i(mCache);
     91     while (i.next()) {
     92         const PatchDescription& key = i.key();
     93         if (key.getPatch() == patch) {
     94             patchesToRemove.push(patch_pair_t(&key, i.value()));
     95         }
     96     }
     97 }
     98 
     99 void PatchCache::removeDeferred(Res_png_9patch* patch) {
    100     Mutex::Autolock _l(mLock);
    101 
    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]) {
    106             patch = nullptr;
    107             break;
    108         }
    109     }
    110     LOG_ALWAYS_FATAL_IF(patch == nullptr);
    111 
    112     mGarbage.push(patch);
    113 }
    114 
    115 void PatchCache::clearGarbage() {
    116     Vector<patch_pair_t> patchesToRemove;
    117 
    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;
    127         }
    128         mGarbage.clear();
    129     }
    130 
    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];
    135 
    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;
    140         mFreeBlocks = block;
    141 
    142         mSize -= patch->getSize();
    143 
    144         mCache.remove(*pair.getFirst());
    145         delete patch;
    146     }
    147 
    148 #if DEBUG_PATCHES
    149     if (patchesToRemove.size() > 0) {
    150         dumpFreeBlocks("Removed garbage");
    151     }
    152 #endif
    153 }
    154 
    155 void PatchCache::createVertexBuffer() {
    156     mRenderState.meshState().genOrUpdateMeshBuffer(&mMeshBuffer, mMaxSize, nullptr,
    157                                                    GL_DYNAMIC_DRAW);
    158     mSize = 0;
    159     mFreeBlocks = new BufferBlock(0, mMaxSize);
    160 }
    161 
    162 /**
    163  * Sets the mesh's offsets and copies its associated vertices into
    164  * the mesh buffer (VBO).
    165  */
    166 void PatchCache::setupMesh(Patch* newMesh) {
    167     // This call ensures the VBO exists and that it is bound
    168     if (!mMeshBuffer) {
    169         createVertexBuffer();
    170     }
    171 
    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) {
    175         clearCache();
    176         createVertexBuffer();
    177     }
    178 
    179     // Find a block where we can fit the mesh
    180     BufferBlock* previous = nullptr;
    181     BufferBlock* block = mFreeBlocks;
    182     while (block) {
    183         // The mesh fits
    184         if (block->size >= size) {
    185             break;
    186         }
    187         previous = block;
    188         block = block->next;
    189     }
    190 
    191     // We have enough space left in the buffer, but it's
    192     // too fragmented, let's clear the cache
    193     if (!block) {
    194         clearCache();
    195         createVertexBuffer();
    196         previous = nullptr;
    197         block = mFreeBlocks;
    198     }
    199 
    200     // Copy the 9patch mesh in the VBO
    201     newMesh->positionOffset = (GLintptr)(block->offset);
    202     newMesh->textureOffset = newMesh->positionOffset + kMeshTextureOffset;
    203 
    204     mRenderState.meshState().updateMeshBufferSubData(mMeshBuffer, newMesh->positionOffset, size,
    205                                                      newMesh->vertices.get());
    206 
    207     // Remove the block since we've used it entirely
    208     if (block->size == size) {
    209         if (previous) {
    210             previous->next = block->next;
    211         } else {
    212             mFreeBlocks = block->next;
    213         }
    214         delete block;
    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 static const UvMapper sIdentity;
    225 
    226 const Patch* PatchCache::get(const uint32_t bitmapWidth, const uint32_t bitmapHeight,
    227                              const float pixelWidth, const float pixelHeight,
    228                              const Res_png_9patch* patch) {
    229     const PatchDescription description(bitmapWidth, bitmapHeight, pixelWidth, pixelHeight, patch);
    230     const Patch* mesh = mCache.get(description);
    231 
    232     if (!mesh) {
    233         Patch* newMesh =
    234                 new Patch(bitmapWidth, bitmapHeight, pixelWidth, pixelHeight, sIdentity, patch);
    235 
    236         if (newMesh->vertices) {
    237             setupMesh(newMesh);
    238         }
    239 
    240 #if DEBUG_PATCHES
    241         dumpFreeBlocks("Adding patch");
    242 #endif
    243 
    244         mCache.put(description, newMesh);
    245         return newMesh;
    246     }
    247 
    248     return mesh;
    249 }
    250 
    251 #if DEBUG_PATCHES
    252 void PatchCache::dumpFreeBlocks(const char* prefix) {
    253     String8 dump;
    254     BufferBlock* block = mFreeBlocks;
    255     while (block) {
    256         dump.appendFormat("->(%d, %d)", block->positionOffset, block->size);
    257         block = block->next;
    258     }
    259     ALOGD("%s: Free blocks%s", prefix, dump.string());
    260 }
    261 #endif
    262 
    263 };  // namespace uirenderer
    264 };  // namespace android
    265