Update VectorDrawables cache at frame start

Draw VectorDrawables in GPU backed surface. Render VD cache
at the beginning of the frame to avoid context switching.

Test: CTS graphics tests pass.
Change-Id: Ia14e0ec4049c3fa87f03547fbda44043bf8dd793
diff --git a/libs/hwui/VectorDrawable.cpp b/libs/hwui/VectorDrawable.cpp
index 8823a92..f6b2912 100644
--- a/libs/hwui/VectorDrawable.cpp
+++ b/libs/hwui/VectorDrawable.cpp
@@ -491,6 +491,36 @@
     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();
+        SkAutoCanvasRestore acr(canvas, true);
+        canvas->clear(SK_ColorTRANSPARENT);
+        canvas->scale(scaleX, scaleY);
+        mRootNode->draw(canvas, false);
+        mCache.dirty = false;
+        canvas->flush();
+    }
+}
+
+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());
+}
+
 void Tree::updateBitmapCache(Bitmap& bitmap, bool useStagingData) {
     SkBitmap outCache;
     bitmap.getSkBitmap(&outCache);
diff --git a/libs/hwui/VectorDrawable.h b/libs/hwui/VectorDrawable.h
index 729a4dd..22cfe29 100644
--- a/libs/hwui/VectorDrawable.h
+++ b/libs/hwui/VectorDrawable.h
@@ -31,6 +31,7 @@
 #include <SkPathMeasure.h>
 #include <SkRect.h>
 #include <SkShader.h>
+#include <SkSurface.h>
 
 #include <cutils/compiler.h>
 #include <stddef.h>
@@ -677,15 +678,37 @@
     // This should only be called from animations on RT
     TreeProperties* mutateProperties() { return &mProperties; }
 
+    // called from RT only
+    const TreeProperties& properties() const { return mProperties; }
+
     // This should always be called from RT.
     void markDirty() { mCache.dirty = true; }
     bool isDirty() const { return mCache.dirty; }
     bool getPropertyChangeWillBeConsumed() const { return mWillBeConsumed; }
     void setPropertyChangeWillBeConsumed(bool willBeConsumed) { mWillBeConsumed = willBeConsumed; }
 
+    // Returns true if VD cache surface is big enough. This should always be called from RT and it
+    // works with Skia pipelines only.
+    bool canReuseSurface() {
+        SkSurface* surface = mCache.surface.get();
+        return surface && surface->width() >= mProperties.getScaledWidth()
+              && surface->height() >= mProperties.getScaledHeight();
+    }
+
+    // Draws VD cache into a canvas. This should always be called from RT and it works with Skia
+    // pipelines only.
+    void draw(SkCanvas* canvas);
+
+    // Draws VD into a GPU backed surface. If canReuseSurface returns false, then "surface" must
+    // contain a new surface. This should always be called from RT and it works with Skia pipelines
+    // only.
+    void updateCache(sk_sp<SkSurface> surface);
+
 private:
     struct Cache {
-        sk_sp<Bitmap> bitmap;
+        sk_sp<Bitmap> bitmap; //used by HWUI pipeline and software
+        //TODO: use surface instead of bitmap when drawing in software canvas
+        sk_sp<SkSurface> surface; //used only by Skia pipelines
         bool dirty = true;
     };
 
diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
index 496f7ba..3ddc09f 100644
--- a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
+++ b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
@@ -19,6 +19,7 @@
 #include "renderthread/CanvasContext.h"
 #include "VectorDrawable.h"
 #include "DumpOpsCanvas.h"
+#include "SkiaPipeline.h"
 
 #include <SkImagePriv.h>
 
@@ -92,6 +93,8 @@
         // If any vector drawable in the display list needs update, damage the node.
         if (vectorDrawable->isDirty()) {
             isDirty = true;
+            static_cast<SkiaPipeline*>(info.canvasContext.getRenderPipeline())
+                ->getVectorDrawables()->push_back(vectorDrawable);
         }
         vectorDrawable->setPropertyChangeWillBeConsumed(true);
     }
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
index 10c1865..75f1adc 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
@@ -25,6 +25,7 @@
 #include <SkPictureRecorder.h>
 #include <SkPixelSerializer.h>
 #include <SkStream.h>
+#include "VectorDrawable.h"
 
 #include <unistd.h>
 
@@ -40,7 +41,9 @@
 
 Vector3 SkiaPipeline::mLightCenter = {FLT_MIN, FLT_MIN, FLT_MIN};
 
