summaryrefslogtreecommitdiff
path: root/libs/hwui/VectorDrawable.cpp
diff options
context:
space:
mode:
authorStan Iliev <stani@google.com>2017-03-23 16:56:51 -0400
committerStan Iliev <stani@google.com>2017-07-18 19:00:15 -0400
commit3310fb1b24c6cdd0781674fab69e359caf9d17e5 (patch)
treef795813741f9e97aad102e4d1b777e52e36beee7 /libs/hwui/VectorDrawable.cpp
parenta554ba6e2ebc320e9227a0302c8079c0f2cb9e85 (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.cpp109
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) {