summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author John Reck <jreck@google.com> 2019-10-04 14:48:27 -0700
committer John Reck <jreck@google.com> 2019-10-04 14:49:20 -0700
commit83161dcd6aa15c7da161b4ae561b06d20edd2510 (patch)
treed564507b70019ef9b3905fbd3256f7c2d51948cd
parent781630e4b4d67e0371c133ae64923ede1b989250 (diff)
Delete VectorDrawableAtlas
Poking around in a few apps it doesn't appear that the VectorDrawableAtlas is achieving sufficient utilization to justify its existence. The potential for draw call merging doesn't seem warranted for the RAM cost of the atlas. Bug: 137853925 Test: builds Change-Id: Id2419bc6dccb6316636d50c568f8fac75a2d563f
-rw-r--r--libs/hwui/Android.bp2
-rw-r--r--libs/hwui/VectorDrawable.cpp120
-rw-r--r--libs/hwui/VectorDrawable.h33
-rw-r--r--libs/hwui/pipeline/skia/SkiaDisplayList.cpp5
-rw-r--r--libs/hwui/pipeline/skia/SkiaPipeline.cpp23
-rw-r--r--libs/hwui/pipeline/skia/SkiaPipeline.h13
-rw-r--r--libs/hwui/pipeline/skia/VectorDrawableAtlas.cpp283
-rw-r--r--libs/hwui/pipeline/skia/VectorDrawableAtlas.h212
-rw-r--r--libs/hwui/renderthread/CacheManager.cpp20
-rw-r--r--libs/hwui/renderthread/CacheManager.h11
-rw-r--r--libs/hwui/renderthread/CanvasContext.cpp1
-rw-r--r--libs/hwui/renderthread/IRenderPipeline.h1
-rw-r--r--libs/hwui/renderthread/RenderProxy.cpp22
-rw-r--r--libs/hwui/renderthread/RenderProxy.h4
-rw-r--r--libs/hwui/tests/unit/SkiaPipelineTests.cpp33
-rw-r--r--libs/hwui/tests/unit/VectorDrawableAtlasTests.cpp163
16 files changed, 7 insertions, 939 deletions
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index ae90f117d448..1bcef7314bbf 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -219,7 +219,6 @@ cc_defaults {
"pipeline/skia/SkiaPipeline.cpp",
"pipeline/skia/SkiaProfileRenderer.cpp",
"pipeline/skia/SkiaVulkanPipeline.cpp",
- "pipeline/skia/VectorDrawableAtlas.cpp",
"pipeline/skia/VkFunctorDrawable.cpp",
"pipeline/skia/VkInteropFunctorDrawable.cpp",
"renderstate/RenderState.cpp",
@@ -345,7 +344,6 @@ cc_test {
"tests/unit/ThreadBaseTests.cpp",
"tests/unit/TypefaceTests.cpp",
"tests/unit/VectorDrawableTests.cpp",
- "tests/unit/VectorDrawableAtlasTests.cpp",
"tests/unit/WebViewFunctorManagerTests.cpp",
],
}
diff --git a/libs/hwui/VectorDrawable.cpp b/libs/hwui/VectorDrawable.cpp
index 61403aa93c97..217b0c4a8b98 100644
--- a/libs/hwui/VectorDrawable.cpp
+++ b/libs/hwui/VectorDrawable.cpp
@@ -496,87 +496,6 @@ Bitmap& Tree::getBitmapUpdateIfDirty() {
return *mCache.bitmap;
}
-void Tree::updateCache(sp<skiapipeline::VectorDrawableAtlas>& atlas, GrContext* context) {
-#ifdef __ANDROID__ // Layoutlib does not support hardware acceleration
- SkRect dst;
- sk_sp<SkSurface> surface = mCache.getSurface(&dst);
- bool canReuseSurface = surface && dst.width() >= mProperties.getScaledWidth() &&
- dst.height() >= mProperties.getScaledHeight();
- if (!canReuseSurface) {
- int scaledWidth = SkScalarCeilToInt(mProperties.getScaledWidth());
- int scaledHeight = SkScalarCeilToInt(mProperties.getScaledHeight());
- auto atlasEntry = atlas->requestNewEntry(scaledWidth, scaledHeight, context);
- if (INVALID_ATLAS_KEY != atlasEntry.key) {
- dst = atlasEntry.rect;
- surface = atlasEntry.surface;
- mCache.setAtlas(atlas, atlasEntry.key);
- } else {
- // don't draw, if we failed to allocate an offscreen buffer
- mCache.clear();
- surface.reset();
- }
- }
- if (!canReuseSurface || mCache.dirty) {
- if (surface) {
- Bitmap& bitmap = getBitmapUpdateIfDirty();
- SkBitmap skiaBitmap;
- bitmap.getSkBitmap(&skiaBitmap);
- surface->writePixels(skiaBitmap, dst.fLeft, dst.fTop);
- }
- mCache.dirty = false;
- }
-#endif
-}
-
-void Tree::Cache::setAtlas(sp<skiapipeline::VectorDrawableAtlas> newAtlas,
- skiapipeline::AtlasKey newAtlasKey) {
- LOG_ALWAYS_FATAL_IF(newAtlasKey == INVALID_ATLAS_KEY);
- clear();
- mAtlas = newAtlas;
- mAtlasKey = newAtlasKey;
-}
-
-sk_sp<SkSurface> Tree::Cache::getSurface(SkRect* bounds) {
- sk_sp<SkSurface> surface;
-#ifdef __ANDROID__ // Layoutlib does not support hardware acceleration
- sp<skiapipeline::VectorDrawableAtlas> atlas = mAtlas.promote();
- if (atlas.get() && mAtlasKey != INVALID_ATLAS_KEY) {
- auto atlasEntry = atlas->getEntry(mAtlasKey);
- *bounds = atlasEntry.rect;
- surface = atlasEntry.surface;
- mAtlasKey = atlasEntry.key;
- }
-#endif
-
- return surface;
-}
-
-void Tree::Cache::clear() {
-#ifdef __ANDROID__ // Layoutlib does not support hardware acceleration
- if (mAtlasKey != INVALID_ATLAS_KEY) {
- if (renderthread::RenderThread::isCurrent()) {
- sp<skiapipeline::VectorDrawableAtlas> lockAtlas = mAtlas.promote();
- if (lockAtlas.get()) {
- lockAtlas->releaseEntry(mAtlasKey);
- }
- } else {
- // VectorDrawableAtlas can be accessed only on RenderThread.
- // Use by-copy capture of the current Cache variables, because "this" may not be valid
- // by the time the lambda is evaluated on RenderThread.
- renderthread::RenderThread::getInstance().queue().post(
- [atlas = mAtlas, atlasKey = mAtlasKey]() {
- sp<skiapipeline::VectorDrawableAtlas> lockAtlas = atlas.promote();
- if (lockAtlas.get()) {
- lockAtlas->releaseEntry(atlasKey);
- }
- });
- }
- mAtlasKey = INVALID_ATLAS_KEY;
- }
- mAtlas = nullptr;
-#endif
-}
-
void Tree::draw(SkCanvas* canvas, const SkRect& bounds, const SkPaint& inPaint) {
if (canvas->quickReject(bounds)) {
// The RenderNode is on screen, but the AVD is not.
@@ -587,39 +506,14 @@ void Tree::draw(SkCanvas* canvas, const SkRect& bounds, const SkPaint& inPaint)
SkPaint paint = inPaint;
paint.setAlpha(mProperties.getRootAlpha() * 255);
- if (canvas->getGrContext() == nullptr) {
- // Recording to picture, don't use the SkSurface which won't work off of renderthread.
- Bitmap& bitmap = getBitmapUpdateIfDirty();
- SkBitmap skiaBitmap;
- bitmap.getSkBitmap(&skiaBitmap);
-
- int scaledWidth = SkScalarCeilToInt(mProperties.getScaledWidth());
- int scaledHeight = SkScalarCeilToInt(mProperties.getScaledHeight());
- canvas->drawBitmapRect(skiaBitmap, SkRect::MakeWH(scaledWidth, scaledHeight), bounds,
- &paint, SkCanvas::kFast_SrcRectConstraint);
- return;
- }
+ Bitmap& bitmap = getBitmapUpdateIfDirty();
+ SkBitmap skiaBitmap;
+ bitmap.getSkBitmap(&skiaBitmap);
- SkRect src;
- sk_sp<SkSurface> vdSurface = mCache.getSurface(&src);
- if (vdSurface) {
- canvas->drawImageRect(vdSurface->makeImageSnapshot().get(), src, bounds, &paint,
- SkCanvas::kFast_SrcRectConstraint);
- } else {
- // Handle the case when VectorDrawableAtlas has been destroyed, because of memory pressure.
- // We render the VD into a temporary standalone buffer and mark the frame as dirty. Next
- // frame will be cached into the atlas.
- Bitmap& bitmap = getBitmapUpdateIfDirty();
- SkBitmap skiaBitmap;
- bitmap.getSkBitmap(&skiaBitmap);
-
- int scaledWidth = SkScalarCeilToInt(mProperties.getScaledWidth());
- int scaledHeight = SkScalarCeilToInt(mProperties.getScaledHeight());
- canvas->drawBitmapRect(skiaBitmap, SkRect::MakeWH(scaledWidth, scaledHeight), bounds,
- &paint, SkCanvas::kFast_SrcRectConstraint);
- mCache.clear();
- markDirty();
- }
+ int scaledWidth = SkScalarCeilToInt(mProperties.getScaledWidth());
+ int scaledHeight = SkScalarCeilToInt(mProperties.getScaledHeight());
+ canvas->drawBitmapRect(skiaBitmap, SkRect::MakeWH(scaledWidth, scaledHeight), bounds,
+ &paint, SkCanvas::kFast_SrcRectConstraint);
}
void Tree::updateBitmapCache(Bitmap& bitmap, bool useStagingData) {
diff --git a/libs/hwui/VectorDrawable.h b/libs/hwui/VectorDrawable.h
index 9c0bb161380c..e1b6f2adde74 100644
--- a/libs/hwui/VectorDrawable.h
+++ b/libs/hwui/VectorDrawable.h
@@ -651,46 +651,13 @@ public:
void getPaintFor(SkPaint* outPaint, const TreeProperties &props) const;
BitmapPalette computePalette();
- /**
- * Draws VD into a GPU backed surface.
- * This should always be called from RT and it works with Skia pipeline only.
- */
- void updateCache(sp<skiapipeline::VectorDrawableAtlas>& atlas, GrContext* context);
-
void setAntiAlias(bool aa) { mRootNode->setAntiAlias(aa); }
private:
class Cache {
public:
sk_sp<Bitmap> bitmap; // used by HWUI pipeline and software
- // TODO: use surface instead of bitmap when drawing in software canvas
bool dirty = true;
-
- // the rest of the code in Cache is used by Skia pipelines only
-
- ~Cache() { clear(); }
-
- /**
- * Stores a weak pointer to the atlas and a key.
- */
- void setAtlas(sp<skiapipeline::VectorDrawableAtlas> atlas,
- skiapipeline::AtlasKey newAtlasKey);
-
- /**
- * Gets a surface and bounds from the atlas.
- *
- * @return nullptr if the altas has been deleted.
- */
- sk_sp<SkSurface> getSurface(SkRect* bounds);
-
- /**
- * Releases atlas key from the atlas, which makes it available for reuse.
- */
- void clear();
-
- private:
- wp<skiapipeline::VectorDrawableAtlas> mAtlas;
- skiapipeline::AtlasKey mAtlasKey = INVALID_ATLAS_KEY;
};
bool allocateBitmapIfNeeded(Cache& cache, int width, int height);
diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
index d7076d4cf424..158c3493a90c 100644
--- a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
+++ b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
@@ -150,12 +150,7 @@ bool SkiaDisplayList::prepareListAndChildren(
const SkRect& bounds = vectorDrawable->properties().getBounds();
if (intersects(info.screenSize, totalMatrix, bounds)) {
isDirty = true;
-#ifdef __ANDROID__ // Layoutlib does not support CanvasContext
- static_cast<SkiaPipeline*>(info.canvasContext.getRenderPipeline())
- ->getVectorDrawables()
- ->push_back(vectorDrawable);
vectorDrawable->setPropertyChangeWillBeConsumed(true);
-#endif
}
}
}
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
index 3010206cdc5b..87ef7fc9a6e1 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
@@ -43,7 +43,6 @@ namespace uirenderer {
namespace skiapipeline {
SkiaPipeline::SkiaPipeline(RenderThread& thread) : mRenderThread(thread) {
- mVectorDrawables.reserve(30);
}
SkiaPipeline::~SkiaPipeline() {
@@ -73,18 +72,11 @@ void SkiaPipeline::unpinImages() {
mPinnedImages.clear();
}
-void SkiaPipeline::onPrepareTree() {
- // The only time mVectorDrawables is not empty is if prepare tree was called 2 times without
- // a renderFrame in the middle.
- mVectorDrawables.clear();
-}
-
void SkiaPipeline::renderLayers(const LightGeometry& lightGeometry,
LayerUpdateQueue* layerUpdateQueue, bool opaque,
const LightInfo& lightInfo) {
LightingInfo::updateLighting(lightGeometry, lightInfo);
ATRACE_NAME("draw layers");
- renderVectorDrawableCache();
renderLayersImpl(*layerUpdateQueue, opaque);
layerUpdateQueue->clear();
}
@@ -213,19 +205,6 @@ void SkiaPipeline::prepareToDraw(const RenderThread& thread, Bitmap* bitmap) {
}
}
-void SkiaPipeline::renderVectorDrawableCache() {
- if (!mVectorDrawables.empty()) {
- sp<VectorDrawableAtlas> atlas = mRenderThread.cacheManager().acquireVectorDrawableAtlas();
- auto grContext = mRenderThread.getGrContext();
- atlas->prepareForDraw(grContext);
- ATRACE_NAME("Update VectorDrawables");
- for (auto vd : mVectorDrawables) {
- vd->updateCache(atlas, grContext);
- }
- mVectorDrawables.clear();
- }
-}
-
static void savePictureAsync(const sk_sp<SkData>& data, const std::string& filename) {
CommonPool::post([data, filename] {
if (0 == access(filename.c_str(), F_OK)) {
@@ -380,8 +359,6 @@ void SkiaPipeline::renderFrame(const LayerUpdateQueue& layers, const SkRect& cli
Properties::skpCaptureEnabled = true;
}
- renderVectorDrawableCache();
-
// draw all layers up front
renderLayersImpl(layers, opaque);
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.h b/libs/hwui/pipeline/skia/SkiaPipeline.h
index 37b559f92f05..215ff36ebb2b 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.h
@@ -41,7 +41,6 @@ public:
bool pinImages(std::vector<SkImage*>& mutableImages) override;
bool pinImages(LsaVector<sk_sp<Bitmap>>& images) override { return false; }
void unpinImages() override;
- void onPrepareTree() override;
void renderLayers(const LightGeometry& lightGeometry, LayerUpdateQueue* layerUpdateQueue,
bool opaque, const LightInfo& lightInfo) override;
@@ -57,8 +56,6 @@ public:
const Rect& contentDrawBounds, sk_sp<SkSurface> surface,
const SkMatrix& preTransform);
- std::vector<VectorDrawableRoot*>* getVectorDrawables() { return &mVectorDrawables; }
-
static void prepareToDraw(const renderthread::RenderThread& thread, Bitmap* bitmap);
void renderLayersImpl(const LayerUpdateQueue& layers, bool opaque);
@@ -93,11 +90,6 @@ private:
const std::vector<sp<RenderNode>>& nodes, const Rect& contentDrawBounds,
sk_sp<SkSurface> surface, const SkMatrix& preTransform);
- /**
- * Render mVectorDrawables into offscreen buffers.
- */
- void renderVectorDrawableCache();
-
// Called every frame. Normally returns early with screen canvas.
// But when capture is enabled, returns an nwaycanvas where commands are also recorded.
SkCanvas* tryCapture(SkSurface* surface);
@@ -113,11 +105,6 @@ private:
std::vector<sk_sp<SkImage>> mPinnedImages;
- /**
- * populated by prepareTree with dirty VDs
- */
- std::vector<VectorDrawableRoot*> mVectorDrawables;
-
// Block of properties used only for debugging to record a SkPicture and save it in a file.
// There are three possible ways of recording drawing commands.
enum class CaptureMode {
diff --git a/libs/hwui/pipeline/skia/VectorDrawableAtlas.cpp b/libs/hwui/pipeline/skia/VectorDrawableAtlas.cpp
deleted file mode 100644
index e783f389feb8..000000000000
--- a/libs/hwui/pipeline/skia/VectorDrawableAtlas.cpp
+++ /dev/null
@@ -1,283 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "VectorDrawableAtlas.h"
-
-#include <GrRectanizer_pow2.h>
-#include <SkCanvas.h>
-#include <cmath>
-#include "renderthread/RenderProxy.h"
-#include "renderthread/RenderThread.h"
-#include "utils/TraceUtils.h"
-
-namespace android {
-namespace uirenderer {
-namespace skiapipeline {
-
-VectorDrawableAtlas::VectorDrawableAtlas(size_t surfaceArea, StorageMode storageMode)
- : mWidth((int)std::sqrt(surfaceArea))
- , mHeight((int)std::sqrt(surfaceArea))
- , mStorageMode(storageMode) {}
-
-void VectorDrawableAtlas::prepareForDraw(GrContext* context) {
- if (StorageMode::allowSharedSurface == mStorageMode) {
- if (!mSurface) {
- mSurface = createSurface(mWidth, mHeight, context);
- mRectanizer = std::make_unique<GrRectanizerPow2>(mWidth, mHeight);
- mPixelUsedByVDs = 0;
- mPixelAllocated = 0;
- mConsecutiveFailures = 0;
- mFreeRects.clear();
- } else {
- if (isFragmented()) {
- // Invoke repack outside renderFrame to avoid jank.
- renderthread::RenderProxy::repackVectorDrawableAtlas();
- }
- }
- }
-}
-
-#define MAX_CONSECUTIVE_FAILURES 5
-#define MAX_UNUSED_RATIO 2.0f
-
-bool VectorDrawableAtlas::isFragmented() {
- return mConsecutiveFailures > MAX_CONSECUTIVE_FAILURES &&
- mPixelUsedByVDs * MAX_UNUSED_RATIO < mPixelAllocated;
-}
-
-void VectorDrawableAtlas::repackIfNeeded(GrContext* context) {
- // We repackage when atlas failed to allocate space MAX_CONSECUTIVE_FAILURES consecutive
- // times and the atlas allocated pixels are at least MAX_UNUSED_RATIO times higher than pixels
- // used by atlas VDs.
- if (isFragmented() && mSurface) {
- repack(context);
- }
-}
-
-// compare to CacheEntry objects based on VD area.
-bool VectorDrawableAtlas::compareCacheEntry(const CacheEntry& first, const CacheEntry& second) {
- return first.VDrect.width() * first.VDrect.height() <
- second.VDrect.width() * second.VDrect.height();
-}
-
-void VectorDrawableAtlas::repack(GrContext* context) {
- ATRACE_CALL();
- sk_sp<SkSurface> newSurface;
- SkCanvas* canvas = nullptr;
- if (StorageMode::allowSharedSurface == mStorageMode) {
- newSurface = createSurface(mWidth, mHeight, context);
- if (!newSurface) {
- return;
- }
- canvas = newSurface->getCanvas();
- canvas->clear(SK_ColorTRANSPARENT);
- mRectanizer = std::make_unique<GrRectanizerPow2>(mWidth, mHeight);
- } else {
- if (!mSurface) {
- return; // nothing to repack
- }
- mRectanizer.reset();
- }
- mFreeRects.clear();
- SkImage* sourceImageAtlas = nullptr;
- if (mSurface) {
- sourceImageAtlas = mSurface->makeImageSnapshot().get();
- }
-
- // Sort the list by VD size, which allows for the smallest VDs to get first in the atlas.
- // Sorting is safe, because it does not affect iterator validity.
- if (mRects.size() <= 100) {
- mRects.sort(compareCacheEntry);
- }
-
- for (CacheEntry& entry : mRects) {
- SkRect currentVDRect = entry.VDrect;
- SkImage* sourceImage; // copy either from the atlas or from a standalone surface
- if (entry.surface) {
- if (!fitInAtlas(currentVDRect.width(), currentVDRect.height())) {
- continue; // don't even try to repack huge VD
- }
- sourceImage = entry.surface->makeImageSnapshot().get();
- } else {
- sourceImage = sourceImageAtlas;
- }
- size_t VDRectArea = currentVDRect.width() * currentVDRect.height();
- SkIPoint16 pos;
- if (canvas && mRectanizer->addRect(currentVDRect.width(), currentVDRect.height(), &pos)) {
- SkRect newRect =
- SkRect::MakeXYWH(pos.fX, pos.fY, currentVDRect.width(), currentVDRect.height());
- canvas->drawImageRect(sourceImage, currentVDRect, newRect, nullptr);
- entry.VDrect = newRect;
- entry.rect = newRect;
- if (entry.surface) {
- // A rectangle moved from a standalone surface to the atlas.
- entry.surface = nullptr;
- mPixelUsedByVDs += VDRectArea;
- }
- } else {
- // Repack failed for this item. If it is not already, store it in a standalone
- // surface.
- if (!entry.surface) {
- // A rectangle moved from an atlas to a standalone surface.
- mPixelUsedByVDs -= VDRectArea;
- SkRect newRect = SkRect::MakeWH(currentVDRect.width(), currentVDRect.height());
- entry.surface = createSurface(newRect.width(), newRect.height(), context);
- auto tempCanvas = entry.surface->getCanvas();
- tempCanvas->clear(SK_ColorTRANSPARENT);
- tempCanvas->drawImageRect(sourceImageAtlas, currentVDRect, newRect, nullptr);
- entry.VDrect = newRect;
- entry.rect = newRect;
- }
- }
- }
- mPixelAllocated = mPixelUsedByVDs;
- context->flush();
- mSurface = newSurface;
- mConsecutiveFailures = 0;
-}
-
-AtlasEntry VectorDrawableAtlas::requestNewEntry(int width, int height, GrContext* context) {
- AtlasEntry result;
- if (width <= 0 || height <= 0) {
- return result;
- }
-
- if (mSurface) {
- const size_t area = width * height;
-
- // Use a rectanizer to allocate unused space from the atlas surface.
- bool notTooBig = fitInAtlas(width, height);
- SkIPoint16 pos;
- if (notTooBig && mRectanizer->addRect(width, height, &pos)) {
- mPixelUsedByVDs += area;
- mPixelAllocated += area;
- result.rect = SkRect::MakeXYWH(pos.fX, pos.fY, width, height);
- result.surface = mSurface;
- auto eraseIt = mRects.emplace(mRects.end(), result.rect, result.rect, nullptr);
- CacheEntry* entry = &(*eraseIt);
- entry->eraseIt = eraseIt;
- result.key = reinterpret_cast<AtlasKey>(entry);
- mConsecutiveFailures = 0;
- return result;
- }
-
- // Try to reuse atlas memory from rectangles freed by "releaseEntry".
- auto freeRectIt = mFreeRects.lower_bound(area);
- while (freeRectIt != mFreeRects.end()) {
- SkRect& freeRect = freeRectIt->second;
- if (freeRect.width() >= width && freeRect.height() >= height) {
- result.rect = SkRect::MakeXYWH(freeRect.fLeft, freeRect.fTop, width, height);
- result.surface = mSurface;
- auto eraseIt = mRects.emplace(mRects.end(), result.rect, freeRect, nullptr);
- CacheEntry* entry = &(*eraseIt);
- entry->eraseIt = eraseIt;
- result.key = reinterpret_cast<AtlasKey>(entry);
- mPixelUsedByVDs += area;
- mFreeRects.erase(freeRectIt);
- mConsecutiveFailures = 0;
- return result;
- }
- freeRectIt++;
- }
-
- if (notTooBig && mConsecutiveFailures <= MAX_CONSECUTIVE_FAILURES) {
- mConsecutiveFailures++;
- }
- }
-
- // Allocate a surface for a rectangle that is too big or if atlas is full.
- if (nullptr != context) {
- result.rect = SkRect::MakeWH(width, height);
- result.surface = createSurface(width, height, context);
- auto eraseIt = mRects.emplace(mRects.end(), result.rect, result.rect, result.surface);
- CacheEntry* entry = &(*eraseIt);
- entry->eraseIt = eraseIt;
- result.key = reinterpret_cast<AtlasKey>(entry);
- }
-
- return result;
-}
-
-AtlasEntry VectorDrawableAtlas::getEntry(AtlasKey atlasKey) {
- AtlasEntry result;
- if (INVALID_ATLAS_KEY != atlasKey) {
- CacheEntry* entry = reinterpret_cast<CacheEntry*>(atlasKey);
- result.rect = entry->VDrect;
- result.surface = entry->surface;
- if (!result.surface) {
- result.surface = mSurface;
- }
- result.key = atlasKey;
- }
- return result;
-}
-
-void VectorDrawableAtlas::releaseEntry(AtlasKey atlasKey) {
- if (INVALID_ATLAS_KEY != atlasKey) {
- if (!renderthread::RenderThread::isCurrent()) {
- {
- AutoMutex _lock(mReleaseKeyLock);
- mKeysForRelease.push_back(atlasKey);
- }
- // invoke releaseEntry on the renderthread
- renderthread::RenderProxy::releaseVDAtlasEntries();
- return;
- }
- CacheEntry* entry = reinterpret_cast<CacheEntry*>(atlasKey);
- if (!entry->surface) {
- // Store freed atlas rectangles in "mFreeRects" and try to reuse them later, when atlas
- // is full.
- SkRect& removedRect = entry->rect;
- size_t rectArea = removedRect.width() * removedRect.height();
- mFreeRects.emplace(rectArea, removedRect);
- SkRect& removedVDRect = entry->VDrect;
- size_t VDRectArea = removedVDRect.width() * removedVDRect.height();
- mPixelUsedByVDs -= VDRectArea;
- mConsecutiveFailures = 0;
- }
- auto eraseIt = entry->eraseIt;
- mRects.erase(eraseIt);
- }
-}
-
-void VectorDrawableAtlas::delayedReleaseEntries() {
- AutoMutex _lock(mReleaseKeyLock);
- for (auto key : mKeysForRelease) {
- releaseEntry(key);
- }
- mKeysForRelease.clear();
-}
-
-sk_sp<SkSurface> VectorDrawableAtlas::createSurface(int width, int height, GrContext* context) {
- SkImageInfo info = SkImageInfo::MakeN32(width, height, kPremul_SkAlphaType);
- // This must have a top-left origin so that calls to surface->canvas->writePixels
- // performs a basic texture upload instead of a more complex drawing operation
- return SkSurface::MakeRenderTarget(context, SkBudgeted::kYes, info, 0, kTopLeft_GrSurfaceOrigin,
- nullptr);
-}
-
-void VectorDrawableAtlas::setStorageMode(StorageMode mode) {
- mStorageMode = mode;
- if (StorageMode::disallowSharedSurface == mStorageMode && mSurface) {
- mSurface.reset();
- mRectanizer.reset();
- mFreeRects.clear();
- }
-}
-
-} /* namespace skiapipeline */
-} /* namespace uirenderer */
-} /* namespace android */
diff --git a/libs/hwui/pipeline/skia/VectorDrawableAtlas.h b/libs/hwui/pipeline/skia/VectorDrawableAtlas.h
deleted file mode 100644
index 5e892aa7e92c..000000000000
--- a/libs/hwui/pipeline/skia/VectorDrawableAtlas.h
+++ /dev/null
@@ -1,212 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <SkSurface.h>
-#include <utils/FatVector.h>
-#include <utils/RefBase.h>
-#include <utils/Thread.h>
-#include <list>
-#include <map>
-
-class GrRectanizer;
-
-namespace android {
-namespace uirenderer {
-namespace skiapipeline {
-
-typedef uintptr_t AtlasKey;
-
-#define INVALID_ATLAS_KEY 0
-
-struct AtlasEntry {
- sk_sp<SkSurface> surface;
- SkRect rect;
- AtlasKey key = INVALID_ATLAS_KEY;
-};
-
-/**
- * VectorDrawableAtlas provides offscreen buffers used to draw VD and AnimatedVD.
- * VectorDrawableAtlas can allocate a standalone surface or provide a subrect from a shared surface.
- * VectorDrawableAtlas is owned by the CacheManager and weak pointers are kept by each
- * VectorDrawable that is using it. VectorDrawableAtlas and its surface can be deleted at any time,
- * except during a renderFrame call. VectorDrawable does not contain a pointer to atlas SkSurface
- * nor any coordinates into the atlas, but instead holds a rectangle "id", which is resolved only
- * when drawing. This design makes VectorDrawableAtlas free to move the data internally.
- * At draw time a VectorDrawable may find, that its atlas has been deleted, which will make it
- * draw in a standalone cache surface not part of an atlas. In this case VD won't use
- * VectorDrawableAtlas until the next frame.
- * VectorDrawableAtlas tries to fit VDs in the atlas SkSurface. If there is not enough space in
- * the atlas, VectorDrawableAtlas creates a standalone surface for each VD.
- * When a VectorDrawable is deleted, it invokes VectorDrawableAtlas::releaseEntry, which is keeping
- * track of free spaces and allow to reuse the surface for another VD.
- */
-// TODO: Check if not using atlas for AnimatedVD is more efficient.
-// TODO: For low memory situations, when there are no paint effects in VD, we may render without an
-// TODO: offscreen surface.
-class VectorDrawableAtlas : public virtual RefBase {
-public:
- enum class StorageMode { allowSharedSurface, disallowSharedSurface };
-
- explicit VectorDrawableAtlas(size_t surfaceArea,
- StorageMode storageMode = StorageMode::allowSharedSurface);
-
- /**
- * "prepareForDraw" may allocate a new surface if needed. It may schedule to repack the
- * atlas at a later time.
- */
- void prepareForDraw(GrContext* context);
-
- /**
- * Repack the atlas if needed, by moving used rectangles into a new atlas surface.
- * The goal of repacking is to fix a fragmented atlas.
- */
- void repackIfNeeded(GrContext* context);
-
- /**
- * Returns true if atlas is fragmented and repack is needed.
- */
- bool isFragmented();
-
- /**
- * "requestNewEntry" is called by VectorDrawable to allocate a new rectangle area from the atlas
- * or create a standalone surface if atlas is full.
- * On success it returns a non-negative unique id, which can be used later with "getEntry" and
- * "releaseEntry".
- */
- AtlasEntry requestNewEntry(int width, int height, GrContext* context);
-
- /**
- * "getEntry" extracts coordinates and surface of a previously created rectangle.
- * "atlasKey" is an unique id created by "requestNewEntry". Passing a non-existing "atlasKey" is
- * causing an undefined behaviour.
- * On success it returns a rectangle Id -> may be same or different from "atlasKey" if
- * implementation decides to move the record internally.
- */
- AtlasEntry getEntry(AtlasKey atlasKey);
-
- /**
- * "releaseEntry" is invoked when a VectorDrawable is deleted. Passing a non-existing "atlasKey"
- * is causing an undefined behaviour. This is the only function in the class that can be
- * invoked from any thread. It will marshal internally to render thread if needed.
- */
- void releaseEntry(AtlasKey atlasKey);
-
- void setStorageMode(StorageMode mode);
-
- /**
- * "delayedReleaseEntries" is indirectly invoked by "releaseEntry", when "releaseEntry" is
- * invoked from a non render thread.
- */
- void delayedReleaseEntries();
-
-private:
- struct CacheEntry {
- CacheEntry(const SkRect& newVDrect, const SkRect& newRect,
- const sk_sp<SkSurface>& newSurface)
- : VDrect(newVDrect), rect(newRect), surface(newSurface) {}
-
- /**
- * size and position of VectorDrawable into the atlas or in "this.surface"
- */
- SkRect VDrect;
-
- /**
- * rect allocated in atlas surface or "this.surface". It may be bigger than "VDrect"
- */
- SkRect rect;
-
- /**
- * this surface is used if atlas is full or VD is too big
- */
- sk_sp<SkSurface> surface;
-
- /**
- * iterator is used to delete self with a constant complexity (without traversing the list)
- */
- std::list<CacheEntry>::iterator eraseIt;
- };
-
- /**
- * atlas surface shared by all VDs
- */
- sk_sp<SkSurface> mSurface;
-
- std::unique_ptr<GrRectanizer> mRectanizer;
- const int mWidth;
- const int mHeight;
-
- /**
- * "mRects" keeps records only for rectangles used by VDs. List has nice properties: constant
- * complexity to insert and erase and references are not invalidated by insert/erase.
- */
- std::list<CacheEntry> mRects;
-
- /**
- * Rectangles freed by "releaseEntry" are removed from "mRects" and added to "mFreeRects".
- * "mFreeRects" is using for an index the rectangle area. There could be more than one free
- * rectangle with the same area, which is the reason to use "multimap" instead of "map".
- */
- std::multimap<size_t, SkRect> mFreeRects;
-
- /**
- * area in atlas used by VectorDrawables (area in standalone surface not counted)
- */
- int mPixelUsedByVDs = 0;
-
- /**
- * area allocated in mRectanizer
- */
- int mPixelAllocated = 0;
-
- /**
- * Consecutive times we had to allocate standalone surfaces, because atlas was full.
- */
- int mConsecutiveFailures = 0;
-
- /**
- * mStorageMode allows using a shared surface to store small vector drawables.
- * Using a shared surface can boost the performance by allowing GL ops to be batched, but may
- * consume more memory.
- */
- StorageMode mStorageMode;
-
- /**
- * mKeysForRelease is used by releaseEntry implementation to pass atlas keys from an arbitrary
- * calling thread to the render thread.
- */
- std::vector<AtlasKey> mKeysForRelease;
-
- /**
- * A lock used to protect access to mKeysForRelease.
- */
- Mutex mReleaseKeyLock;
-
- sk_sp<SkSurface> createSurface(int width, int height, GrContext* context);
-
- inline bool fitInAtlas(int width, int height) {
- return 2 * width < mWidth && 2 * height < mHeight;
- }
-
- void repack(GrContext* context);
-
- static bool compareCacheEntry(const CacheEntry& first, const CacheEntry& second);
-};
-
-} /* namespace skiapipeline */
-} /* namespace uirenderer */
-} /* namespace android */
diff --git a/libs/hwui/renderthread/CacheManager.cpp b/libs/hwui/renderthread/CacheManager.cpp
index fc268138e071..ab9b1e20a80d 100644
--- a/libs/hwui/renderthread/CacheManager.cpp
+++ b/libs/hwui/renderthread/CacheManager.cpp
@@ -56,10 +56,6 @@ CacheManager::CacheManager(const DisplayInfo& display)
, mBackgroundCpuFontCacheBytes(mMaxCpuFontCacheBytes * BACKGROUND_RETENTION_PERCENTAGE) {
SkGraphics::SetFontCacheLimit(mMaxCpuFontCacheBytes);
-
- mVectorDrawableAtlas = new skiapipeline::VectorDrawableAtlas(
- mMaxSurfaceArea / 2,
- skiapipeline::VectorDrawableAtlas::StorageMode::disallowSharedSurface);
}
void CacheManager::reset(sk_sp<GrContext> context) {
@@ -76,9 +72,6 @@ void CacheManager::reset(sk_sp<GrContext> context) {
void CacheManager::destroy() {
// cleanup any caches here as the GrContext is about to go away...
mGrContext.reset(nullptr);
- mVectorDrawableAtlas = new skiapipeline::VectorDrawableAtlas(
- mMaxSurfaceArea / 2,
- skiapipeline::VectorDrawableAtlas::StorageMode::disallowSharedSurface);
}
class CommonPoolExecutor : public SkExecutor {
@@ -109,7 +102,6 @@ void CacheManager::trimMemory(TrimMemoryMode mode) {
switch (mode) {
case TrimMemoryMode::Complete:
- mVectorDrawableAtlas = new skiapipeline::VectorDrawableAtlas(mMaxSurfaceArea / 2);
mGrContext->freeGpuResources();
SkGraphics::PurgeAllCaches();
break;
@@ -138,16 +130,6 @@ void CacheManager::trimStaleResources() {
mGrContext->purgeResourcesNotUsedInMs(std::chrono::seconds(30));
}
-sp<skiapipeline::VectorDrawableAtlas> CacheManager::acquireVectorDrawableAtlas() {
- LOG_ALWAYS_FATAL_IF(mVectorDrawableAtlas.get() == nullptr);
- LOG_ALWAYS_FATAL_IF(mGrContext == nullptr);
-
- /**
- * TODO: define memory conditions where we clear the cache (e.g. surface->reset())
- */
- return mVectorDrawableAtlas;
-}
-
void CacheManager::dumpMemoryUsage(String8& log, const RenderState* renderState) {
if (!mGrContext) {
log.appendFormat("No valid cache instance.\n");
@@ -176,8 +158,6 @@ void CacheManager::dumpMemoryUsage(String8& log, const RenderState* renderState)
log.appendFormat("Other Caches:\n");
log.appendFormat(" Current / Maximum\n");
- log.appendFormat(" VectorDrawableAtlas %6.2f kB / %6.2f KB (entries = %zu)\n", 0.0f, 0.0f,
- (size_t)0);
if (renderState) {
if (renderState->mActiveLayers.size() > 0) {
diff --git a/libs/hwui/renderthread/CacheManager.h b/libs/hwui/renderthread/CacheManager.h
index d7977cc4566d..857710babf0c 100644
--- a/libs/hwui/renderthread/CacheManager.h
+++ b/libs/hwui/renderthread/CacheManager.h
@@ -25,8 +25,6 @@
#include <utils/String8.h>
#include <vector>
-#include "pipeline/skia/VectorDrawableAtlas.h"
-
namespace android {
class Surface;
@@ -51,8 +49,6 @@ public:
void trimStaleResources();
void dumpMemoryUsage(String8& log, const RenderState* renderState = nullptr);
- sp<skiapipeline::VectorDrawableAtlas> acquireVectorDrawableAtlas();
-
size_t getCacheSize() const { return mMaxResourceBytes; }
size_t getBackgroundCacheSize() const { return mBackgroundResourceBytes; }
@@ -77,13 +73,6 @@ private:
const size_t mMaxGpuFontAtlasBytes;
const size_t mMaxCpuFontCacheBytes;
const size_t mBackgroundCpuFontCacheBytes;
-
- struct PipelineProps {
- const void* pipelineKey = nullptr;
- size_t surfaceArea = 0;
- };
-
- sp<skiapipeline::VectorDrawableAtlas> mVectorDrawableAtlas;
};
} /* namespace renderthread */
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 30cc007d454b..b41bdf6a174f 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -306,7 +306,6 @@ void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo, int64_t sy
info.out.canDrawThisFrame = true;
mAnimationContext->startFrame(info.mode);
- mRenderPipeline->onPrepareTree();
for (const sp<RenderNode>& node : mRenderNodes) {
// Only the primary target node will be drawn full - all other nodes would get drawn in
// real time mode. In case of a window, the primary node is the window content and the other
diff --git a/libs/hwui/renderthread/IRenderPipeline.h b/libs/hwui/renderthread/IRenderPipeline.h
index 3b81014c05e2..ef0aa98d4220 100644
--- a/libs/hwui/renderthread/IRenderPipeline.h
+++ b/libs/hwui/renderthread/IRenderPipeline.h
@@ -80,7 +80,6 @@ public:
virtual bool pinImages(std::vector<SkImage*>& mutableImages) = 0;
virtual bool pinImages(LsaVector<sk_sp<Bitmap>>& images) = 0;
virtual void unpinImages() = 0;
- virtual void onPrepareTree() = 0;
virtual SkColorType getSurfaceColorType() const = 0;
virtual sk_sp<SkColorSpace> getSurfaceColorSpace() = 0;
virtual GrSurfaceOrigin getSurfaceOrigin() = 0;
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index 40fbdffaf90a..4f7ad7b69f57 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -24,7 +24,6 @@
#include "Readback.h"
#include "Rect.h"
#include "WebViewFunctorManager.h"
-#include "pipeline/skia/VectorDrawableAtlas.h"
#include "renderthread/CanvasContext.h"
#include "renderthread/RenderTask.h"
#include "renderthread/RenderThread.h"
@@ -365,27 +364,6 @@ void RenderProxy::disableVsync() {
Properties::disableVsync = true;
}
-void RenderProxy::repackVectorDrawableAtlas() {
- RenderThread& thread = RenderThread::getInstance();
- thread.queue().post([&thread]() {
- // The context may be null if trimMemory executed, but then the atlas was deleted too.
- if (thread.getGrContext() != nullptr) {
- thread.cacheManager().acquireVectorDrawableAtlas()->repackIfNeeded(
- thread.getGrContext());
- }
- });
-}
-
-void RenderProxy::releaseVDAtlasEntries() {
- RenderThread& thread = RenderThread::getInstance();
- thread.queue().post([&thread]() {
- // The context may be null if trimMemory executed, but then the atlas was deleted too.
- if (thread.getGrContext() != nullptr) {
- thread.cacheManager().acquireVectorDrawableAtlas()->delayedReleaseEntries();
- }
- });
-}
-
void RenderProxy::preload() {
// Create RenderThread object and start the thread. Then preload Vulkan/EGL driver.
auto& thread = RenderThread::getInstance();
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index c3eb6ede9bf2..e6fe1d4864da 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -150,10 +150,6 @@ public:
ANDROID_API static void preload();
- static void repackVectorDrawableAtlas();
-
- static void releaseVDAtlasEntries();
-
private:
RenderThread& mRenderThread;
CanvasContext* mContext;
diff --git a/libs/hwui/tests/unit/SkiaPipelineTests.cpp b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
index 958baa78deab..307d13606cb8 100644
--- a/libs/hwui/tests/unit/SkiaPipelineTests.cpp
+++ b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
@@ -61,39 +61,6 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderFrame) {
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorRED);
}
-RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, testOnPrepareTree) {
- auto redNode = TestUtils::createSkiaNode(
- 0, 0, 1, 1, [](RenderProperties& props, SkiaRecordingCanvas& redCanvas) {
- redCanvas.drawColor(SK_ColorRED, SkBlendMode::kSrcOver);
- });
-
- LayerUpdateQueue layerUpdateQueue;
- SkRect dirty = SkRectMakeLargest();
- std::vector<sp<RenderNode>> renderNodes;
- renderNodes.push_back(redNode);
- bool opaque = true;
- android::uirenderer::Rect contentDrawBounds(0, 0, 1, 1);
- auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread);
- {
- // add a pointer to a deleted vector drawable object in the pipeline
- sp<VectorDrawableRoot> dirtyVD(new VectorDrawableRoot(new VectorDrawable::Group()));
- dirtyVD->mutateProperties()->setScaledSize(5, 5);
- pipeline->getVectorDrawables()->push_back(dirtyVD.get());
- }
-
- // pipeline should clean list of dirty vector drawables before prepare tree
- pipeline->onPrepareTree();
-
- auto surface = SkSurface::MakeRasterN32Premul(1, 1);
- surface->getCanvas()->drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver);
- ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
-
- // drawFrame will crash if "SkiaPipeline::onPrepareTree" did not clean invalid VD pointer
- pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface,
- SkMatrix::I());
- ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorRED);
-}
-
RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderFrameCheckOpaque) {
auto halfGreenNode = TestUtils::createSkiaNode(
0, 0, 2, 2, [](RenderProperties& props, SkiaRecordingCanvas& bottomHalfGreenCanvas) {
diff --git a/libs/hwui/tests/unit/VectorDrawableAtlasTests.cpp b/libs/hwui/tests/unit/VectorDrawableAtlasTests.cpp
deleted file mode 100644
index 0c95fdd42851..000000000000
--- a/libs/hwui/tests/unit/VectorDrawableAtlasTests.cpp
+++ /dev/null
@@ -1,163 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <gtest/gtest.h>
-
-#include <GrRectanizer.h>
-#include "pipeline/skia/VectorDrawableAtlas.h"
-#include "tests/common/TestUtils.h"
-
-using namespace android;
-using namespace android::uirenderer;
-using namespace android::uirenderer::renderthread;
-using namespace android::uirenderer::skiapipeline;
-
-RENDERTHREAD_SKIA_PIPELINE_TEST(VectorDrawableAtlas, addGetRemove) {
- VectorDrawableAtlas atlas(100 * 100);
- atlas.prepareForDraw(renderThread.getGrContext());
- // create 150 rects 10x10, which won't fit in the atlas (atlas can fit no more than 100 rects)
- const int MAX_RECTS = 150;
- AtlasEntry VDRects[MAX_RECTS];
-
- sk_sp<SkSurface> atlasSurface;
-
- // check we are able to allocate new rects
- // check that rects in the atlas do not intersect
- for (uint32_t i = 0; i < MAX_RECTS; i++) {
- VDRects[i] = atlas.requestNewEntry(10, 10, renderThread.getGrContext());
- if (0 == i) {
- atlasSurface = VDRects[0].surface;
- }
- ASSERT_TRUE(VDRects[i].key != INVALID_ATLAS_KEY);
- ASSERT_TRUE(VDRects[i].surface.get() != nullptr);
- ASSERT_TRUE(VDRects[i].rect.width() == 10 && VDRects[i].rect.height() == 10);
-
- // nothing in the atlas should intersect
- if (atlasSurface.get() == VDRects[i].surface.get()) {
- for (uint32_t j = 0; j < i; j++) {
- if (atlasSurface.get() == VDRects[j].surface.get()) {
- ASSERT_FALSE(VDRects[i].rect.intersect(VDRects[j].rect));
- }
- }
- }
- }
-
- // first 1/3 rects should all be in the same surface
- for (uint32_t i = 1; i < MAX_RECTS / 3; i++) {
- ASSERT_NE(VDRects[i].key, VDRects[0].key);
- ASSERT_EQ(VDRects[i].surface.get(), atlasSurface.get());
- }
-
- // first rect is using atlas and last is a standalone surface
- ASSERT_NE(VDRects[0].surface.get(), VDRects[MAX_RECTS - 1].surface.get());
-
- // check getEntry returns the same surfaces that we had created
- for (uint32_t i = 0; i < MAX_RECTS; i++) {
- auto VDRect = atlas.getEntry(VDRects[i].key);
- ASSERT_TRUE(VDRect.key != INVALID_ATLAS_KEY);
- ASSERT_EQ(VDRects[i].key, VDRect.key);
- ASSERT_EQ(VDRects[i].surface.get(), VDRect.surface.get());
- ASSERT_EQ(VDRects[i].rect, VDRect.rect);
- atlas.releaseEntry(VDRect.key);
- }
-
- // check that any new rects will be allocated in the atlas, even that rectanizer is full.
- // rects in the atlas should not intersect.
- for (uint32_t i = 0; i < MAX_RECTS / 3; i++) {
- VDRects[i] = atlas.requestNewEntry(10, 10, renderThread.getGrContext());
- ASSERT_TRUE(VDRects[i].key != INVALID_ATLAS_KEY);
- ASSERT_EQ(VDRects[i].surface.get(), atlasSurface.get());
- ASSERT_TRUE(VDRects[i].rect.width() == 10 && VDRects[i].rect.height() == 10);
- for (uint32_t j = 0; j < i; j++) {
- ASSERT_FALSE(VDRects[i].rect.intersect(VDRects[j].rect));
- }
- }
-}
-
-RENDERTHREAD_SKIA_PIPELINE_TEST(VectorDrawableAtlas, disallowSharedSurface) {
- VectorDrawableAtlas atlas(100 * 100);
- // don't allow to use a shared surface
- atlas.setStorageMode(VectorDrawableAtlas::StorageMode::disallowSharedSurface);
- atlas.prepareForDraw(renderThread.getGrContext());
- // create 150 rects 10x10, which won't fit in the atlas (atlas can fit no more than 100 rects)
- const int MAX_RECTS = 150;
- AtlasEntry VDRects[MAX_RECTS];
-
- // check we are able to allocate new rects
- // check that rects in the atlas use unique surfaces
- for (uint32_t i = 0; i < MAX_RECTS; i++) {
- VDRects[i] = atlas.requestNewEntry(10, 10, renderThread.getGrContext());
- ASSERT_TRUE(VDRects[i].key != INVALID_ATLAS_KEY);
- ASSERT_TRUE(VDRects[i].surface.get() != nullptr);
- ASSERT_TRUE(VDRects[i].rect.width() == 10 && VDRects[i].rect.height() == 10);
-
- // nothing in the atlas should use the same surface
- for (uint32_t j = 0; j < i; j++) {
- ASSERT_NE(VDRects[i].surface.get(), VDRects[j].surface.get());
- }
- }
-}
-
-RENDERTHREAD_SKIA_PIPELINE_TEST(VectorDrawableAtlas, repack) {
- VectorDrawableAtlas atlas(100 * 100);
- ASSERT_FALSE(atlas.isFragmented());
- atlas.prepareForDraw(renderThread.getGrContext());
- ASSERT_FALSE(atlas.isFragmented());
- // create 150 rects 10x10, which won't fit in the atlas (atlas can fit no more than 100 rects)
- const int MAX_RECTS = 150;
- AtlasEntry VDRects[MAX_RECTS];
-
- sk_sp<SkSurface> atlasSurface;
-
- // fill the atlas with check we are able to allocate new rects
- for (uint32_t i = 0; i < MAX_RECTS; i++) {
- VDRects[i] = atlas.requestNewEntry(10, 10, renderThread.getGrContext());
- if (0 == i) {
- atlasSurface = VDRects[0].surface;
- }
- ASSERT_TRUE(VDRects[i].key != INVALID_ATLAS_KEY);
- }
-
- ASSERT_FALSE(atlas.isFragmented());
-
- // first 1/3 rects should all be in the same surface
- for (uint32_t i = 1; i < MAX_RECTS / 3; i++) {
- ASSERT_NE(VDRects[i].key, VDRects[0].key);
- ASSERT_EQ(VDRects[i].surface.get(), atlasSurface.get());
- }
-
- // release all entries
- for (uint32_t i = 0; i < MAX_RECTS; i++) {
- auto VDRect = atlas.getEntry(VDRects[i].key);
- ASSERT_TRUE(VDRect.key != INVALID_ATLAS_KEY);
- atlas.releaseEntry(VDRect.key);
- }
-
- ASSERT_FALSE(atlas.isFragmented());
-
- // allocate 4x4 rects, which will fragment the atlas badly, because each entry occupies a 10x10
- // area
- for (uint32_t i = 0; i < 4 * MAX_RECTS; i++) {
- AtlasEntry entry = atlas.requestNewEntry(4, 4, renderThread.getGrContext());
- ASSERT_TRUE(entry.key != INVALID_ATLAS_KEY);
- }
-
- ASSERT_TRUE(atlas.isFragmented());
-
- atlas.repackIfNeeded(renderThread.getGrContext());
-
- ASSERT_FALSE(atlas.isFragmented());
-} \ No newline at end of file