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