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 #pragma once
     18 
     19 #include <map>
     20 #include <SkSurface.h>
     21 #include <utils/FatVector.h>
     22 #include <utils/RefBase.h>
     23 #include <list>
     24 
     25 class GrRectanizer;
     26 
     27 namespace android {
     28 namespace uirenderer {
     29 namespace skiapipeline {
     30 
     31 typedef uintptr_t AtlasKey;
     32 
     33 #define INVALID_ATLAS_KEY 0
     34 
     35 struct AtlasEntry {
     36     sk_sp<SkSurface> surface;
     37     SkRect rect;
     38     AtlasKey key = INVALID_ATLAS_KEY;
     39 };
     40 
     41 /**
     42  * VectorDrawableAtlas provides offscreen buffers used to draw VD and AnimatedVD.
     43  * VectorDrawableAtlas can allocate a standalone surface or provide a subrect from a shared surface.
     44  * VectorDrawableAtlas is owned by the CacheManager and weak pointers are kept by each
     45  * VectorDrawable that is using it. VectorDrawableAtlas and its surface can be deleted at any time,
     46  * except during a renderFrame call. VectorDrawable does not contain a pointer to atlas SkSurface
     47  * nor any coordinates into the atlas, but instead holds a rectangle "id", which is resolved only
     48  * when drawing. This design makes VectorDrawableAtlas free to move the data internally.
     49  * At draw time a VectorDrawable may find, that its atlas has been deleted, which will make it
     50  * draw in a standalone cache surface not part of an atlas. In this case VD won't use
     51  * VectorDrawableAtlas until the next frame.
     52  * VectorDrawableAtlas tries to fit VDs in the atlas SkSurface. If there is not enough space in
     53  * the atlas, VectorDrawableAtlas creates a standalone surface for each VD.
     54  * When a VectorDrawable is deleted, it invokes VectorDrawableAtlas::releaseEntry, which is keeping
     55  * track of free spaces and allow to reuse the surface for another VD.
     56  */
     57  //TODO: Check if not using atlas for AnimatedVD is more efficient.
     58  //TODO: For low memory situations, when there are no paint effects in VD, we may render without an
     59  //TODO: offscreen surface.
     60 class VectorDrawableAtlas : public virtual RefBase {
     61 public:
     62     enum class StorageMode {
     63         allowSharedSurface,
     64         disallowSharedSurface
     65     };
     66 
     67     VectorDrawableAtlas(size_t surfaceArea,
     68             StorageMode storageMode = StorageMode::allowSharedSurface);
     69 
     70     /**
     71      * "prepareForDraw" may allocate a new surface if needed. It may schedule to repack the
     72      * atlas at a later time.
     73      */
     74     void prepareForDraw(GrContext* context);
     75 
     76     /**
     77      * Repack the atlas if needed, by moving used rectangles into a new atlas surface.
     78      * The goal of repacking is to fix a fragmented atlas.
     79      */
     80     void repackIfNeeded(GrContext* context);
     81 
     82     /**
     83      * Returns true if atlas is fragmented and repack is needed.
     84      */
     85     bool isFragmented();
     86 
     87     /**
     88      * "requestNewEntry" is called by VectorDrawable to allocate a new rectangle area from the atlas
     89      * or create a standalone surface if atlas is full.
     90      * On success it returns a non-negative unique id, which can be used later with "getEntry" and
     91      * "releaseEntry".
     92      */
     93     AtlasEntry requestNewEntry(int width, int height, GrContext* context);
     94 
     95     /**
     96      * "getEntry" extracts coordinates and surface of a previously created rectangle.
     97      * "atlasKey" is an unique id created by "requestNewEntry". Passing a non-existing "atlasKey" is
     98      * causing an undefined behaviour.
     99      * On success it returns a rectangle Id -> may be same or different from "atlasKey" if
    100      * implementation decides to move the record internally.
    101      */
    102     AtlasEntry getEntry(AtlasKey atlasKey);
    103 
    104     /**
    105      * "releaseEntry" is invoked when a VectorDrawable is deleted. Passing a non-existing "atlasKey"
    106      * is causing an undefined behaviour.
    107      */
    108     void releaseEntry(AtlasKey atlasKey);
    109 
    110     void setStorageMode(StorageMode mode);
    111 
    112 private:
    113     struct CacheEntry {
    114         CacheEntry(const SkRect& newVDrect, const SkRect& newRect,
    115                 const sk_sp<SkSurface>& newSurface)
    116                 : VDrect(newVDrect)
    117                 , rect(newRect)
    118                 , surface(newSurface) { }
    119 
    120         /**
    121          * size and position of VectorDrawable into the atlas or in "this.surface"
    122          */
    123         SkRect VDrect;
    124 
    125         /**
    126          * rect allocated in atlas surface or "this.surface". It may be bigger than "VDrect"
    127          */
    128         SkRect rect;
    129 
    130         /**
    131          * this surface is used if atlas is full or VD is too big
    132          */
    133         sk_sp<SkSurface> surface;
    134 
    135         /**
    136          * iterator is used to delete self with a constant complexity (without traversing the list)
    137          */
    138         std::list<CacheEntry>::iterator eraseIt;
    139     };
    140 
    141     /**
    142      * atlas surface shared by all VDs
    143      */
    144     sk_sp<SkSurface> mSurface;
    145 
    146     std::unique_ptr<GrRectanizer> mRectanizer;
    147     const int mWidth;
    148     const int mHeight;
    149 
    150     /**
    151      * "mRects" keeps records only for rectangles used by VDs. List has nice properties: constant
    152      * complexity to insert and erase and references are not invalidated by insert/erase.
    153      */
    154     std::list<CacheEntry> mRects;
    155 
    156     /**
    157      * Rectangles freed by "releaseEntry" are removed from "mRects" and added to "mFreeRects".
    158      * "mFreeRects" is using for an index the rectangle area. There could be more than one free
    159      * rectangle with the same area, which is the reason to use "multimap" instead of "map".
    160      */
    161     std::multimap<size_t, SkRect> mFreeRects;
    162 
    163     /**
    164      * area in atlas used by VectorDrawables (area in standalone surface not counted)
    165      */
    166     int mPixelUsedByVDs = 0;
    167 
    168     /**
    169      * area allocated in mRectanizer
    170      */
    171     int mPixelAllocated = 0;
    172 
    173     /**
    174      * Consecutive times we had to allocate standalone surfaces, because atlas was full.
    175      */
    176     int mConsecutiveFailures = 0;
    177 
    178     /**
    179      * mStorageMode allows using a shared surface to store small vector drawables.
    180      * Using a shared surface can boost the performance by allowing GL ops to be batched, but may
    181      * consume more memory.
    182      */
    183     StorageMode mStorageMode;
    184 
    185     sk_sp<SkSurface> createSurface(int width, int height, GrContext* context);
    186 
    187     inline bool fitInAtlas(int width, int height) {
    188         return 2*width < mWidth && 2*height < mHeight;
    189     }
    190 
    191     void repack(GrContext* context);
    192 
    193     static bool compareCacheEntry(const CacheEntry& first, const CacheEntry& second);
    194 };
    195 
    196 } /* namespace skiapipeline */
    197 } /* namespace uirenderer */
    198 } /* namespace android */
    199