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 
VectorDrawableAtlas(size_t surfaceArea,StorageMode storageMode)30 VectorDrawableAtlas::VectorDrawableAtlas(size_t surfaceArea, StorageMode storageMode)
31         : mWidth((int)std::sqrt(surfaceArea))
32         , mHeight((int)std::sqrt(surfaceArea))
33         , mStorageMode(storageMode) {}
34 
prepareForDraw(GrContext * context)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 
isFragmented()56 bool VectorDrawableAtlas::isFragmented() {
57     return mConsecutiveFailures > MAX_CONSECUTIVE_FAILURES &&
58            mPixelUsedByVDs * MAX_UNUSED_RATIO < mPixelAllocated;
59 }
60 
repackIfNeeded(GrContext * context)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.
compareCacheEntry(const CacheEntry & first,const CacheEntry & second)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 
repack(GrContext * context)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 
requestNewEntry(int width,int height,GrContext * context)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 
getEntry(AtlasKey atlasKey)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 
releaseEntry(AtlasKey atlasKey)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 
delayedReleaseEntries()256 void VectorDrawableAtlas::delayedReleaseEntries() {
257     AutoMutex _lock(mReleaseKeyLock);
258     for (auto key : mKeysForRelease) {
259         releaseEntry(key);
260     }
261     mKeysForRelease.clear();
262 }
263 
createSurface(int width,int height,GrContext * context)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 
setStorageMode(StorageMode mode)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