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 "utils/TraceUtils.h"
23 #include "renderthread/RenderProxy.h"
24
25 namespace android {
26 namespace uirenderer {
27 namespace skiapipeline {
28
VectorDrawableAtlas(size_t surfaceArea,StorageMode storageMode)29 VectorDrawableAtlas::VectorDrawableAtlas(size_t surfaceArea, StorageMode storageMode)
30 : mWidth((int)std::sqrt(surfaceArea))
31 , mHeight((int)std::sqrt(surfaceArea))
32 , mStorageMode(storageMode) {
33 }
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 {
73 return first.VDrect.width()*first.VDrect.height() < 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 = SkRect::MakeXYWH(pos.fX, pos.fY, currentVDRect.width(),
121 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(),
137 currentVDRect.height());
138 entry.surface = createSurface(newRect.width(), newRect.height(), context);
139 auto tempCanvas = entry.surface->getCanvas();
140 tempCanvas->clear(SK_ColorTRANSPARENT);
141 tempCanvas->drawImageRect(sourceImageAtlas, currentVDRect, newRect, nullptr);
142 entry.VDrect = newRect;
143 entry.rect = newRect;
144 }
145 }
146 }
147 mPixelAllocated = mPixelUsedByVDs;
148 context->flush();
149 mSurface = newSurface;
150 mConsecutiveFailures = 0;
151 }
152
requestNewEntry(int width,int height,GrContext * context)153 AtlasEntry VectorDrawableAtlas::requestNewEntry(int width, int height, GrContext* context) {
154 AtlasEntry result;
155 if (width <= 0 || height <= 0) {
156 return result;
157 }
158
159 if (mSurface) {
160 const size_t area = width*height;
161
162 // Use a rectanizer to allocate unused space from the atlas surface.
163 bool notTooBig = fitInAtlas(width, height);
164 SkIPoint16 pos;
165 if (notTooBig && mRectanizer->addRect(width, height, &pos)) {
166 mPixelUsedByVDs += area;
167 mPixelAllocated += area;
168 result.rect = SkRect::MakeXYWH(pos.fX, pos.fY, width, height);
169 result.surface = mSurface;
170 auto eraseIt = mRects.emplace(mRects.end(), result.rect, result.rect, nullptr);
171 CacheEntry* entry = &(*eraseIt);
172 entry->eraseIt = eraseIt;
173 result.key = reinterpret_cast<AtlasKey>(entry);
174 mConsecutiveFailures = 0;
175 return result;
176 }
177
178 // Try to reuse atlas memory from rectangles freed by "releaseEntry".
179 auto freeRectIt = mFreeRects.lower_bound(area);
180 while (freeRectIt != mFreeRects.end()) {
181 SkRect& freeRect = freeRectIt->second;
182 if (freeRect.width() >= width && freeRect.height() >= height) {
183 result.rect = SkRect::MakeXYWH(freeRect.fLeft, freeRect.fTop, width, height);
184 result.surface = mSurface;
185 auto eraseIt = mRects.emplace(mRects.end(), result.rect, freeRect, nullptr);
186 CacheEntry* entry = &(*eraseIt);
187 entry->eraseIt = eraseIt;
188 result.key = reinterpret_cast<AtlasKey>(entry);
189 mPixelUsedByVDs += area;
190 mFreeRects.erase(freeRectIt);
191 mConsecutiveFailures = 0;
192 return result;
193 }
194 freeRectIt++;
195 }
196
197 if (notTooBig && mConsecutiveFailures <= MAX_CONSECUTIVE_FAILURES) {
198 mConsecutiveFailures++;
199 }
200 }
201
202 // Allocate a surface for a rectangle that is too big or if atlas is full.
203 if (nullptr != context) {
204 result.rect = SkRect::MakeWH(width, height);
205 result.surface = createSurface(width, height, context);
206 auto eraseIt = mRects.emplace(mRects.end(), result.rect, result.rect, result.surface);
207 CacheEntry* entry = &(*eraseIt);
208 entry->eraseIt = eraseIt;
209 result.key = reinterpret_cast<AtlasKey>(entry);
210 }
211
212 return result;
213 }
214
getEntry(AtlasKey atlasKey)215 AtlasEntry VectorDrawableAtlas::getEntry(AtlasKey atlasKey) {
216 AtlasEntry result;
217 if (INVALID_ATLAS_KEY != atlasKey) {
218 CacheEntry* entry = reinterpret_cast<CacheEntry*>(atlasKey);
219 result.rect = entry->VDrect;
220 result.surface = entry->surface;
221 if (!result.surface) {
222 result.surface = mSurface;
223 }
224 result.key = atlasKey;
225 }
226 return result;
227 }
228
releaseEntry(AtlasKey atlasKey)229 void VectorDrawableAtlas::releaseEntry(AtlasKey atlasKey) {
230 if (INVALID_ATLAS_KEY != atlasKey) {
231 CacheEntry* entry = reinterpret_cast<CacheEntry*>(atlasKey);
232 if (!entry->surface) {
233 // Store freed atlas rectangles in "mFreeRects" and try to reuse them later, when atlas
234 // is full.
235 SkRect& removedRect = entry->rect;
236 size_t rectArea = removedRect.width()*removedRect.height();
237 mFreeRects.emplace(rectArea, removedRect);
238 SkRect& removedVDRect = entry->VDrect;
239 size_t VDRectArea = removedVDRect.width()*removedVDRect.height();
240 mPixelUsedByVDs -= VDRectArea;
241 mConsecutiveFailures = 0;
242 }
243 auto eraseIt = entry->eraseIt;
244 mRects.erase(eraseIt);
245 }
246 }
247
createSurface(int width,int height,GrContext * context)248 sk_sp<SkSurface> VectorDrawableAtlas::createSurface(int width, int height, GrContext* context) {
249 #ifndef ANDROID_ENABLE_LINEAR_BLENDING
250 sk_sp<SkColorSpace> colorSpace = nullptr;
251 #else
252 sk_sp<SkColorSpace> colorSpace = SkColorSpace::MakeSRGB();
253 #endif
254 SkImageInfo info = SkImageInfo::MakeN32(width, height, kPremul_SkAlphaType, colorSpace);
255 return SkSurface::MakeRenderTarget(context, SkBudgeted::kYes, info);
256 }
257
setStorageMode(StorageMode mode)258 void VectorDrawableAtlas::setStorageMode(StorageMode mode) {
259 mStorageMode = mode;
260 if (StorageMode::disallowSharedSurface == mStorageMode && mSurface) {
261 mSurface.reset();
262 mRectanizer.reset();
263 mFreeRects.clear();
264 }
265 }
266
267 } /* namespace skiapipeline */
268 } /* namespace uirenderer */
269 } /* namespace android */
270