-SkiaPipeline::SkiaPipeline(RenderThread& thread) :  mRenderThread(thread) { }
+SkiaPipeline::SkiaPipeline(RenderThread& thread) :  mRenderThread(thread) {
+    mVectorDrawables.reserve(30);
+}
 
 TaskManager* SkiaPipeline::getTaskManager() {
     return &mTaskManager;
@@ -74,6 +77,7 @@
         const BakedOpRenderer::LightInfo& lightInfo) {
     updateLighting(lightGeometry, lightInfo);
     ATRACE_NAME("draw layers");
+    renderVectorDrawableCache();
     renderLayersImpl(*layerUpdateQueue, opaque);
     layerUpdateQueue->clear();
 }
@@ -176,10 +180,35 @@
     }
 };
 
+void SkiaPipeline::renderVectorDrawableCache() {
+    //render VectorDrawables into offscreen buffers
+    for (auto vd : mVectorDrawables) {
+        sk_sp<SkSurface> surface;
+        if (!vd->canReuseSurface()) {
+#ifndef ANDROID_ENABLE_LINEAR_BLENDING
+            sk_sp<SkColorSpace> colorSpace = nullptr;
+#else
+            sk_sp<SkColorSpace> colorSpace = SkColorSpace::MakeSRGB();
+#endif
+            int scaledWidth = SkScalarCeilToInt(vd->properties().getScaledWidth());
+            int scaledHeight = SkScalarCeilToInt(vd->properties().getScaledHeight());
+            SkImageInfo info = SkImageInfo::MakeN32(scaledWidth, scaledHeight,
+                    kPremul_SkAlphaType, colorSpace);
+            SkASSERT(mRenderThread.getGrContext() != nullptr);
+            surface = SkSurface::MakeRenderTarget(mRenderThread.getGrContext(), SkBudgeted::kYes,
+                    info);
+        }
+        vd->updateCache(surface);
+    }
+    mVectorDrawables.clear();
+}
+
 void SkiaPipeline::renderFrame(const LayerUpdateQueue& layers, const SkRect& clip,
         const std::vector<sp<RenderNode>>& nodes, bool opaque, const Rect &contentDrawBounds,
         sk_sp<SkSurface> surface) {
 
+    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 c58fedf..6f5e719 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.h
@@ -49,6 +49,8 @@
             const std::vector< sp<RenderNode> >& nodes, bool opaque, const Rect &contentDrawBounds,
             sk_sp<SkSurface> surface);
 
+    std::vector<VectorDrawableRoot*>* getVectorDrawables() { return &mVectorDrawables; }
+
     static void destroyLayer(RenderNode* node);
 
     static void prepareToDraw(const renderthread::RenderThread& thread, Bitmap* bitmap);
@@ -119,8 +121,18 @@
             const std::vector< sp<RenderNode> >& nodes, const Rect &contentDrawBounds,
             sk_sp<SkSurface>);
 
+    /**
+     *  Render mVectorDrawables into offscreen buffers.
+     */
+    void renderVectorDrawableCache();
+
     TaskManager mTaskManager;
     std::vector<sk_sp<SkImage>> mPinnedImages;
+
+    /**
+     *  populated by prepareTree with dirty VDs
+     */
+    std::vector<VectorDrawableRoot*> mVectorDrawables;
     static float mLightRadius;
     static uint8_t mAmbientShadowAlpha;
     static uint8_t mSpotShadowAlpha;
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
index 559d268..27edc25 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
@@ -126,22 +126,7 @@
          return SkRect::MakeLargest();
      }
      virtual void onDraw(SkCanvas* canvas) override {
-         Bitmap& hwuiBitmap = mRoot->getBitmapUpdateIfDirty();
-         SkBitmap bitmap;
-         hwuiBitmap.getSkBitmap(&bitmap);
-         SkPaint* paint = mRoot->getPaint();
-         canvas->drawBitmapRect(bitmap, mRoot->mutateProperties()->getBounds(), paint);
-         /*
-          * TODO we can draw this directly but need to address the following...
-          *
-          * 1) Add drawDirect(SkCanvas*) to VectorDrawableRoot
-          * 2) fix VectorDrawable.cpp's Path::draw to not make a temporary path
-          *    so that we don't break caching
-          * 3) figure out how to set path's as volatile during animation
-          * 4) if mRoot->getPaint() != null either promote to layer (during
-          *    animation) or cache in SkSurface (for static content)
-          *
-          */
+         mRoot->draw(canvas);
      }
 
  private:
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 738c091..33eda96 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -194,6 +194,8 @@
 
     void waitOnFences();
 
+    IRenderPipeline* getRenderPipeline() { return mRenderPipeline.get(); }
+
 private:
     CanvasContext(RenderThread& thread, bool translucent, RenderNode* rootRenderNode,
             IContextFactory* contextFactory, std::unique_ptr<IRenderPipeline> renderPipeline);