• 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 #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