diff options
author | Stan Iliev <stani@google.com> | 2017-03-23 16:56:51 -0400 |
---|---|---|
committer | Stan Iliev <stani@google.com> | 2017-07-18 19:00:15 -0400 |
commit | 3310fb1b24c6cdd0781674fab69e359caf9d17e5 (patch) | |
tree | f795813741f9e97aad102e4d1b777e52e36beee7 /libs/hwui/VectorDrawable.cpp | |
parent | a554ba6e2ebc320e9227a0302c8079c0f2cb9e85 (diff) |
Cache VectorDrawables in an atlas
Optimize VectorDrawables for Skia pipeline: draw small VectorDrawables
in a GPU atlas instead of seprate offscreen buffers.
This implementation is using CacheManger and allows for the atlas to
be released if there is a memory pressure.
Test: A new unit test for VectorDrawableAtlas is passing. Systrace shows
0.5ms faster DrawFrame for fling in Settings app main screen.
Change-Id: Ide3884eefae777e1547f1dfdb67b807185839fb4
Diffstat (limited to 'libs/hwui/VectorDrawable.cpp')
-rw-r--r-- | libs/hwui/VectorDrawable.cpp | 109 |
1 files changed, 89 insertions, 20 deletions
diff --git a/libs/hwui/VectorDrawable.cpp b/libs/hwui/VectorDrawable.cpp index f6b2912a6254..8a1de02e6a0c 100644 --- a/libs/hwui/VectorDrawable.cpp +++ b/libs/hwui/VectorDrawable.cpp @@ -491,34 +491,103 @@ Bitmap& Tree::getBitmapUpdateIfDirty() { return *mCache.bitmap; } -void Tree::updateCache(sk_sp<SkSurface> surface) { - if (surface.get()) { - mCache.surface = surface; - } - if (surface.get() || mCache.dirty) { - SkSurface* vdSurface = mCache.surface.get(); - SkCanvas* canvas = vdSurface->getCanvas(); - float scaleX = vdSurface->width() / mProperties.getViewportWidth(); - float scaleY = vdSurface->height() / mProperties.getViewportHeight(); +void Tree::updateCache(sp<skiapipeline::VectorDrawableAtlas>& atlas, GrContext* context) { + 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) { + draw(surface.get(), dst); + mCache.dirty = false; + } +} + +void Tree::draw(SkSurface* surface, const SkRect& dst) { + if (surface) { + SkCanvas* canvas = surface->getCanvas(); + float scaleX = dst.width() / mProperties.getViewportWidth(); + float scaleY = dst.height() / mProperties.getViewportHeight(); SkAutoCanvasRestore acr(canvas, true); + canvas->translate(dst.fLeft, dst.fTop); + canvas->clipRect(SkRect::MakeWH(dst.width(), dst.height())); canvas->clear(SK_ColorTRANSPARENT); canvas->scale(scaleX, scaleY); mRootNode->draw(canvas, false); - mCache.dirty = false; - canvas->flush(); } } +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; + 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; + } + + return surface; +} + +void Tree::Cache::clear() { + sp<skiapipeline::VectorDrawableAtlas> lockAtlas = mAtlas.promote(); + if (lockAtlas.get()) { + lockAtlas->releaseEntry(mAtlasKey); + } + mAtlas = nullptr; + mAtlasKey = INVALID_ATLAS_KEY; +} + void Tree::draw(SkCanvas* canvas) { - /* - * TODO address the following... - * - * 1) figure out how to set path's as volatile during animation - * 2) if mRoot->getPaint() != null either promote to layer (during - * animation) or cache in SkSurface (for static content) - */ - canvas->drawImageRect(mCache.surface->makeImageSnapshot().get(), - mutateProperties()->getBounds(), getPaint()); + SkRect src; + sk_sp<SkSurface> vdSurface = mCache.getSurface(&src); + if (vdSurface) { + canvas->drawImageRect(vdSurface->makeImageSnapshot().get(), src, + mutateProperties()->getBounds(), getPaint()); + } 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. + int scaledWidth = SkScalarCeilToInt(mProperties.getScaledWidth()); + int scaledHeight = SkScalarCeilToInt(mProperties.getScaledHeight()); + SkRect src = SkRect::MakeWH(scaledWidth, scaledHeight); +#ifndef ANDROID_ENABLE_LINEAR_BLENDING + sk_sp<SkColorSpace> colorSpace = nullptr; +#else + sk_sp<SkColorSpace> colorSpace = SkColorSpace::MakeSRGB(); +#endif + SkImageInfo info = SkImageInfo::MakeN32(scaledWidth, scaledHeight, kPremul_SkAlphaType, + colorSpace); + sk_sp<SkSurface> surface = SkSurface::MakeRenderTarget(canvas->getGrContext(), + SkBudgeted::kYes, info); + draw(surface.get(), src); + mCache.clear(); + canvas->drawImageRect(surface->makeImageSnapshot().get(), mutateProperties()->getBounds(), + getPaint()); + markDirty(); + } } void Tree::updateBitmapCache(Bitmap& bitmap, bool useStagingData) { |