Unclipped savelayer support in new renderer

bug:22480459

Change-Id: I89dd5de8d7d008a1e298d227d767aabff5c96e27
diff --git a/libs/hwui/BakedOpDispatcher.cpp b/libs/hwui/BakedOpDispatcher.cpp
index f58ca31..195d235 100644
--- a/libs/hwui/BakedOpDispatcher.cpp
+++ b/libs/hwui/BakedOpDispatcher.cpp
@@ -736,6 +736,7 @@
 void BakedOpDispatcher::onLayerOp(BakedOpRenderer& renderer, const LayerOp& op, const BakedOpState& state) {
     OffscreenBuffer* buffer = *op.layerHandle;
 
+    // Note that we don't use op->paint here - it's never set on a LayerOp
     float layerAlpha = op.alpha * state.alpha;
     Glop glop;
     GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
@@ -754,11 +755,35 @@
 }
 
 void BakedOpDispatcher::onCopyToLayerOp(BakedOpRenderer& renderer, const CopyToLayerOp& op, const BakedOpState& state) {
-    LOG_ALWAYS_FATAL("TODO!");
+    LOG_ALWAYS_FATAL_IF(*(op.layerHandle) != nullptr, "layer already exists!");
+    *(op.layerHandle) = renderer.copyToLayer(state.computedState.clippedBounds);
+    LOG_ALWAYS_FATAL_IF(*op.layerHandle == nullptr, "layer copy failed");
 }
 
 void BakedOpDispatcher::onCopyFromLayerOp(BakedOpRenderer& renderer, const CopyFromLayerOp& op, const BakedOpState& state) {
-    LOG_ALWAYS_FATAL("TODO!");
+    LOG_ALWAYS_FATAL_IF(*op.layerHandle == nullptr, "no layer to draw underneath!");
+    if (!state.computedState.clippedBounds.isEmpty()) {
+        if (op.paint && op.paint->getAlpha() < 255) {
+            SkPaint layerPaint;
+            layerPaint.setAlpha(op.paint->getAlpha());
+            layerPaint.setXfermodeMode(SkXfermode::kDstIn_Mode);
+            layerPaint.setColorFilter(op.paint->getColorFilter());
+            RectOp rectOp(state.computedState.clippedBounds, Matrix4::identity(), nullptr, &layerPaint);
+            BakedOpDispatcher::onRectOp(renderer, rectOp, state);
+        }
+
+        OffscreenBuffer& layer = **(op.layerHandle);
+        auto mode = PaintUtils::getXfermodeDirect(op.paint);
+        Glop glop;
+        GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
+                .setRoundRectClipState(state.roundRectClipState)
+                .setMeshTexturedUvQuad(nullptr, layer.getTextureCoordinates())
+                .setFillLayer(layer.texture, nullptr, 1.0f, mode, Blend::ModeOrderSwap::Swap)
+                .setTransform(state.computedState.transform, TransformFlags::None)
+                .setModelViewMapUnitToRect(state.computedState.clippedBounds)
+                .build();
+        renderer.renderGlop(state, glop);
+    }
 }
 
 } // namespace uirenderer
diff --git a/libs/hwui/BakedOpRenderer.cpp b/libs/hwui/BakedOpRenderer.cpp
index 4aebe3c..b9c13e6 100644
--- a/libs/hwui/BakedOpRenderer.cpp
+++ b/libs/hwui/BakedOpRenderer.cpp
@@ -79,6 +79,20 @@
     mRenderTarget.frameBufferId = 0;
 }
 
+OffscreenBuffer* BakedOpRenderer::copyToLayer(const Rect& area) {
+    OffscreenBuffer* buffer = mRenderState.layerPool().get(mRenderState,
+            area.getWidth(), area.getHeight());
+    if (!area.isEmpty()) {
+        mCaches.textureState().activateTexture(0);
+        mCaches.textureState().bindTexture(buffer->texture.id);
+
+        glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0,
+                area.left, mRenderTarget.viewportHeight - area.bottom,
+                area.getWidth(), area.getHeight());
+    }
+    return buffer;
+}
+
 void BakedOpRenderer::startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) {
     LOG_ALWAYS_FATAL_IF(mRenderTarget.frameBufferId != 0, "primary framebufferId must be 0");
     mRenderState.bindFramebuffer(0);
diff --git a/libs/hwui/BakedOpRenderer.h b/libs/hwui/BakedOpRenderer.h
index 65e8b29..e857f6b 100644
--- a/libs/hwui/BakedOpRenderer.h
+++ b/libs/hwui/BakedOpRenderer.h
@@ -19,6 +19,7 @@
 
 #include "BakedOpState.h"
 #include "Matrix.h"
+#include "utils/Macros.h"
 
 namespace android {
 namespace uirenderer {
@@ -61,9 +62,10 @@
 
     void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect);
     void endFrame(const Rect& repaintRect);
-    OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height);
+    WARN_UNUSED_RESULT OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height);
     void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect);
     void endLayer();
