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 { CacheEntryCacheEntry114 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 fitInAtlas(int width,int height)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