diff options
-rw-r--r-- | libs/hwui/Android.bp | 2 | ||||
-rw-r--r-- | libs/hwui/VectorDrawable.cpp | 120 | ||||
-rw-r--r-- | libs/hwui/VectorDrawable.h | 33 | ||||
-rw-r--r-- | libs/hwui/pipeline/skia/SkiaDisplayList.cpp | 5 | ||||
-rw-r--r-- | libs/hwui/pipeline/skia/SkiaPipeline.cpp | 23 | ||||
-rw-r--r-- | libs/hwui/pipeline/skia/SkiaPipeline.h | 13 | ||||
-rw-r--r-- | libs/hwui/pipeline/skia/VectorDrawableAtlas.cpp | 283 | ||||
-rw-r--r-- | libs/hwui/pipeline/skia/VectorDrawableAtlas.h | 212 | ||||
-rw-r--r-- | libs/hwui/renderthread/CacheManager.cpp | 20 | ||||
-rw-r--r-- | libs/hwui/renderthread/CacheManager.h | 11 | ||||
-rw-r--r-- | libs/hwui/renderthread/CanvasContext.cpp | 1 | ||||
-rw-r--r-- | libs/hwui/renderthread/IRenderPipeline.h | 1 | ||||
-rw-r--r-- | libs/hwui/renderthread/RenderProxy.cpp | 22 | ||||
-rw-r--r-- | libs/hwui/renderthread/RenderProxy.h | 4 | ||||
-rw-r--r-- | libs/hwui/tests/unit/SkiaPipelineTests.cpp | 33 | ||||
-rw-r--r-- | libs/hwui/tests/unit/VectorDrawableAtlasTests.cpp | 163 |
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 |