• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <SkSurface.h>
20 #include <utils/FatVector.h>
21 #include <utils/RefBase.h>
22 #include <utils/Thread.h>
23 #include <list>
24 #include <map>
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  */
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.
61 class VectorDrawableAtlas : public virtual RefBase {
62 public:
63     enum class StorageMode { allowSharedSurface, disallowSharedSurface };
64 
65     explicit VectorDrawableAtlas(size_t surfaceArea,
66                                  StorageMode storageMode = StorageMode::allowSharedSurface);
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"
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.
106      */
107     void releaseEntry(AtlasKey atlasKey);
108 
109     void setStorageMode(StorageMode mode);
110 
111     /**
112      * "delayedReleaseEntries" is indirectly invoked by "releaseEntry", when "releaseEntry" is
113      * invoked from a non render thread.
114      */
115     void delayedReleaseEntries();
116 
117 private:
118     struct CacheEntry {
CacheEntryCacheEntry119         CacheEntry(const SkRect& newVDrect, const SkRect& newRect,
120                    const sk_sp<SkSurface>& newSurface)
121                 : VDrect(newVDrect), rect(newRect), surface(newSurface) {}
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 
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 
199     sk_sp<SkSurface> createSurface(int width, int height, GrContext* context);
200 
fitInAtlas(int width,int height)201     inline bool fitInAtlas(int width, int height) {
202         return 2 * width < mWidth && 2 * height < mHeight;
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 */
213