blob: e783f389feb8b06c4ad5544c5098bd7c462196ba [file] [log] [blame]
Stan Iliev3310fb12017-03-23 16:56:51 -04001/*
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>
Stan Iliev3310fb12017-03-23 16:56:51 -040022#include "renderthread/RenderProxy.h"
Stan Iliev6b894d72017-08-23 12:41:41 -040023#include "renderthread/RenderThread.h"
John Reck1bcacfd2017-11-03 10:12:19 -070024#include "utils/TraceUtils.h"
Stan Iliev3310fb12017-03-23 16:56:51 -040025
26namespace android {
27namespace uirenderer {
28namespace skiapipeline {
29
30VectorDrawableAtlas::VectorDrawableAtlas(size_t surfaceArea, StorageMode storageMode)
31 : mWidth((int)std::sqrt(surfaceArea))
32 , mHeight((int)std::sqrt(surfaceArea))
John Reck1bcacfd2017-11-03 10:12:19 -070033 , mStorageMode(storageMode) {}
Stan Iliev3310fb12017-03-23 16:56:51 -040034
35void 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
56bool VectorDrawableAtlas::isFragmented() {
John Reck1bcacfd2017-11-03 10:12:19 -070057 return mConsecutiveFailures > MAX_CONSECUTIVE_FAILURES &&
58 mPixelUsedByVDs * MAX_UNUSED_RATIO < mPixelAllocated;
Stan Iliev3310fb12017-03-23 16:56:51 -040059}
60
61void 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.
John Reck1bcacfd2017-11-03 10:12:19 -070071bool VectorDrawableAtlas::compareCacheEntry(const CacheEntry& first, const CacheEntry& second) {
72 return first.VDrect.width() * first.VDrect.height() <
73 second.VDrect.width() * second.VDrect.height();
Stan Iliev3310fb12017-03-23 16:56:51 -040074}
75
76void 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) {
John Reck1bcacfd2017-11-03 10:12:19 -070090 return; // nothing to repack
Stan Iliev3310fb12017-03-23 16:56:51 -040091 }
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;
John Reck1bcacfd2017-11-03 10:12:19 -0700108 SkImage* sourceImage; // copy either from the atlas or from a standalone surface
Stan Iliev3310fb12017-03-23 16:56:51 -0400109 if (entry.surface) {
110 if (!fitInAtlas(currentVDRect.width(), currentVDRect.height())) {
John Reck1bcacfd2017-11-03 10:12:19 -0700111 continue; // don't even try to repack huge VD
Stan Iliev3310fb12017-03-23 16:56:51 -0400112 }
113 sourceImage = entry.surface->makeImageSnapshot().get();
114 } else {
115 sourceImage = sourceImageAtlas;
116 }
John Reck1bcacfd2017-11-03 10:12:19 -0700117 size_t VDRectArea = currentVDRect.width() * currentVDRect.height();
Stan Iliev3310fb12017-03-23 16:56:51 -0400118 SkIPoint16 pos;
119 if (canvas && mRectanizer->addRect(currentVDRect.width(), currentVDRect.height(), &pos)) {
John Reck1bcacfd2017-11-03 10:12:19 -0700120 SkRect newRect =
121 SkRect::MakeXYWH(pos.fX, pos.fY, currentVDRect.width(), currentVDRect.height());
Stan Iliev3310fb12017-03-23 16:56:51 -0400122 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;
John Reck1bcacfd2017-11-03 10:12:19 -0700136 SkRect newRect = SkRect::MakeWH(currentVDRect.width(), currentVDRect.height());
Stan Iliev3310fb12017-03-23 16:56:51 -0400137 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
152AtlasEntry 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) {
John Reck1bcacfd2017-11-03 10:12:19 -0700159 const size_t area = width * height;
Stan Iliev3310fb12017-03-23 16:56:51 -0400160
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
214AtlasEntry 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
228void VectorDrawableAtlas::releaseEntry(AtlasKey atlasKey) {
229 if (INVALID_ATLAS_KEY != atlasKey) {
Stan Iliev6b894d72017-08-23 12:41:41 -0400230 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 }
Stan Iliev3310fb12017-03-23 16:56:51 -0400239 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;
John Reck1bcacfd2017-11-03 10:12:19 -0700244 size_t rectArea = removedRect.width() * removedRect.height();
Stan Iliev3310fb12017-03-23 16:56:51 -0400245 mFreeRects.emplace(rectArea, removedRect);
246 SkRect& removedVDRect = entry->VDrect;
John Reck1bcacfd2017-11-03 10:12:19 -0700247 size_t VDRectArea = removedVDRect.width() * removedVDRect.height();
Stan Iliev3310fb12017-03-23 16:56:51 -0400248 mPixelUsedByVDs -= VDRectArea;
249 mConsecutiveFailures = 0;
250 }
251 auto eraseIt = entry->eraseIt;
252 mRects.erase(eraseIt);
253 }
254}
255
Stan Iliev6b894d72017-08-23 12:41:41 -0400256void VectorDrawableAtlas::delayedReleaseEntries() {
257 AutoMutex _lock(mReleaseKeyLock);
258 for (auto key : mKeysForRelease) {
259 releaseEntry(key);
260 }
261 mKeysForRelease.clear();
262}
263
Stan Iliev3310fb12017-03-23 16:56:51 -0400264sk_sp<SkSurface> VectorDrawableAtlas::createSurface(int width, int height, GrContext* context) {
Leon Scroggins III12497572019-01-31 10:06:12 -0500265 SkImageInfo info = SkImageInfo::MakeN32(width, height, kPremul_SkAlphaType);
Derek Sollenberger7fe53c12017-08-31 15:40:12 -0400266 // 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
John Reck1bcacfd2017-11-03 10:12:19 -0700268 return SkSurface::MakeRenderTarget(context, SkBudgeted::kYes, info, 0, kTopLeft_GrSurfaceOrigin,
269 nullptr);
Stan Iliev3310fb12017-03-23 16:56:51 -0400270}
271
272void 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 */