summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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