Merge "Consume TextureView matrix safely" into nyc-dev
diff --git a/libs/hwui/FrameBuilder.cpp b/libs/hwui/FrameBuilder.cpp
index 0ebb886..b1314fe 100644
--- a/libs/hwui/FrameBuilder.cpp
+++ b/libs/hwui/FrameBuilder.cpp
@@ -699,7 +699,17 @@
 
 void FrameBuilder::deferTextureLayerOp(const TextureLayerOp& op) {
     if (CC_UNLIKELY(!op.layer->isRenderable())) return;
-    BakedOpState* bakedState = tryBakeOpState(op);
+
+    const TextureLayerOp* textureLayerOp = &op;
+    // Now safe to access transform (which was potentially unready at record time)
+    if (!op.layer->getTransform().isIdentity()) {
+        // non-identity transform present, so 'inject it' into op by copying + replacing matrix
+        Matrix4 combinedMatrix(op.localMatrix);
+        combinedMatrix.multiply(op.layer->getTransform());
+        textureLayerOp = mAllocator.create<TextureLayerOp>(op, combinedMatrix);
+    }
+    BakedOpState* bakedState = tryBakeOpState(*textureLayerOp);
+
     if (!bakedState) return; // quick rejected
     currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::TextureLayer);
 }
diff --git a/libs/hwui/RecordedOp.h b/libs/hwui/RecordedOp.h
index 64c604a..0271a80 100644
--- a/libs/hwui/RecordedOp.h
+++ b/libs/hwui/RecordedOp.h
@@ -419,6 +419,14 @@
     TextureLayerOp(BASE_PARAMS_PAINTLESS, Layer* layer)
             : SUPER_PAINTLESS(TextureLayerOp)
             , layer(layer) {}
+
+    // Copy an existing TextureLayerOp, replacing the underlying matrix
+    TextureLayerOp(const TextureLayerOp& op, const Matrix4& replacementMatrix)
+            : RecordedOp(RecordedOpId::TextureLayerOp, op.unmappedBounds, replacementMatrix,
+                    op.localClip, op.paint)
+            , layer(op.layer) {
+
+    }
     Layer* layer;
 };
 
diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
index f43dade..4f9cd68 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -576,15 +576,9 @@
 
     // Note that the backing layer has *not* yet been updated, so don't trust
     // its width, height, transform, etc...!
-    Matrix4 totalTransform(*(mState.currentSnapshot()->transform));
-    if (layerHandle->getTransform()) {
-        Matrix4 layerTransform(*layerHandle->getTransform());
-        totalTransform.multiply(layerTransform);
-    }
-
     addOp(alloc().create_trivial<TextureLayerOp>(
             Rect(layerHandle->getWidth(), layerHandle->getHeight()),
-            totalTransform,
+            *(mState.currentSnapshot()->transform),
             getRecordedClip(),
             layerHandle->backingLayer()));
 }
diff --git a/libs/hwui/tests/common/TestUtils.cpp b/libs/hwui/tests/common/TestUtils.cpp
index 059e9ae..c762eed 100644
--- a/libs/hwui/tests/common/TestUtils.cpp
+++ b/libs/hwui/tests/common/TestUtils.cpp
@@ -44,6 +44,7 @@
         renderthread::RenderThread& renderThread, uint32_t width, uint32_t height,
         const SkMatrix& transform) {
     Layer* layer = LayerRenderer::createTextureLayer(renderThread.renderState());
+    layer->getTransform().load(transform);
 
     sp<DeferredLayerUpdater> layerUpdater = new DeferredLayerUpdater(layer);
     layerUpdater->setSize(width, height);
diff --git a/libs/hwui/tests/unit/FrameBuilderTests.cpp b/libs/hwui/tests/unit/FrameBuilderTests.cpp
index dca56d4..e97aaa6 100644
--- a/libs/hwui/tests/unit/FrameBuilderTests.cpp
+++ b/libs/hwui/tests/unit/FrameBuilderTests.cpp
@@ -372,8 +372,8 @@
     EXPECT_EQ(3, renderer.getIndex()) << "Expect 3 ops";
 }
 
