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