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 30 VectorDrawableAtlas::VectorDrawableAtlas(size_t surfaceArea, StorageMode storageMode) 31 : mWidth((int)std::sqrt(surfaceArea)) 32 , mHeight((int)std::sqrt(surfaceArea)) 33 , mStorageMode(storageMode) {} 34 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 56 bool VectorDrawableAtlas::isFragmented() { 57 return mConsecutiveFailures > MAX_CONSECUTIVE_FAILURES && 58 mPixelUsedByVDs * MAX_UNUSED_RATIO < mPixelAllocated; 59 } 60 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. 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 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 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 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 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 256 void VectorDrawableAtlas::delayedReleaseEntries() { 257 AutoMutex _lock(mReleaseKeyLock); 258 for (auto key : mKeysForRelease) { 259 releaseEntry(key); 260 } 261 mKeysForRelease.clear(); 262 } 263 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 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