+    WARN_UNUSED_RESULT OffscreenBuffer* copyToLayer(const Rect& area);
 
     Texture* getTexture(const SkBitmap* bitmap);
     const LightInfo& getLightInfo() const { return mLightInfo; }
diff --git a/libs/hwui/BakedOpState.cpp b/libs/hwui/BakedOpState.cpp
index b7c9281..f1cc846 100644
--- a/libs/hwui/BakedOpState.cpp
+++ b/libs/hwui/BakedOpState.cpp
@@ -75,9 +75,9 @@
     clipSideFlags = OpClipSideFlags::Full;
 }
 
-ResolvedRenderState::ResolvedRenderState(const Rect& dstRect)
+ResolvedRenderState::ResolvedRenderState(const ClipRect* viewportRect, const Rect& dstRect)
         : transform(Matrix4::identity())
-        , clipState(nullptr)
+        , clipState(viewportRect)
         , clippedBounds(dstRect)
         , clipSideFlags(OpClipSideFlags::None) {}
 
diff --git a/libs/hwui/BakedOpState.h b/libs/hwui/BakedOpState.h
index 70b0484..5c7b43f 100644
--- a/libs/hwui/BakedOpState.h
+++ b/libs/hwui/BakedOpState.h
@@ -58,9 +58,8 @@
     // Constructor for unbounded ops without transform/clip (namely shadows)
     ResolvedRenderState(LinearAllocator& allocator, Snapshot& snapshot);
 
-    // Constructor for primitive ops without clip or transform
-    // NOTE: these ops can't be queried for RT clip / local clip
-    ResolvedRenderState(const Rect& dstRect);
+    // Constructor for primitive ops provided clip, and no transform
+    ResolvedRenderState(const ClipRect* viewportRect, const Rect& dstRect);
 
     Rect computeLocalSpaceClip() const {
         Matrix4 inverse;
@@ -71,15 +70,13 @@
         return outClip;
     }
 
-    // NOTE: Can only be used on clipped/snapshot based ops
     const Rect& clipRect() const {
         return clipState->rect;
     }
 
-    // NOTE: Can only be used on clipped/snapshot based ops
     bool requiresClip() const {
         return clipSideFlags != OpClipSideFlags::None
-                || CC_UNLIKELY(clipState->mode != ClipMode::Rectangle);
+               || CC_UNLIKELY(clipState->mode != ClipMode::Rectangle);
     }
 
     // returns the clip if it's needed to draw the operation, otherwise nullptr
