Home | History | Annotate | Download | only in skia
      1 /*
      2  * Copyright (C) 2017 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 "VectorDrawableAtlas.h"
     18 
     19 #include <GrRectanizer_pow2.h>
     20 #include <SkCanvas.h>
     21 #include <cmath>
     22 #include "renderthread/RenderProxy.h"
     23 #include "renderthread/RenderThread.h"
     24 #include "utils/TraceUtils.h"
     25 
     26 namespace android {
     27 namespace uirenderer {
     28 namespace skiapipeline {
     29 
     30 VectorDrawableAtlas::VectorDrawableAtlas(size_t surfaceArea, StorageMode storageMode)
     31         : mWidth((int)std::sqrt(surfaceArea))
     32         , mHeight((int)std::sqrt(surfaceArea))
     33         , mStorageMode(storageMode) {}
     34 
     35 void VectorDrawableAtlas::prepareForDraw(GrContext* context) {
     36     if (StorageMode::allowSharedSurface == mStorageMode) {
     37         if (!mSurface) {
     38             mSurface = createSurface(mWidth, mHeight, context);
     39             mRectanizer = std::make_unique<GrRectanizerPow2>(mWidth, mHeight);
     40             mPixelUsedByVDs = 0;
     41             mPixelAllocated = 0;
     42             mConsecutiveFailures = 0;
     43             mFreeRects.clear();
     44         } else {
     45             if (isFragmented()) {
     46                 // Invoke repack outside renderFrame to avoid jank.
     47                 renderthread::RenderProxy::repackVectorDrawableAtlas();
     48             }
     49         }
     50     }
     51 }
     52 
     53 #define MAX_CONSECUTIVE_FAILURES 5
     54 #define MAX_UNUSED_RATIO 2.0f
     55 
     56 bool VectorDrawableAtlas::isFragmented() {
     57     return mConsecutiveFailures > MAX_CONSECUTIVE_FAILURES &&
     58            mPixelUsedByVDs * MAX_UNUSED_RATIO < mPixelAllocated;
     59 }
     60 
     61 void VectorDrawableAtlas::repackIfNeeded(GrContext* context) {
     62     // We repackage when atlas failed to allocate space MAX_CONSECUTIVE_FAILURES consecutive
     63     // times and the atlas allocated pixels are at least MAX_UNUSED_RATIO times higher than pixels
     64     // used by atlas VDs.
     65     if (isFragmented() && mSurface) {
     66         repack(context);
     67     }
     68 }
     69 
     70 // compare to CacheEntry objects based on VD area.
     71 bool VectorDrawableAtlas::compareCacheEntry(const CacheEntry& first, const CacheEntry& second) {
     72     return first.VDrect.width() * first.VDrect.height() <
     73            second.VDrect.width() * second.VDrect.height();
     74 }
     75 
     76 void VectorDrawableAtlas::repack(GrContext* context) {
     77     ATRACE_CALL();
     78     sk_sp<SkSurface> newSurface;
     79     SkCanvas* canvas = nullptr;
     80     if (StorageMode::allowSharedSurface == mStorageMode) {
     81         newSurface = createSurface(mWidth, mHeight, context);
     82         if (!newSurface) {
     83             return;
     84         }
     85         canvas = newSurface->getCanvas();
     86         canvas->clear(SK_ColorTRANSPARENT);
     87         mRectanizer = std::make_unique<GrRectanizerPow2>(mWidth, mHeight);
     88     } else {
     89         if (!mSurface) {
     90             return;  // nothing to repack
     91         }
     92         mRectanizer.reset();
     93     }
     94     mFreeRects.clear();
     95     SkImage* sourceImageAtlas = nullptr;
     96     if (mSurface) {
     97         sourceImageAtlas = mSurface->makeImageSnapshot().get();
     98     }
     99 
    100     // Sort the list by VD size, which allows for the smallest VDs to get first in the atlas.
    101     // Sorting is safe, because it does not affect iterator validity.
    102     if (mRects.size() <= 100) {
    103         mRects.sort(compareCacheEntry);
    104     }
    105 
    106     for (CacheEntry& entry : mRects) {
    107         SkRect currentVDRect = entry.VDrect;
    108         SkImage* sourceImage;  // copy either from the atlas or from a standalone surface
    109         if (entry.surface) {
    110             if (!fitInAtlas(currentVDRect.width(), currentVDRect.height())) {
    111                 continue;  // don't even try to repack huge VD
    112             }
    113             sourceImage = entry.surface->makeImageSnapshot().get();
    114         } else {
    115             sourceImage = sourceImageAtlas;
    116         }
    117         size_t VDRectArea = currentVDRect.width() * currentVDRect.height();
    118         SkIPoint16 pos;
    119         if (canvas && mRectanizer->addRect(currentVDRect.width(), currentVDRect.height(), &pos)) {
    120             SkRect newRect =
    121                     SkRect::MakeXYWH(pos.fX, pos.fY, currentVDRect.width(), currentVDRect.height());
    122             canvas->drawImageRect(sourceImage, currentVDRect, newRect, nullptr);
    123             entry.VDrect = newRect;
    124             entry.rect = newRect;
    125             if (entry.surface) {
    126                 // A rectangle moved from a standalone surface to the atlas.
    127                 entry.surface = nullptr;
    128                 mPixelUsedByVDs += VDRectArea;
    129             }
    130         } else {
    131             // Repack failed for this item. If it is not already, store it in a standalone
    132             // surface.
    133             if (!entry.surface) {
    134                 // A rectangle moved from an atlas to a standalone surface.
    135                 mPixelUsedByVDs -= VDRectArea;
    136                 SkRect newRect = SkRect::MakeWH(currentVDRect.width(), currentVDRect.height());
    137                 entry.surface = createSurface(newRect.width(), newRect.height(), context);
    138                 auto tempCanvas = entry.surface->getCanvas();
    139                 tempCanvas->clear(SK_ColorTRANSPARENT);
    140                 tempCanvas->drawImageRect(sourceImageAtlas, currentVDRect, newRect, nullptr);
    141                 entry.VDrect = newRect;
    142                 entry.rect = newRect;
    143             }
    144         }
    145     }
    146     mPixelAllocated = mPixelUsedByVDs;
    147     context->flush();
    148     mSurface = newSurface;
    149     mConsecutiveFailures = 0;
    150 }
    151 
    152 AtlasEntry VectorDrawableAtlas::requestNewEntry(int width, int height, GrContext* context) {
    153     AtlasEntry result;
    154     if (width <= 0 || height <= 0) {
    155         return result;
    156     }
    157 
    158     if (mSurface) {
    159         const size_t area = width * height;
    160 
    161         // Use a rectanizer to allocate unused space from the atlas surface.
    162         bool notTooBig = fitInAtlas(width, height);
    163         SkIPoint16 pos;
    164         if (notTooBig && mRectanizer->addRect(width, height, &pos)) {
    165             mPixelUsedByVDs += area;
    166             mPixelAllocated += area;
    167             result.rect = SkRect::MakeXYWH(pos.fX, pos.fY, width, height);
    168             result.surface = mSurface;
    169             auto eraseIt = mRects.emplace(mRects.end(), result.rect, result.rect, nullptr);
    170             CacheEntry* entry = &(*eraseIt);
    171             entry->eraseIt = eraseIt;
    172             result.key = reinterpret_cast<AtlasKey>(entry);
    173             mConsecutiveFailures = 0;
    174             return result;
    175         }
    176 
    177         // Try to reuse atlas memory from rectangles freed by "releaseEntry".
    178         auto freeRectIt = mFreeRects.lower_bound(area);
    179         while (freeRectIt != mFreeRects.end()) {
    180             SkRect& freeRect = freeRectIt->second;
    181             if (freeRect.width() >= width && freeRect.height() >= height) {
    182                 result.rect = SkRect::MakeXYWH(freeRect.fLeft, freeRect.fTop, width, height);
    183                 result.surface = mSurface;
    184                 auto eraseIt = mRects.emplace(mRects.end(), result.rect, freeRect, nullptr);
    185                 CacheEntry* entry = &(*eraseIt);
    186                 entry->eraseIt = eraseIt;
    187                 result.key = reinterpret_cast<AtlasKey>(entry);
    188                 mPixelUsedByVDs += area;
    189                 mFreeRects.erase(freeRectIt);
    190                 mConsecutiveFailures = 0;
    191                 return result;
    192             }
    193             freeRectIt++;
    194         }
    195 
    196         if (notTooBig && mConsecutiveFailures <= MAX_CONSECUTIVE_FAILURES) {
    197             mConsecutiveFailures++;
    198         }
    199     }
    200 
    201     // Allocate a surface for a rectangle that is too big or if atlas is full.
    202     if (nullptr != context) {
    203         result.rect = SkRect::MakeWH(width, height);
    204         result.surface = createSurface(width, height, context);
    205         auto eraseIt = mRects.emplace(mRects.end(), result.rect, result.rect, result.surface);
    206         CacheEntry* entry = &(*eraseIt);
    207         entry->eraseIt = eraseIt;
    208         result.key = reinterpret_cast<AtlasKey>(entry);
    209     }
    210 
    211     return result;
    212 }
    213 
    214 AtlasEntry VectorDrawableAtlas::getEntry(AtlasKey atlasKey) {
    215     AtlasEntry result;
    216     if (INVALID_ATLAS_KEY != atlasKey) {
    217         CacheEntry* entry = reinterpret_cast<CacheEntry*>(atlasKey);
    218         result.rect = entry->VDrect;
    219         result.surface = entry->surface;
    220         if (!result.surface) {
    221             result.surface = mSurface;
    222         }
    223         result.key = atlasKey;
    224     }
    225     return result;
    226 }
    227 
    228 void VectorDrawableAtlas::releaseEntry(AtlasKey atlasKey) {
    229     if (INVALID_ATLAS_KEY != atlasKey) {
    230         if (!renderthread::RenderThread::isCurrent()) {
    231             {
    232                 AutoMutex _lock(mReleaseKeyLock);
    233                 mKeysForRelease.push_back(atlasKey);
    234             }
    235             // invoke releaseEntry on the renderthread
    236             renderthread::RenderProxy::releaseVDAtlasEntries();
    237             return;
    238         }
    239         CacheEntry* entry = reinterpret_cast<CacheEntry*>(atlasKey);
    240         if (!entry->surface) {
    241             // Store freed atlas rectangles in "mFreeRects" and try to reuse them later, when atlas
    242             // is full.
    243             SkRect& removedRect = entry->rect;
    244             size_t rectArea = removedRect.width() * removedRect.height();
    245             mFreeRects.emplace(rectArea, removedRect);
    246             SkRect& removedVDRect = entry->VDrect;
    247             size_t VDRectArea = removedVDRect.width() * removedVDRect.height();
    248             mPixelUsedByVDs -= VDRectArea;
    249             mConsecutiveFailures = 0;
    250         }
    251         auto eraseIt = entry->eraseIt;
    252         mRects.erase(eraseIt);
    253     }
    254 }
    255 
    256 void VectorDrawableAtlas::delayedReleaseEntries() {
    257     AutoMutex _lock(mReleaseKeyLock);
    258     for (auto key : mKeysForRelease) {
    259         releaseEntry(key);
    260     }
    261     mKeysForRelease.clear();
    262 }
    263 
    264 sk_sp<SkSurface> VectorDrawableAtlas::createSurface(int width, int height, GrContext* context) {
    265     SkImageInfo info = SkImageInfo::MakeN32(width, height, kPremul_SkAlphaType);
    266     // This must have a top-left origin so that calls to surface->canvas->writePixels
    267     // performs a basic texture upload instead of a more complex drawing operation
    268     return SkSurface::MakeRenderTarget(context, SkBudgeted::kYes, info, 0, kTopLeft_GrSurfaceOrigin,
    269                                        nullptr);
    270 }
    271 
    272 void VectorDrawableAtlas::setStorageMode(StorageMode mode) {
    273     mStorageMode = mode;
    274     if (StorageMode::disallowSharedSurface == mStorageMode && mSurface) {
    275         mSurface.reset();
    276         mRectanizer.reset();
    277         mFreeRects.clear();
    278     }
    279 }
    280 
    281 } /* namespace skiapipeline */
    282 } /* namespace uirenderer */
    283 } /* namespace android */
    284