-RENDERTHREAD_TEST(FrameBuilder, textureLayer) {
-    class TextureLayerTestRenderer : public TestRendererBase {
+RENDERTHREAD_TEST(FrameBuilder, textureLayer_clipLocalMatrix) {
+    class TextureLayerClipLocalMatrixTestRenderer : public TestRendererBase {
     public:
         void onTextureLayerOp(const TextureLayerOp& op, const BakedOpState& state) override {
             EXPECT_EQ(0, mIndex++);
@@ -398,11 +398,56 @@
     });
     FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
             TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
-    TextureLayerTestRenderer renderer;
+    TextureLayerClipLocalMatrixTestRenderer renderer;
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(1, renderer.getIndex());
 }
 
+RENDERTHREAD_TEST(FrameBuilder, textureLayer_combineMatrices) {
+    class TextureLayerCombineMatricesTestRenderer : public TestRendererBase {
+    public:
+        void onTextureLayerOp(const TextureLayerOp& op, const BakedOpState& state) override {
+            EXPECT_EQ(0, mIndex++);
+
+            Matrix4 expected;
+            expected.loadTranslate(35, 45, 0);
+            EXPECT_MATRIX_APPROX_EQ(expected, state.computedState.transform);
+        }
+    };
+
+    auto layerUpdater = TestUtils::createTextureLayerUpdater(renderThread, 100, 100,
+            SkMatrix::MakeTrans(5, 5));
+
+    auto node = TestUtils::createNode(0, 0, 200, 200,
+            [&layerUpdater](RenderProperties& props, RecordingCanvas& canvas) {
+        canvas.save(SaveFlags::MatrixClip);
+        canvas.translate(30, 40);
+        canvas.drawLayer(layerUpdater.get());
+        canvas.restore();
+    });
+
+    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+            TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
+    TextureLayerCombineMatricesTestRenderer renderer;
+    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
+    EXPECT_EQ(1, renderer.getIndex());
+}
+
+RENDERTHREAD_TEST(FrameBuilder, textureLayer_reject) {
+    auto layerUpdater = TestUtils::createTextureLayerUpdater(renderThread, 100, 100,
+            SkMatrix::MakeTrans(5, 5));
+    layerUpdater->backingLayer()->setRenderTarget(GL_NONE); // Should be rejected
+
+    auto node = TestUtils::createNode(0, 0, 200, 200,
+            [&layerUpdater](RenderProperties& props, RecordingCanvas& canvas) {
+        canvas.drawLayer(layerUpdater.get());
+    });
+    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+            TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
+    FailRenderer renderer;
+    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
+}
+
 RENDERTHREAD_TEST(FrameBuilder, functor_reject) {
     class FunctorTestRenderer : public TestRendererBase {
     public:
diff --git a/libs/hwui/tests/unit/RecordingCanvasTests.cpp b/libs/hwui/tests/unit/RecordingCanvasTests.cpp
index 33108c7..58376c6 100644
--- a/libs/hwui/tests/unit/RecordingCanvasTests.cpp
+++ b/libs/hwui/tests/unit/RecordingCanvasTests.cpp
@@ -16,6 +16,7 @@
 
 #include <gtest/gtest.h>
 
+#include <DeferredLayerUpdater.h>
 #include <RecordedOp.h>
 #include <RecordingCanvas.h>
 #include <hwui/Paint.h>
@@ -39,6 +40,12 @@
     }
 }
 
+static void validateSingleOp(std::unique_ptr<DisplayList>& dl,
+        std::function<void(const RecordedOp& op)> opValidator) {
+    ASSERT_EQ(1u, dl->getOps().size()) << "Must be exactly one op";
+    opValidator(*(dl->getOps()[0]));
+}
+
 TEST(RecordingCanvas, emptyPlayback) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) {
         canvas.save(SaveFlags::MatrixClip);
@@ -284,6 +291,21 @@
     ASSERT_EQ(2, count);
 }
 
+RENDERTHREAD_TEST(RecordingCanvas, textureLayer) {
+    auto layerUpdater = TestUtils::createTextureLayerUpdater(renderThread, 100, 100,
+            SkMatrix::MakeTrans(5, 5));
+
+    auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200,
+            [&layerUpdater](RecordingCanvas& canvas) {
+        canvas.drawLayer(layerUpdater.get());
+    });
+
+    validateSingleOp(dl, [] (const RecordedOp& op) {
+        ASSERT_EQ(RecordedOpId::TextureLayerOp, op.opId);
+        ASSERT_TRUE(op.localMatrix.isIdentity()) << "Op must not apply matrix at record time.";
+    });
+}
+
 TEST(RecordingCanvas, saveLayer_simple) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
         canvas.saveLayerAlpha(10, 20, 190, 180, 128, SaveFlags::ClipToLayer);