@@ -144,8 +141,8 @@
     }
 
     static BakedOpState* directConstruct(LinearAllocator& allocator,
-            const Rect& dstRect, const RecordedOp& recordedOp) {
-        return new (allocator) BakedOpState(dstRect, recordedOp);
+            const ClipRect* clip, const Rect& dstRect, const RecordedOp& recordedOp) {
+        return new (allocator) BakedOpState(clip, dstRect, recordedOp);
     }
 
     static void* operator new(size_t size, LinearAllocator& allocator) {
@@ -177,8 +174,8 @@
             , projectionPathMask(snapshot.projectionPathMask)
             , op(shadowOpPtr) {}
 
-    BakedOpState(const Rect& dstRect, const RecordedOp& recordedOp)
-            : computedState(dstRect)
+    BakedOpState(const ClipRect* viewportRect, const Rect& dstRect, const RecordedOp& recordedOp)
+            : computedState(viewportRect, dstRect)
             , alpha(1.0f)
             , roundRectClipState(nullptr)
             , projectionPathMask(nullptr)
diff --git a/libs/hwui/OpReorderer.cpp b/libs/hwui/OpReorderer.cpp
index 34c3d60..cff4f3c 100644
--- a/libs/hwui/OpReorderer.cpp
+++ b/libs/hwui/OpReorderer.cpp
@@ -31,7 +31,6 @@
 namespace uirenderer {
 
 class BatchBase {
-
 public:
     BatchBase(batchid_t batchId, BakedOpState* op, bool merging)
             : mBatchId(batchId)
@@ -213,7 +212,8 @@
         , repaintRect(repaintRect)
         , offscreenBuffer(renderNode ? renderNode->getLayer() : nullptr)
         , beginLayerOp(beginLayerOp)
-        , renderNode(renderNode) {}
+        , renderNode(renderNode)
+        , viewportClip(Rect(width, height)) {}
 
 // iterate back toward target to see if anything drawn since should overlap the new op
 // if no target, merging ops still iterate to find similar batch to insert after
@@ -270,7 +270,8 @@
         SimpleRectsOp* op = new (allocator) SimpleRectsOp(bounds,
                 Matrix4::identity(), nullptr, paint,
                 verts, vertCount);
-        BakedOpState* bakedState = BakedOpState::directConstruct(allocator, bounds, *op);
+        BakedOpState* bakedState = BakedOpState::directConstruct(allocator,
+                &viewportClip, bounds, *op);
 
 
         deferUnmergeableOp(allocator, bakedState, OpBatchType::Vertices);
@@ -1030,13 +1031,14 @@
     dstRect.doIntersect(mCanvasState.currentSnapshot()->getRenderTargetClip());
 
     // Allocate a holding position for the layer object (copyTo will produce, copyFrom will consume)
-    OffscreenBuffer** layerHandle = mAllocator.create<OffscreenBuffer*>();
+    OffscreenBuffer** layerHandle = mAllocator.create<OffscreenBuffer*>(nullptr);
 
     /**
      * First, defer an operation to copy out the content from the rendertarget into a layer.
      */
     auto copyToOp = new (mAllocator) CopyToLayerOp(op, layerHandle);
-    BakedOpState* bakedState = BakedOpState::directConstruct(mAllocator, dstRect, *copyToOp);
+    BakedOpState* bakedState = BakedOpState::directConstruct(mAllocator,
+            &(currentLayer().viewportClip), dstRect, *copyToOp);
     currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::CopyToLayer);
 
     /**
@@ -1050,11 +1052,12 @@
      * a balanced EndUnclippedLayerOp is seen
      */
     auto copyFromOp = new (mAllocator) CopyFromLayerOp(op, layerHandle);
-    bakedState = BakedOpState::directConstruct(mAllocator, dstRect, *copyFromOp);
+    bakedState = BakedOpState::directConstruct(mAllocator,
+            &(currentLayer().viewportClip), dstRect, *copyFromOp);
     currentLayer().activeUnclippedSaveLayers.push_back(bakedState);
 }
 
-void OpReorderer::deferEndUnclippedLayerOp(const EndUnclippedLayerOp& op) {
+void OpReorderer::deferEndUnclippedLayerOp(const EndUnclippedLayerOp& /* ignored */) {
     LOG_ALWAYS_FATAL_IF(currentLayer().activeUnclippedSaveLayers.empty(), "no layer to end!");
 
     BakedOpState* copyFromLayerOp = currentLayer().activeUnclippedSaveLayers.back();
diff --git a/libs/hwui/OpReorderer.h b/libs/hwui/OpReorderer.h
index b824d02..8d9d90b 100644
--- a/libs/hwui/OpReorderer.h
+++ b/libs/hwui/OpReorderer.h
@@ -111,6 +111,7 @@
         OffscreenBuffer* offscreenBuffer;
         const BeginLayerOp* beginLayerOp;
         const RenderNode* renderNode;
+        const ClipRect viewportClip;
 
         // list of deferred CopyFromLayer ops, to be deferred upon encountering EndUnclippedLayerOps
         std::vector<BakedOpState*> activeUnclippedSaveLayers;
diff --git a/libs/hwui/renderstate/OffscreenBufferPool.cpp b/libs/hwui/renderstate/OffscreenBufferPool.cpp
index 6b44557..227b640 100644
--- a/libs/hwui/renderstate/OffscreenBufferPool.cpp
+++ b/libs/hwui/renderstate/OffscreenBufferPool.cpp
@@ -54,6 +54,12 @@
             GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
 }
 
+Rect OffscreenBuffer::getTextureCoordinates() {
+    const float texX = 1.0f / float(texture.width);
+    const float texY = 1.0f / float(texture.height);
+    return Rect(0, viewportHeight * texY, viewportWidth * texX, 0);
+}
+
 void OffscreenBuffer::updateMeshFromRegion() {
     // avoid T-junctions as they cause artifacts in between the resultant
     // geometry when complex transforms occur.
diff --git a/libs/hwui/renderstate/OffscreenBufferPool.h b/libs/hwui/renderstate/OffscreenBufferPool.h
index fac6c35..2d8d529 100644
--- a/libs/hwui/renderstate/OffscreenBufferPool.h
+++ b/libs/hwui/renderstate/OffscreenBufferPool.h
@@ -46,6 +46,8 @@
             uint32_t viewportWidth, uint32_t viewportHeight);
     ~OffscreenBuffer();
 
+    Rect getTextureCoordinates();
+
     // must be called prior to rendering, to construct/update vertex buffer
     void updateMeshFromRegion();
 
diff --git a/libs/hwui/tests/common/TestUtils.h b/libs/hwui/tests/common/TestUtils.h
index ac14fc8..edde31e 100644
--- a/libs/hwui/tests/common/TestUtils.h
+++ b/libs/hwui/tests/common/TestUtils.h
@@ -53,6 +53,13 @@
             && MathUtils::areEqual(a.right, b.right) \
             && MathUtils::areEqual(a.bottom, b.bottom));
 
+#define EXPECT_CLIP_RECT(expRect, clipStatePtr) \
+        EXPECT_NE(nullptr, (clipStatePtr)) << "Op is unclipped"; \
+        if ((clipStatePtr)->mode == ClipMode::Rectangle) { \
+            EXPECT_EQ((expRect), reinterpret_cast<const ClipRect*>(clipStatePtr)->rect); \
+        } else { \
+            ADD_FAILURE() << "ClipState not a rect"; \
+        }
 /**
  * Like gtest's TEST, but runs on the RenderThread, and 'renderThread' is passed, in top level scope
  * (for e.g. accessing its RenderState)
diff --git a/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp b/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp
index 78fcd8b..c899850 100644
--- a/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp
@@ -29,14 +29,27 @@
 public:
     sp<RenderNode> card;
     void createContent(int width, int height, TestCanvas& canvas) override {
-        canvas.drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode); // background
+        canvas.drawColor(Color::White, SkXfermode::kSrcOver_Mode); // background
 
-        card = TestUtils::createNode(0, 0, 200, 200,
+        card = TestUtils::createNode(0, 0, 400, 800,
                 [](RenderProperties& props, TestCanvas& canvas) {
-            canvas.saveLayerAlpha(0, 0, 200, 200, 128, SkCanvas::kClipToLayer_SaveFlag);
-            canvas.drawColor(0xFF00FF00, SkXfermode::kSrcOver_Mode); // outer, unclipped
-            canvas.saveLayerAlpha(50, 50, 150, 150, 128, SkCanvas::kClipToLayer_SaveFlag);
-            canvas.drawColor(0xFF0000FF, SkXfermode::kSrcOver_Mode); // inner, clipped
+            // nested clipped saveLayers
+            canvas.saveLayerAlpha(0, 0, 400, 400, 200, SkCanvas::kClipToLayer_SaveFlag);
+            canvas.drawColor(Color::Green_700, SkXfermode::kSrcOver_Mode);
+            canvas.clipRect(50, 50, 350, 350, SkRegion::kIntersect_Op);
+            canvas.saveLayerAlpha(100, 100, 300, 300, 128, SkCanvas::kClipToLayer_SaveFlag);
+            canvas.drawColor(Color::Blue_500, SkXfermode::kSrcOver_Mode);
+            canvas.restore();
+            canvas.restore();
+
+            // single unclipped saveLayer
+            canvas.save(SkCanvas::kMatrixClip_SaveFlag);
+            canvas.translate(0, 400);
+            canvas.saveLayerAlpha(100, 100, 300, 300, 128, SkCanvas::SaveFlags(0)); // unclipped
+            SkPaint paint;
+            paint.setAntiAlias(true);
+            paint.setColor(Color::Green_700);
+            canvas.drawCircle(200, 200, 200, paint);
             canvas.restore();
             canvas.restore();
         });
diff --git a/libs/hwui/tests/unit/OffscreenBufferPoolTests.cpp b/libs/hwui/tests/unit/OffscreenBufferPoolTests.cpp
index 2187654..e96e9ba 100644
--- a/libs/hwui/tests/unit/OffscreenBufferPoolTests.cpp
+++ b/libs/hwui/tests/unit/OffscreenBufferPoolTests.cpp
@@ -15,11 +15,11 @@
  */
 
 #include <gtest/gtest.h>
+#include <Rect.h>
 #include <renderstate/OffscreenBufferPool.h>
 
 #include <tests/common/TestUtils.h>
 
-using namespace android;
 using namespace android::uirenderer;
 
 TEST(OffscreenBuffer, computeIdealDimension) {
@@ -43,6 +43,18 @@
     });
 }
 
+TEST(OffscreenBuffer, getTextureCoordinates) {
+    TestUtils::runOnRenderThread([] (renderthread::RenderThread& thread) {
+        OffscreenBuffer layerAligned(thread.renderState(), Caches::getInstance(), 256u, 256u);
+        EXPECT_EQ(Rect(0, 1, 1, 0),
+                layerAligned.getTextureCoordinates());
+
+        OffscreenBuffer layerUnaligned(thread.renderState(), Caches::getInstance(), 200u, 225u);
+        EXPECT_EQ(Rect(0, 225.0f / 256.0f, 200.0f / 256.0f, 0),
+                layerUnaligned.getTextureCoordinates());
+    });
+}
+
 TEST(OffscreenBufferPool, construct) {
     TestUtils::runOnRenderThread([] (renderthread::RenderThread& thread) {
         OffscreenBufferPool pool;
@@ -51,7 +63,6 @@
         EXPECT_EQ((uint32_t) Properties::layerPoolSize, pool.getMaxSize())
                 << "pool must read size from Properties";
     });
-
 }
 
 TEST(OffscreenBufferPool, getPutClear) {
diff --git a/libs/hwui/tests/unit/OpReordererTests.cpp b/libs/hwui/tests/unit/OpReordererTests.cpp
index 0ed70a0..701e446 100644
--- a/libs/hwui/tests/unit/OpReordererTests.cpp
+++ b/libs/hwui/tests/unit/OpReordererTests.cpp
@@ -565,7 +565,7 @@
         void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override {
             EXPECT_EQ(0, mIndex++);
             EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds);
-            EXPECT_EQ(nullptr, state.computedState.clipState);
+            EXPECT_CLIP_RECT(Rect(200, 200), state.computedState.clipState);
             EXPECT_TRUE(state.computedState.transform.isIdentity());
         }
         void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override {
@@ -583,7 +583,7 @@
         void onCopyFromLayerOp(const CopyFromLayerOp& op, const BakedOpState& state) override {
             EXPECT_EQ(3, mIndex++);
             EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds);
-            EXPECT_EQ(nullptr, state.computedState.clipState);
+            EXPECT_CLIP_RECT(Rect(200, 200), state.computedState.clipState);
             EXPECT_TRUE(state.computedState.transform.isIdentity());
         }
     };
diff --git a/libs/hwui/tests/unit/RecordingCanvasTests.cpp b/libs/hwui/tests/unit/RecordingCanvasTests.cpp
index 795ac30..ff098c8 100644
--- a/libs/hwui/tests/unit/RecordingCanvasTests.cpp
+++ b/libs/hwui/tests/unit/RecordingCanvasTests.cpp
@@ -33,14 +33,6 @@
     }
 }
 
-#define EXPECT_CLIP_RECT(expRect, clipStatePtr) \
-    EXPECT_NE(nullptr, (clipStatePtr)) << "Op is unclipped"; \
-    if ((clipStatePtr)->mode == ClipMode::Rectangle) { \
-        EXPECT_EQ((expRect), reinterpret_cast<const ClipRect*>(clipStatePtr)->rect); \
-    } else { \
-        ADD_FAILURE() << "ClipState not a rect"; \
-    }
-
 TEST(RecordingCanvas, emptyPlayback) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) {
         canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);