FrameBuilderTests.cpp revision 919645aded88b7d9f82b6f05777cbe827f415d1a
18ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis/*
28ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis * Copyright (C) 2016 The Android Open Source Project
38ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis *
48ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis * Licensed under the Apache License, Version 2.0 (the "License");
58ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis * you may not use this file except in compliance with the License.
68ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis * You may obtain a copy of the License at
78ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis *
88ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis *      http://www.apache.org/licenses/LICENSE-2.0
98ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis *
108ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis * Unless required by applicable law or agreed to in writing, software
118ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis * distributed under the License is distributed on an "AS IS" BASIS,
128ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
138ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis * See the License for the specific language governing permissions and
148ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis * limitations under the License.
158ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis */
168ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis
172adaf04fab35cf47c824d74d901b54094e01ccd3Andy McFadden#include <gtest/gtest.h>
182adaf04fab35cf47c824d74d901b54094e01ccd3Andy McFadden
198ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis#include <BakedOpState.h>
208ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis#include <DeferredLayerUpdater.h>
218ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis#include <FrameBuilder.h>
228ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis#include <LayerUpdateQueue.h>
238ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis#include <RecordedOp.h>
248ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis#include <RecordingCanvas.h>
258ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis#include <tests/common/TestUtils.h>
268ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis
278ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis#include <unordered_map>
28f78575400977f644cf0b12beb2fa5fc278b6ed4cJesse Hall
298ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennisnamespace android {
308ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennisnamespace uirenderer {
315065a55291b67f584d7b0be3fa3cfc4e29a3cd1cDan Stoza
328ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennisconst FrameBuilder::LightGeometry sLightGeometry = { {100, 100, 100}, 50};
338ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis
348ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis/**
358ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis * Virtual class implemented by each test to redirect static operation / state transitions to
36f0eaf25e9247edf4d124bedaeb863f7abdf35a3eDan Stoza * virtual methods.
37399184a4cd728ea1421fb0bc1722274a29e38f4aJesse Hall *
38e3c697fb929c856b59fa56a8e05a2a7eba187c3dMathias Agopian * Virtual dispatch allows for default behaviors to be specified (very common case in below tests),
39eafabcdc1639fb96062d9e3c39b0ae27b0238ae1Mathias Agopian * and allows Renderer vs Dispatching behavior to be merged.
400273adbf0bc202eda2ca579ae0773464ea9c701fAndy McFadden *
41466a192d2088f9238d34597d1aa28da41367c1caAndy McFadden * onXXXOp methods fail by default - tests should override ops they expect
42466a192d2088f9238d34597d1aa28da41367c1caAndy McFadden * startRepaintLayer fails by default - tests should override if expected
43466a192d2088f9238d34597d1aa28da41367c1caAndy McFadden * startFrame/endFrame do nothing by default - tests should override to intercept
44466a192d2088f9238d34597d1aa28da41367c1caAndy McFadden */
45e3c697fb929c856b59fa56a8e05a2a7eba187c3dMathias Agopianclass TestRendererBase {
460273adbf0bc202eda2ca579ae0773464ea9c701fAndy McFaddenpublic:
47466a192d2088f9238d34597d1aa28da41367c1caAndy McFadden    virtual ~TestRendererBase() {}
48466a192d2088f9238d34597d1aa28da41367c1caAndy McFadden    virtual OffscreenBuffer* startTemporaryLayer(uint32_t, uint32_t) {
49466a192d2088f9238d34597d1aa28da41367c1caAndy McFadden        ADD_FAILURE() << "Temporary layers not expected in this test";
50466a192d2088f9238d34597d1aa28da41367c1caAndy McFadden        return nullptr;
510273adbf0bc202eda2ca579ae0773464ea9c701fAndy McFadden    }
522adaf04fab35cf47c824d74d901b54094e01ccd3Andy McFadden    virtual void recycleTemporaryLayer(OffscreenBuffer*) {
530273adbf0bc202eda2ca579ae0773464ea9c701fAndy McFadden        ADD_FAILURE() << "Temporary layers not expected in this test";
542adaf04fab35cf47c824d74d901b54094e01ccd3Andy McFadden    }
558ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis    virtual void startRepaintLayer(OffscreenBuffer*, const Rect& repaintRect) {
568ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis        ADD_FAILURE() << "Layer repaint not expected in this test";
572adaf04fab35cf47c824d74d901b54094e01ccd3Andy McFadden    }
588ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis    virtual void endLayer() {
598072711307aa98ee5ee6f7369860ae38c3e19656Mathias Agopian        ADD_FAILURE() << "Layer updates not expected in this test";
607d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin    }
617d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin    virtual void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) {}
628072711307aa98ee5ee6f7369860ae38c3e19656Mathias Agopian    virtual void endFrame(const Rect& repaintRect) {}
637d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin
647d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin    // define virtual defaults for single draw methods
658072711307aa98ee5ee6f7369860ae38c3e19656Mathias Agopian#define X(Type) \
668072711307aa98ee5ee6f7369860ae38c3e19656Mathias Agopian    virtual void on##Type(const Type&, const BakedOpState&) { \
67a5c75c01620179ce00812354778a29a80d76e71fMathias Agopian        ADD_FAILURE() << #Type " not expected in this test"; \
688ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis    }
692adaf04fab35cf47c824d74d901b54094e01ccd3Andy McFadden    MAP_RENDERABLE_OPS(X)
702adaf04fab35cf47c824d74d901b54094e01ccd3Andy McFadden#undef X
718ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis
728ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis    // define virtual defaults for merged draw methods
737d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin#define X(Type) \
747d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin    virtual void onMerged##Type##s(const MergedBakedOpList& opList) { \
757d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin        ADD_FAILURE() << "Merged " #Type "s not expected in this test"; \
767d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin    }
77583b1b32191992d6ada58b3c61c71932a71c0c4bPablo Ceballos    MAP_MERGEABLE_OPS(X)
78583b1b32191992d6ada58b3c61c71932a71c0c4bPablo Ceballos#undef X
797d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin
807d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin    int getIndex() { return mIndex; }
817d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin
827b305fffc39d0fe0926e7fd2d7f6a524fbce62b7Jamie Gennisprotected:
838ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis    int mIndex = 0;
84fa455354557f6283ff3a7d76979e52fd251c155fPablo Ceballos};
8572daab652e3481566c01ce45c6afdb9fcec6f140Pablo Ceballos
8672daab652e3481566c01ce45c6afdb9fcec6f140Pablo Ceballos/**
8772daab652e3481566c01ce45c6afdb9fcec6f140Pablo Ceballos * Dispatches all static methods to similar formed methods on renderer, which fail by default but
8872daab652e3481566c01ce45c6afdb9fcec6f140Pablo Ceballos * are overridden by subclasses per test.
89fa455354557f6283ff3a7d76979e52fd251c155fPablo Ceballos */
90fa455354557f6283ff3a7d76979e52fd251c155fPablo Ceballosclass TestDispatcher {
91fa455354557f6283ff3a7d76979e52fd251c155fPablo Ceballospublic:
9272daab652e3481566c01ce45c6afdb9fcec6f140Pablo Ceballos    // define single op methods, which redirect to TestRendererBase
9372daab652e3481566c01ce45c6afdb9fcec6f140Pablo Ceballos#define X(Type) \
9472daab652e3481566c01ce45c6afdb9fcec6f140Pablo Ceballos    static void on##Type(TestRendererBase& renderer, const Type& op, const BakedOpState& state) { \
95fa455354557f6283ff3a7d76979e52fd251c155fPablo Ceballos        renderer.on##Type(op, state); \
96fa455354557f6283ff3a7d76979e52fd251c155fPablo Ceballos    }
97fa455354557f6283ff3a7d76979e52fd251c155fPablo Ceballos    MAP_RENDERABLE_OPS(X);
98fa455354557f6283ff3a7d76979e52fd251c155fPablo Ceballos#undef X
99fa455354557f6283ff3a7d76979e52fd251c155fPablo Ceballos
100fa455354557f6283ff3a7d76979e52fd251c155fPablo Ceballos    // define merged op methods, which redirect to TestRendererBase
101fa455354557f6283ff3a7d76979e52fd251c155fPablo Ceballos#define X(Type) \
102fa455354557f6283ff3a7d76979e52fd251c155fPablo Ceballos    static void onMerged##Type##s(TestRendererBase& renderer, const MergedBakedOpList& opList) { \
103fa455354557f6283ff3a7d76979e52fd251c155fPablo Ceballos        renderer.onMerged##Type##s(opList); \
10472daab652e3481566c01ce45c6afdb9fcec6f140Pablo Ceballos    }
10572daab652e3481566c01ce45c6afdb9fcec6f140Pablo Ceballos    MAP_MERGEABLE_OPS(X);
10672daab652e3481566c01ce45c6afdb9fcec6f140Pablo Ceballos#undef X
10772daab652e3481566c01ce45c6afdb9fcec6f140Pablo Ceballos};
10823b4abe024ea88c45e0b94c80e1fb537a573b143Pablo Ceballos
109fa455354557f6283ff3a7d76979e52fd251c155fPablo Ceballosclass FailRenderer : public TestRendererBase {};
110fa455354557f6283ff3a7d76979e52fd251c155fPablo Ceballos
111fa455354557f6283ff3a7d76979e52fd251c155fPablo CeballosRENDERTHREAD_TEST(FrameBuilder, simple) {
112fa455354557f6283ff3a7d76979e52fd251c155fPablo Ceballos    class SimpleTestRenderer : public TestRendererBase {
113fa455354557f6283ff3a7d76979e52fd251c155fPablo Ceballos    public:
114fa455354557f6283ff3a7d76979e52fd251c155fPablo Ceballos        void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
115fa455354557f6283ff3a7d76979e52fd251c155fPablo Ceballos            EXPECT_EQ(0, mIndex++);
116fa455354557f6283ff3a7d76979e52fd251c155fPablo Ceballos            EXPECT_EQ(100u, width);
117fa455354557f6283ff3a7d76979e52fd251c155fPablo Ceballos            EXPECT_EQ(200u, height);
118fa455354557f6283ff3a7d76979e52fd251c155fPablo Ceballos        }
119fa455354557f6283ff3a7d76979e52fd251c155fPablo Ceballos        void onRectOp(const RectOp& op, const BakedOpState& state) override {
120fa455354557f6283ff3a7d76979e52fd251c155fPablo Ceballos            EXPECT_EQ(1, mIndex++);
12123b4abe024ea88c45e0b94c80e1fb537a573b143Pablo Ceballos        }
12223b4abe024ea88c45e0b94c80e1fb537a573b143Pablo Ceballos        void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override {
123b687a2814ca9db576eb1ea33dea90ac35cd61bc1Pablo Ceballos            EXPECT_EQ(2, mIndex++);
12423b4abe024ea88c45e0b94c80e1fb537a573b143Pablo Ceballos        }
125fa455354557f6283ff3a7d76979e52fd251c155fPablo Ceballos        void endFrame(const Rect& repaintRect) override {
126fa455354557f6283ff3a7d76979e52fd251c155fPablo Ceballos            EXPECT_EQ(3, mIndex++);
1278ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis        }
1288ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis    };
1297d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin
1307d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin    auto node = TestUtils::createNode(0, 0, 100, 200,
1317d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin            [](RenderProperties& props, RecordingCanvas& canvas) {
1327d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin        SkBitmap bitmap = TestUtils::createSkBitmap(25, 25);
1337d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin        canvas.drawRect(0, 0, 100, 200, SkPaint());
1347d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin        canvas.drawBitmap(bitmap, 10, 10, nullptr);
1357d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin    });
1367d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin    FrameBuilder frameBuilder(SkRect::MakeWH(100, 200), 100, 200,
1377d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin            sLightGeometry, Caches::getInstance());
1387d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
1397d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin
1407d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin    SimpleTestRenderer renderer;
141a5c75c01620179ce00812354778a29a80d76e71fMathias Agopian    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
142f78575400977f644cf0b12beb2fa5fc278b6ed4cJesse Hall    EXPECT_EQ(4, renderer.getIndex()); // 2 ops + start + end
1437d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin}
1447d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin
1457d2d160cdc3cba9f4454f38433c94b68376cb843Igor MurashkinRENDERTHREAD_TEST(FrameBuilder, simpleStroke) {
146f78575400977f644cf0b12beb2fa5fc278b6ed4cJesse Hall    class SimpleStrokeTestRenderer : public TestRendererBase {
147f78575400977f644cf0b12beb2fa5fc278b6ed4cJesse Hall    public:
1487d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin        void onPointsOp(const PointsOp& op, const BakedOpState& state) override {
149f78575400977f644cf0b12beb2fa5fc278b6ed4cJesse Hall            EXPECT_EQ(0, mIndex++);
1507cdd786fa80cf03551291ae8feca7b77583be1c5Mathias Agopian            // even though initial bounds are empty...
1517d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin            EXPECT_TRUE(op.unmappedBounds.isEmpty())
1527d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin                    << "initial bounds should be empty, since they're unstroked";
1537d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin            EXPECT_EQ(Rect(45, 45, 55, 55), state.computedState.clippedBounds)
1547d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin                    << "final bounds should account for stroke";
1557d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin        }
1567d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin    };
1573be1c6b60a188dc10025e2ce156c11fac050625dDan Stoza
1587d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin    auto node = TestUtils::createNode(0, 0, 100, 200,
1597d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin            [](RenderProperties& props, RecordingCanvas& canvas) {
1607d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin        SkPaint strokedPaint;
1617d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin        strokedPaint.setStrokeWidth(10);
1627d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin        canvas.drawPoint(50, 50, strokedPaint);
1637d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin    });
1647d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin    FrameBuilder frameBuilder(SkRect::MakeWH(100, 200), 100, 200,
1657d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin            sLightGeometry, Caches::getInstance());
1667d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
1677d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin
1687d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin    SimpleStrokeTestRenderer renderer;
1697d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1707d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin    EXPECT_EQ(1, renderer.getIndex());
1717d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin}
172583b1b32191992d6ada58b3c61c71932a71c0c4bPablo Ceballos
173583b1b32191992d6ada58b3c61c71932a71c0c4bPablo CeballosRENDERTHREAD_TEST(FrameBuilder, simpleRejection) {
1749f3053de78630815d60cf48a2cf2348cc5867c45Dan Stoza    auto node = TestUtils::createNode(0, 0, 200, 200,
1759f3053de78630815d60cf48a2cf2348cc5867c45Dan Stoza            [](RenderProperties& props, RecordingCanvas& canvas) {
1769f3053de78630815d60cf48a2cf2348cc5867c45Dan Stoza        canvas.save(SaveFlags::MatrixClip);
1779f3053de78630815d60cf48a2cf2348cc5867c45Dan Stoza        canvas.clipRect(200, 200, 400, 400, SkRegion::kIntersect_Op); // intersection should be empty
1789f3053de78630815d60cf48a2cf2348cc5867c45Dan Stoza        canvas.drawRect(0, 0, 400, 400, SkPaint());
1799f3053de78630815d60cf48a2cf2348cc5867c45Dan Stoza        canvas.restore();
1809f3053de78630815d60cf48a2cf2348cc5867c45Dan Stoza    });
1819f3053de78630815d60cf48a2cf2348cc5867c45Dan Stoza    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
1827d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin            sLightGeometry, Caches::getInstance());
1837d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
1847d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin
185127fc63e8a15366b4395f1363e8e18eb058d1709Dan Stoza    FailRenderer renderer;
186127fc63e8a15366b4395f1363e8e18eb058d1709Dan Stoza    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1877d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin}
1887d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin
1897d2d160cdc3cba9f4454f38433c94b68376cb843Igor MurashkinRENDERTHREAD_TEST(FrameBuilder, simpleBatching) {
190567dbbb6dd42be5013fcde0dadb3316d85f2fa0dPablo Ceballos    const int LOOPS = 5;
191567dbbb6dd42be5013fcde0dadb3316d85f2fa0dPablo Ceballos    class SimpleBatchingTestRenderer : public TestRendererBase {
1928ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis    public:
1939f3053de78630815d60cf48a2cf2348cc5867c45Dan Stoza        void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override {
1949f3053de78630815d60cf48a2cf2348cc5867c45Dan Stoza            EXPECT_TRUE(mIndex++ >= LOOPS) << "Bitmaps should be above all rects";
1959f3053de78630815d60cf48a2cf2348cc5867c45Dan Stoza        }
1969f3053de78630815d60cf48a2cf2348cc5867c45Dan Stoza        void onRectOp(const RectOp& op, const BakedOpState& state) override {
1979f3053de78630815d60cf48a2cf2348cc5867c45Dan Stoza            EXPECT_TRUE(mIndex++ < LOOPS) << "Rects should be below all bitmaps";
1989f3053de78630815d60cf48a2cf2348cc5867c45Dan Stoza        }
1999f3053de78630815d60cf48a2cf2348cc5867c45Dan Stoza    };
2009f3053de78630815d60cf48a2cf2348cc5867c45Dan Stoza
2019f3053de78630815d60cf48a2cf2348cc5867c45Dan Stoza    auto node = TestUtils::createNode(0, 0, 200, 200,
2029f3053de78630815d60cf48a2cf2348cc5867c45Dan Stoza            [](RenderProperties& props, RecordingCanvas& canvas) {
203583b1b32191992d6ada58b3c61c71932a71c0c4bPablo Ceballos        SkBitmap bitmap = TestUtils::createSkBitmap(10, 10,
204583b1b32191992d6ada58b3c61c71932a71c0c4bPablo Ceballos                kAlpha_8_SkColorType); // Disable merging by using alpha 8 bitmap
2059f3053de78630815d60cf48a2cf2348cc5867c45Dan Stoza
2069f3053de78630815d60cf48a2cf2348cc5867c45Dan Stoza        // Alternate between drawing rects and bitmaps, with bitmaps overlapping rects.
2079f3053de78630815d60cf48a2cf2348cc5867c45Dan Stoza        // Rects don't overlap bitmaps, so bitmaps should be brought to front as a group.
2089f3053de78630815d60cf48a2cf2348cc5867c45Dan Stoza        canvas.save(SaveFlags::MatrixClip);
2099f3053de78630815d60cf48a2cf2348cc5867c45Dan Stoza        for (int i = 0; i < LOOPS; i++) {
210d9822a3843017444364899afc3c23fb5be6b9cb9Dan Stoza            canvas.translate(0, 10);
211d9822a3843017444364899afc3c23fb5be6b9cb9Dan Stoza            canvas.drawRect(0, 0, 10, 10, SkPaint());
212d9822a3843017444364899afc3c23fb5be6b9cb9Dan Stoza            canvas.drawBitmap(bitmap, 5, 0, nullptr);
213d9822a3843017444364899afc3c23fb5be6b9cb9Dan Stoza        }
214d9822a3843017444364899afc3c23fb5be6b9cb9Dan Stoza        canvas.restore();
215d9822a3843017444364899afc3c23fb5be6b9cb9Dan Stoza    });
216d9822a3843017444364899afc3c23fb5be6b9cb9Dan Stoza    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
217d9822a3843017444364899afc3c23fb5be6b9cb9Dan Stoza            sLightGeometry, Caches::getInstance());
218d9822a3843017444364899afc3c23fb5be6b9cb9Dan Stoza    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
219d9822a3843017444364899afc3c23fb5be6b9cb9Dan Stoza
220d9822a3843017444364899afc3c23fb5be6b9cb9Dan Stoza    SimpleBatchingTestRenderer renderer;
221d9822a3843017444364899afc3c23fb5be6b9cb9Dan Stoza    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
222d9822a3843017444364899afc3c23fb5be6b9cb9Dan Stoza    EXPECT_EQ(2 * LOOPS, renderer.getIndex())
223d9822a3843017444364899afc3c23fb5be6b9cb9Dan Stoza            << "Expect number of ops = 2 * loop count";
224583b1b32191992d6ada58b3c61c71932a71c0c4bPablo Ceballos}
225583b1b32191992d6ada58b3c61c71932a71c0c4bPablo Ceballos
226d9822a3843017444364899afc3c23fb5be6b9cb9Dan StozaRENDERTHREAD_TEST(FrameBuilder, deferRenderNode_translateClip) {
227d9822a3843017444364899afc3c23fb5be6b9cb9Dan Stoza    class DeferRenderNodeTranslateClipTestRenderer : public TestRendererBase {
228d9822a3843017444364899afc3c23fb5be6b9cb9Dan Stoza    public:
229d9822a3843017444364899afc3c23fb5be6b9cb9Dan Stoza        void onRectOp(const RectOp& op, const BakedOpState& state) override {
230d9822a3843017444364899afc3c23fb5be6b9cb9Dan Stoza            EXPECT_EQ(0, mIndex++);
231d9822a3843017444364899afc3c23fb5be6b9cb9Dan Stoza            EXPECT_EQ(Rect(5, 10, 55, 60), state.computedState.clippedBounds);
2329f3053de78630815d60cf48a2cf2348cc5867c45Dan Stoza            EXPECT_EQ(OpClipSideFlags::Right | OpClipSideFlags::Bottom,
2339f3053de78630815d60cf48a2cf2348cc5867c45Dan Stoza                    state.computedState.clipSideFlags);
2349f3053de78630815d60cf48a2cf2348cc5867c45Dan Stoza        }
2359f3053de78630815d60cf48a2cf2348cc5867c45Dan Stoza    };
2369f3053de78630815d60cf48a2cf2348cc5867c45Dan Stoza
2379f3053de78630815d60cf48a2cf2348cc5867c45Dan Stoza    auto node = TestUtils::createNode(0, 0, 100, 100,
2389f3053de78630815d60cf48a2cf2348cc5867c45Dan Stoza            [](RenderProperties& props, RecordingCanvas& canvas) {
2399f3053de78630815d60cf48a2cf2348cc5867c45Dan Stoza        canvas.drawRect(0, 0, 100, 100, SkPaint());
2409f3053de78630815d60cf48a2cf2348cc5867c45Dan Stoza    });
2419f3053de78630815d60cf48a2cf2348cc5867c45Dan Stoza
2429f3053de78630815d60cf48a2cf2348cc5867c45Dan Stoza    FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100,
2439f3053de78630815d60cf48a2cf2348cc5867c45Dan Stoza            sLightGeometry, Caches::getInstance());
244583b1b32191992d6ada58b3c61c71932a71c0c4bPablo Ceballos    frameBuilder.deferRenderNode(5, 10, Rect(50, 50), // translate + clip node
245583b1b32191992d6ada58b3c61c71932a71c0c4bPablo Ceballos            *TestUtils::getSyncedNode(node));
246812ed0644f8f8f71ca403f4e5793f0dbc1fcf9b2Dan Stoza
247812ed0644f8f8f71ca403f4e5793f0dbc1fcf9b2Dan Stoza    DeferRenderNodeTranslateClipTestRenderer renderer;
248812ed0644f8f8f71ca403f4e5793f0dbc1fcf9b2Dan Stoza    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
2499f3053de78630815d60cf48a2cf2348cc5867c45Dan Stoza    EXPECT_EQ(1, renderer.getIndex());
2509f3053de78630815d60cf48a2cf2348cc5867c45Dan Stoza}
2519f3053de78630815d60cf48a2cf2348cc5867c45Dan Stoza
2529f3053de78630815d60cf48a2cf2348cc5867c45Dan StozaRENDERTHREAD_TEST(FrameBuilder, deferRenderNodeScene) {
2539f3053de78630815d60cf48a2cf2348cc5867c45Dan Stoza    class DeferRenderNodeSceneTestRenderer : public TestRendererBase {
2549f3053de78630815d60cf48a2cf2348cc5867c45Dan Stoza    public:
2559f3053de78630815d60cf48a2cf2348cc5867c45Dan Stoza        void onRectOp(const RectOp& op, const BakedOpState& state) override {
2569f3053de78630815d60cf48a2cf2348cc5867c45Dan Stoza            const Rect& clippedBounds = state.computedState.clippedBounds;
2579f3053de78630815d60cf48a2cf2348cc5867c45Dan Stoza            Matrix4 expected;
258127fc63e8a15366b4395f1363e8e18eb058d1709Dan Stoza            switch (mIndex++) {
259127fc63e8a15366b4395f1363e8e18eb058d1709Dan Stoza            case 0:
2609f3053de78630815d60cf48a2cf2348cc5867c45Dan Stoza                // background - left side
2619f3053de78630815d60cf48a2cf2348cc5867c45Dan Stoza                EXPECT_EQ(Rect(600, 100, 700, 500), clippedBounds);
2629f3053de78630815d60cf48a2cf2348cc5867c45Dan Stoza                expected.loadTranslate(100, 100, 0);
2638ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis                break;
2648ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis            case 1:
2657d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin                // background - top side
2667d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin                EXPECT_EQ(Rect(100, 400, 600, 500), clippedBounds);
2677d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin                expected.loadTranslate(100, 100, 0);
2687d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin                break;
2697d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin            case 2:
2707d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin                // content
2717d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin                EXPECT_EQ(Rect(100, 100, 700, 500), clippedBounds);
2727d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin                expected.loadTranslate(-50, -50, 0);
2731d01a12e7150be569557b64da9b8663c62c13594Eino-Ville Talvala                break;
27497c602c5af5f3ffd69009bf496d86347b71a2b4cMathias Agopian            case 3:
2757d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin                // overlay
2767d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin                EXPECT_EQ(Rect(0, 0, 800, 200), clippedBounds);
2777d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin                break;
2787d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin            default:
2797cdd786fa80cf03551291ae8feca7b77583be1c5Mathias Agopian                ADD_FAILURE() << "Too many rects observed";
2807d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin            }
281583b1b32191992d6ada58b3c61c71932a71c0c4bPablo Ceballos            EXPECT_EQ(expected, state.computedState.transform);
282583b1b32191992d6ada58b3c61c71932a71c0c4bPablo Ceballos        }
2837d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin    };
2847d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin
2857d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin    std::vector<sp<RenderNode>> nodes;
2867d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin    SkPaint transparentPaint;
2877d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin    transparentPaint.setAlpha(128);
2887d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin
2897d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin    // backdrop
2907d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin    nodes.push_back(TestUtils::createNode(100, 100, 700, 500, // 600x400
2917d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin            [&transparentPaint](RenderProperties& props, RecordingCanvas& canvas) {
292f0bc2f1d8d37977bd3aef3d3326a70e9e69d4246Mathias Agopian        canvas.drawRect(0, 0, 600, 400, transparentPaint);
293e142428a9c8b9d2380032cd4d7b55ee440fe8770Mathias Agopian    }));
294e142428a9c8b9d2380032cd4d7b55ee440fe8770Mathias Agopian
295c777b0b3b9b0ea5d8e378fccde6935765e28e329Jesse Hall    // content
2967d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin    Rect contentDrawBounds(150, 150, 650, 450); // 500x300
2977d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin    nodes.push_back(TestUtils::createNode(0, 0, 800, 600,
29882c6bcc9705eabcaf5b9e45bc81867b0e2d61a02Eino-Ville Talvala            [&transparentPaint](RenderProperties& props, RecordingCanvas& canvas) {
2997d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin        canvas.drawRect(0, 0, 800, 600, transparentPaint);
3007d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin    }));
3017d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin
3027d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin    // overlay
3037d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin    nodes.push_back(TestUtils::createNode(0, 0, 800, 600,
3041681d95989271f3a9ac0dbb93d10e4a29f2b4444Ruben Brunk            [&transparentPaint](RenderProperties& props, RecordingCanvas& canvas) {
3051681d95989271f3a9ac0dbb93d10e4a29f2b4444Ruben Brunk        canvas.drawRect(0, 0, 800, 200, transparentPaint);
3063c25621ad7d13f64d3ab95a27fa970fbc9998f73Andy McFadden    }));
30782c6bcc9705eabcaf5b9e45bc81867b0e2d61a02Eino-Ville Talvala
308567dbbb6dd42be5013fcde0dadb3316d85f2fa0dPablo Ceballos    for (auto& node : nodes) {
30982c6bcc9705eabcaf5b9e45bc81867b0e2d61a02Eino-Ville Talvala        TestUtils::syncHierarchyPropertiesAndDisplayList(node);
31082c6bcc9705eabcaf5b9e45bc81867b0e2d61a02Eino-Ville Talvala    }
311567dbbb6dd42be5013fcde0dadb3316d85f2fa0dPablo Ceballos
312567dbbb6dd42be5013fcde0dadb3316d85f2fa0dPablo Ceballos    FrameBuilder frameBuilder(SkRect::MakeWH(800, 600), 800, 600,
3133c25621ad7d13f64d3ab95a27fa970fbc9998f73Andy McFadden            sLightGeometry, Caches::getInstance());
31482c6bcc9705eabcaf5b9e45bc81867b0e2d61a02Eino-Ville Talvala    frameBuilder.deferRenderNodeScene(nodes, contentDrawBounds);
31582c6bcc9705eabcaf5b9e45bc81867b0e2d61a02Eino-Ville Talvala
316567dbbb6dd42be5013fcde0dadb3316d85f2fa0dPablo Ceballos    DeferRenderNodeSceneTestRenderer renderer;
3171681d95989271f3a9ac0dbb93d10e4a29f2b4444Ruben Brunk    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
318f0bc2f1d8d37977bd3aef3d3326a70e9e69d4246Mathias Agopian    EXPECT_EQ(4, renderer.getIndex());
3193c25621ad7d13f64d3ab95a27fa970fbc9998f73Andy McFadden}
32082c6bcc9705eabcaf5b9e45bc81867b0e2d61a02Eino-Ville Talvala
321f0bc2f1d8d37977bd3aef3d3326a70e9e69d4246Mathias AgopianRENDERTHREAD_TEST(FrameBuilder, empty_noFbo0) {
322f0bc2f1d8d37977bd3aef3d3326a70e9e69d4246Mathias Agopian    class EmptyNoFbo0TestRenderer : public TestRendererBase {
323f0bc2f1d8d37977bd3aef3d3326a70e9e69d4246Mathias Agopian    public:
324c777b0b3b9b0ea5d8e378fccde6935765e28e329Jesse Hall        void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
3251681d95989271f3a9ac0dbb93d10e4a29f2b4444Ruben Brunk            ADD_FAILURE() << "Primary frame draw not expected in this test";
3261681d95989271f3a9ac0dbb93d10e4a29f2b4444Ruben Brunk        }
3271681d95989271f3a9ac0dbb93d10e4a29f2b4444Ruben Brunk        void endFrame(const Rect& repaintRect) override {
328f0bc2f1d8d37977bd3aef3d3326a70e9e69d4246Mathias Agopian            ADD_FAILURE() << "Primary frame draw not expected in this test";
329c777b0b3b9b0ea5d8e378fccde6935765e28e329Jesse Hall        }
330e142428a9c8b9d2380032cd4d7b55ee440fe8770Mathias Agopian    };
331e142428a9c8b9d2380032cd4d7b55ee440fe8770Mathias Agopian
332e142428a9c8b9d2380032cd4d7b55ee440fe8770Mathias Agopian    // Use layer update constructor, so no work is enqueued for Fbo0
333e142428a9c8b9d2380032cd4d7b55ee440fe8770Mathias Agopian    LayerUpdateQueue emptyLayerUpdateQueue;
334e142428a9c8b9d2380032cd4d7b55ee440fe8770Mathias Agopian    FrameBuilder frameBuilder(emptyLayerUpdateQueue, sLightGeometry, Caches::getInstance());
335c777b0b3b9b0ea5d8e378fccde6935765e28e329Jesse Hall    EmptyNoFbo0TestRenderer renderer;
3365065a55291b67f584d7b0be3fa3cfc4e29a3cd1cDan Stoza    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
3375065a55291b67f584d7b0be3fa3cfc4e29a3cd1cDan Stoza}
3385065a55291b67f584d7b0be3fa3cfc4e29a3cd1cDan Stoza
339f0bc2f1d8d37977bd3aef3d3326a70e9e69d4246Mathias AgopianRENDERTHREAD_TEST(FrameBuilder, empty_withFbo0) {
340f0bc2f1d8d37977bd3aef3d3326a70e9e69d4246Mathias Agopian    class EmptyWithFbo0TestRenderer : public TestRendererBase {
3413c25621ad7d13f64d3ab95a27fa970fbc9998f73Andy McFadden    public:
34282c6bcc9705eabcaf5b9e45bc81867b0e2d61a02Eino-Ville Talvala        void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
343f0bc2f1d8d37977bd3aef3d3326a70e9e69d4246Mathias Agopian            EXPECT_EQ(0, mIndex++);
344f0bc2f1d8d37977bd3aef3d3326a70e9e69d4246Mathias Agopian        }
345f0bc2f1d8d37977bd3aef3d3326a70e9e69d4246Mathias Agopian        void endFrame(const Rect& repaintRect) override {
3461681d95989271f3a9ac0dbb93d10e4a29f2b4444Ruben Brunk            EXPECT_EQ(1, mIndex++);
347c777b0b3b9b0ea5d8e378fccde6935765e28e329Jesse Hall        }
3485065a55291b67f584d7b0be3fa3cfc4e29a3cd1cDan Stoza    };
349f0bc2f1d8d37977bd3aef3d3326a70e9e69d4246Mathias Agopian    auto node = TestUtils::createNode(10, 10, 110, 110,
350f0bc2f1d8d37977bd3aef3d3326a70e9e69d4246Mathias Agopian            [](RenderProperties& props, RecordingCanvas& canvas) {
351f0bc2f1d8d37977bd3aef3d3326a70e9e69d4246Mathias Agopian        // no drawn content
3527d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin    });
353f0bc2f1d8d37977bd3aef3d3326a70e9e69d4246Mathias Agopian
3547d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin    // Draw, but pass node without draw content, so no work is done for primary frame
3557d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
3567d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin            sLightGeometry, Caches::getInstance());
3577d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
3587d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin
359f0bc2f1d8d37977bd3aef3d3326a70e9e69d4246Mathias Agopian    EmptyWithFbo0TestRenderer renderer;
3602488b20aec097accb20a853d9876bb0a5dc04636Mathias Agopian    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
3612488b20aec097accb20a853d9876bb0a5dc04636Mathias Agopian    EXPECT_EQ(2, renderer.getIndex()) << "No drawing content produced,"
3622488b20aec097accb20a853d9876bb0a5dc04636Mathias Agopian            " but fbo0 update lifecycle should still be observed";
363f0bc2f1d8d37977bd3aef3d3326a70e9e69d4246Mathias Agopian}
364f0bc2f1d8d37977bd3aef3d3326a70e9e69d4246Mathias Agopian
365f0bc2f1d8d37977bd3aef3d3326a70e9e69d4246Mathias AgopianRENDERTHREAD_TEST(FrameBuilder, avoidOverdraw_rects) {
3662488b20aec097accb20a853d9876bb0a5dc04636Mathias Agopian    class AvoidOverdrawRectsTestRenderer : public TestRendererBase {
367f0bc2f1d8d37977bd3aef3d3326a70e9e69d4246Mathias Agopian    public:
368f0bc2f1d8d37977bd3aef3d3326a70e9e69d4246Mathias Agopian        void onRectOp(const RectOp& op, const BakedOpState& state) override {
3692488b20aec097accb20a853d9876bb0a5dc04636Mathias Agopian            EXPECT_EQ(mIndex++, 0) << "Should be one rect";
370f0bc2f1d8d37977bd3aef3d3326a70e9e69d4246Mathias Agopian            EXPECT_EQ(Rect(10, 10, 190, 190), op.unmappedBounds)
371f0bc2f1d8d37977bd3aef3d3326a70e9e69d4246Mathias Agopian                    << "Last rect should occlude others.";
372f0bc2f1d8d37977bd3aef3d3326a70e9e69d4246Mathias Agopian        }
3732488b20aec097accb20a853d9876bb0a5dc04636Mathias Agopian    };
374f0bc2f1d8d37977bd3aef3d3326a70e9e69d4246Mathias Agopian    auto node = TestUtils::createNode(0, 0, 200, 200,
375f0bc2f1d8d37977bd3aef3d3326a70e9e69d4246Mathias Agopian            [](RenderProperties& props, RecordingCanvas& canvas) {
376f0bc2f1d8d37977bd3aef3d3326a70e9e69d4246Mathias Agopian        canvas.drawRect(0, 0, 200, 200, SkPaint());
377f0bc2f1d8d37977bd3aef3d3326a70e9e69d4246Mathias Agopian        canvas.drawRect(0, 0, 200, 200, SkPaint());
378f0bc2f1d8d37977bd3aef3d3326a70e9e69d4246Mathias Agopian        canvas.drawRect(10, 10, 190, 190, SkPaint());
3792488b20aec097accb20a853d9876bb0a5dc04636Mathias Agopian    });
380f0bc2f1d8d37977bd3aef3d3326a70e9e69d4246Mathias Agopian
381f0bc2f1d8d37977bd3aef3d3326a70e9e69d4246Mathias Agopian    // Damage (and therefore clip) is same as last draw, subset of renderable area.
382567dbbb6dd42be5013fcde0dadb3316d85f2fa0dPablo Ceballos    // This means last op occludes other contents, and they'll be rejected to avoid overdraw.
383567dbbb6dd42be5013fcde0dadb3316d85f2fa0dPablo Ceballos    FrameBuilder frameBuilder(SkRect::MakeLTRB(10, 10, 190, 190), 200, 200,
3848ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis            sLightGeometry, Caches::getInstance());
3858ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
3868ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis
3878ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis    EXPECT_EQ(3u, node->getDisplayList()->getOps().size())
3887d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin            << "Recording must not have rejected ops, in order for this test to be valid";
3897d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin
3907d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin    AvoidOverdrawRectsTestRenderer renderer;
391583b1b32191992d6ada58b3c61c71932a71c0c4bPablo Ceballos    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
392583b1b32191992d6ada58b3c61c71932a71c0c4bPablo Ceballos    EXPECT_EQ(1, renderer.getIndex()) << "Expect exactly one op";
3937d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin}
3947d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin
395583b1b32191992d6ada58b3c61c71932a71c0c4bPablo CeballosRENDERTHREAD_TEST(FrameBuilder, avoidOverdraw_bitmaps) {
396583b1b32191992d6ada58b3c61c71932a71c0c4bPablo Ceballos    static SkBitmap opaqueBitmap = TestUtils::createSkBitmap(50, 50,
397583b1b32191992d6ada58b3c61c71932a71c0c4bPablo Ceballos            SkColorType::kRGB_565_SkColorType);
398583b1b32191992d6ada58b3c61c71932a71c0c4bPablo Ceballos    static SkBitmap transpBitmap = TestUtils::createSkBitmap(50, 50,
399583b1b32191992d6ada58b3c61c71932a71c0c4bPablo Ceballos            SkColorType::kAlpha_8_SkColorType);
400583b1b32191992d6ada58b3c61c71932a71c0c4bPablo Ceballos    class AvoidOverdrawBitmapsTestRenderer : public TestRendererBase {
401583b1b32191992d6ada58b3c61c71932a71c0c4bPablo Ceballos    public:
402583b1b32191992d6ada58b3c61c71932a71c0c4bPablo Ceballos        void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override {
403583b1b32191992d6ada58b3c61c71932a71c0c4bPablo Ceballos            switch(mIndex++) {
4048ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis            case 0:
405eafabcdc1639fb96062d9e3c39b0ae27b0238ae1Mathias Agopian                EXPECT_EQ(opaqueBitmap.pixelRef(), op.bitmap->pixelRef());
4067d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin                break;
4077d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin            case 1:
4087d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin                EXPECT_EQ(transpBitmap.pixelRef(), op.bitmap->pixelRef());
4097d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin                break;
4107d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin            default:
411eafabcdc1639fb96062d9e3c39b0ae27b0238ae1Mathias Agopian                ADD_FAILURE() << "Only two ops expected.";
4128072711307aa98ee5ee6f7369860ae38c3e19656Mathias Agopian            }
4132adaf04fab35cf47c824d74d901b54094e01ccd3Andy McFadden        }
4142adaf04fab35cf47c824d74d901b54094e01ccd3Andy McFadden    };
4157d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin
416fe0a87b54654a1392650e7f1862df473287d8332Jamie Gennis    auto node = TestUtils::createNode(0, 0, 50, 50,
417fe0a87b54654a1392650e7f1862df473287d8332Jamie Gennis            [](RenderProperties& props, RecordingCanvas& canvas) {
4182adaf04fab35cf47c824d74d901b54094e01ccd3Andy McFadden        canvas.drawRect(0, 0, 50, 50, SkPaint());
4195bfc24515bb5c8ea7975f72d538df37753733a2fMathias Agopian        canvas.drawRect(0, 0, 50, 50, SkPaint());
420f0eaf25e9247edf4d124bedaeb863f7abdf35a3eDan Stoza        canvas.drawBitmap(transpBitmap, 0, 0, nullptr);
421f0eaf25e9247edf4d124bedaeb863f7abdf35a3eDan Stoza
422f0eaf25e9247edf4d124bedaeb863f7abdf35a3eDan Stoza        // only the below draws should remain, since they're
423f0eaf25e9247edf4d124bedaeb863f7abdf35a3eDan Stoza        canvas.drawBitmap(opaqueBitmap, 0, 0, nullptr);
424f0eaf25e9247edf4d124bedaeb863f7abdf35a3eDan Stoza        canvas.drawBitmap(transpBitmap, 0, 0, nullptr);
4257d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin    });
4267d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin    FrameBuilder frameBuilder(SkRect::MakeWH(50, 50), 50, 50,
4277d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin            sLightGeometry, Caches::getInstance());
4287d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
4297d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin
4307d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin    EXPECT_EQ(5u, node->getDisplayList()->getOps().size())
4317d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin            << "Recording must not have rejected ops, in order for this test to be valid";
4327d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin
4337d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin    AvoidOverdrawBitmapsTestRenderer renderer;
4347d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
4357d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin    EXPECT_EQ(2, renderer.getIndex()) << "Expect exactly two ops";
4367d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin}
4377d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin
4387d2d160cdc3cba9f4454f38433c94b68376cb843Igor MurashkinRENDERTHREAD_TEST(FrameBuilder, clippedMerging) {
4397d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin    class ClippedMergingTestRenderer : public TestRendererBase {
4407d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin    public:
4417d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin        void onMergedBitmapOps(const MergedBakedOpList& opList) override {
4427d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin            EXPECT_EQ(0, mIndex);
4437ea777f097784492f880623067becac1b276f884Igor Murashkin            mIndex += opList.count;
44423b4abe024ea88c45e0b94c80e1fb537a573b143Pablo Ceballos            EXPECT_EQ(4u, opList.count);
44523b4abe024ea88c45e0b94c80e1fb537a573b143Pablo Ceballos            EXPECT_EQ(Rect(10, 10, 90, 90), opList.clip);
44623b4abe024ea88c45e0b94c80e1fb537a573b143Pablo Ceballos            EXPECT_EQ(OpClipSideFlags::Left | OpClipSideFlags::Top | OpClipSideFlags::Right,
4477d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin                    opList.clipSideFlags);
4487d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin        }
4497d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin    };
4507d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin    auto node = TestUtils::createNode(0, 0, 100, 100,
451f0eaf25e9247edf4d124bedaeb863f7abdf35a3eDan Stoza            [](RenderProperties& props, TestCanvas& canvas) {
452365857df8b94c959dea984a63013f6e7730ef976Mathias Agopian        SkBitmap bitmap = TestUtils::createSkBitmap(20, 20);
453fe0a87b54654a1392650e7f1862df473287d8332Jamie Gennis
4542adaf04fab35cf47c824d74d901b54094e01ccd3Andy McFadden        // left side clipped (to inset left half)
4552adaf04fab35cf47c824d74d901b54094e01ccd3Andy McFadden        canvas.clipRect(10, 0, 50, 100, SkRegion::kReplace_Op);
4562adaf04fab35cf47c824d74d901b54094e01ccd3Andy McFadden        canvas.drawBitmap(bitmap, 0, 40, nullptr);
4572adaf04fab35cf47c824d74d901b54094e01ccd3Andy McFadden
4582adaf04fab35cf47c824d74d901b54094e01ccd3Andy McFadden        // top side clipped (to inset top half)
459fe0a87b54654a1392650e7f1862df473287d8332Jamie Gennis        canvas.clipRect(0, 10, 100, 50, SkRegion::kReplace_Op);
4602adaf04fab35cf47c824d74d901b54094e01ccd3Andy McFadden        canvas.drawBitmap(bitmap, 40, 0, nullptr);
461fe0a87b54654a1392650e7f1862df473287d8332Jamie Gennis
4627d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin        // right side clipped (to inset right half)
4637d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin        canvas.clipRect(50, 0, 90, 100, SkRegion::kReplace_Op);
4647d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin        canvas.drawBitmap(bitmap, 80, 40, nullptr);
4657d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin
4667d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin        // bottom not clipped, just abutting (inset bottom half)
4677d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin        canvas.clipRect(0, 50, 100, 90, SkRegion::kReplace_Op);
4687d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin        canvas.drawBitmap(bitmap, 40, 70, nullptr);
4697d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin    });
4707d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin
4717d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin    FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100,
4727d2d160cdc3cba9f4454f38433c94b68376cb843Igor Murashkin            sLightGeometry, Caches::getInstance());
473fe0a87b54654a1392650e7f1862df473287d8332Jamie Gennis    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
474399184a4cd728ea1421fb0bc1722274a29e38f4aJesse Hall
475399184a4cd728ea1421fb0bc1722274a29e38f4aJesse Hall    ClippedMergingTestRenderer renderer;
476399184a4cd728ea1421fb0bc1722274a29e38f4aJesse Hall    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
477399184a4cd728ea1421fb0bc1722274a29e38f4aJesse Hall    EXPECT_EQ(4, renderer.getIndex());
478399184a4cd728ea1421fb0bc1722274a29e38f4aJesse Hall}
479399184a4cd728ea1421fb0bc1722274a29e38f4aJesse Hall
480399184a4cd728ea1421fb0bc1722274a29e38f4aJesse HallRENDERTHREAD_TEST(FrameBuilder, textMerging) {
481399184a4cd728ea1421fb0bc1722274a29e38f4aJesse Hall    class TextMergingTestRenderer : public TestRendererBase {
482399184a4cd728ea1421fb0bc1722274a29e38f4aJesse Hall    public:
483399184a4cd728ea1421fb0bc1722274a29e38f4aJesse Hall        void onMergedTextOps(const MergedBakedOpList& opList) override {
484399184a4cd728ea1421fb0bc1722274a29e38f4aJesse Hall            EXPECT_EQ(0, mIndex);
485399184a4cd728ea1421fb0bc1722274a29e38f4aJesse Hall            mIndex += opList.count;
48629a3e90879fd96404c971e7187cd0e05927bbce0Dan Stoza            EXPECT_EQ(2u, opList.count);
48729a3e90879fd96404c971e7187cd0e05927bbce0Dan Stoza            EXPECT_EQ(OpClipSideFlags::Top, opList.clipSideFlags);
48829a3e90879fd96404c971e7187cd0e05927bbce0Dan Stoza            EXPECT_EQ(OpClipSideFlags::Top, opList.states[0]->computedState.clipSideFlags);
48929a3e90879fd96404c971e7187cd0e05927bbce0Dan Stoza            EXPECT_EQ(OpClipSideFlags::None, opList.states[1]->computedState.clipSideFlags);
49029a3e90879fd96404c971e7187cd0e05927bbce0Dan Stoza        }
49129a3e90879fd96404c971e7187cd0e05927bbce0Dan Stoza    };
49229a3e90879fd96404c971e7187cd0e05927bbce0Dan Stoza    auto node = TestUtils::createNode(0, 0, 400, 400,
49329a3e90879fd96404c971e7187cd0e05927bbce0Dan Stoza            [](RenderProperties& props, TestCanvas& canvas) {
49429a3e90879fd96404c971e7187cd0e05927bbce0Dan Stoza        SkPaint paint;
49529a3e90879fd96404c971e7187cd0e05927bbce0Dan Stoza        paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
49629a3e90879fd96404c971e7187cd0e05927bbce0Dan Stoza        paint.setAntiAlias(true);
497567dbbb6dd42be5013fcde0dadb3316d85f2fa0dPablo Ceballos        paint.setTextSize(50);
4983be1c6b60a188dc10025e2ce156c11fac050625dDan Stoza        TestUtils::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 0); // will be top clipped
4999de7293b0a1b01ebe6fb1ab4a498f144adc8029fDan Stoza        TestUtils::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 100); // not clipped
5009de7293b0a1b01ebe6fb1ab4a498f144adc8029fDan Stoza    });
5019de7293b0a1b01ebe6fb1ab4a498f144adc8029fDan Stoza    FrameBuilder frameBuilder(SkRect::MakeWH(400, 400), 400, 400,
5029de7293b0a1b01ebe6fb1ab4a498f144adc8029fDan Stoza            sLightGeometry, Caches::getInstance());
5039de7293b0a1b01ebe6fb1ab4a498f144adc8029fDan Stoza    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
5049de7293b0a1b01ebe6fb1ab4a498f144adc8029fDan Stoza
5059de7293b0a1b01ebe6fb1ab4a498f144adc8029fDan Stoza    TextMergingTestRenderer renderer;
5069de7293b0a1b01ebe6fb1ab4a498f144adc8029fDan Stoza    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
5079de7293b0a1b01ebe6fb1ab4a498f144adc8029fDan Stoza    EXPECT_EQ(2, renderer.getIndex()) << "Expect 2 ops";
5089de7293b0a1b01ebe6fb1ab4a498f144adc8029fDan Stoza}
5099de7293b0a1b01ebe6fb1ab4a498f144adc8029fDan Stoza
5109de7293b0a1b01ebe6fb1ab4a498f144adc8029fDan StozaRENDERTHREAD_TEST(FrameBuilder, textStrikethrough) {
511812ed0644f8f8f71ca403f4e5793f0dbc1fcf9b2Dan Stoza    const int LOOPS = 5;
512812ed0644f8f8f71ca403f4e5793f0dbc1fcf9b2Dan Stoza    class TextStrikethroughTestRenderer : public TestRendererBase {
513812ed0644f8f8f71ca403f4e5793f0dbc1fcf9b2Dan Stoza    public:
514812ed0644f8f8f71ca403f4e5793f0dbc1fcf9b2Dan Stoza        void onRectOp(const RectOp& op, const BakedOpState& state) override {
515812ed0644f8f8f71ca403f4e5793f0dbc1fcf9b2Dan Stoza            EXPECT_TRUE(mIndex++ >= LOOPS) << "Strikethrough rects should be above all text";
516812ed0644f8f8f71ca403f4e5793f0dbc1fcf9b2Dan Stoza        }
517812ed0644f8f8f71ca403f4e5793f0dbc1fcf9b2Dan Stoza        void onMergedTextOps(const MergedBakedOpList& opList) override {
518812ed0644f8f8f71ca403f4e5793f0dbc1fcf9b2Dan Stoza            EXPECT_EQ(0, mIndex);
519812ed0644f8f8f71ca403f4e5793f0dbc1fcf9b2Dan Stoza            mIndex += opList.count;
520c6f30bdee1f634eb90d68cb76efe935b6535a1e8Dan Stoza            EXPECT_EQ(5u, opList.count);
521c6f30bdee1f634eb90d68cb76efe935b6535a1e8Dan Stoza        }
522c6f30bdee1f634eb90d68cb76efe935b6535a1e8Dan Stoza    };
5237dde599bf1a0dbef7390d91c2689d506371cdbd7Dan Stoza    auto node = TestUtils::createNode(0, 0, 200, 2000,
5247dde599bf1a0dbef7390d91c2689d506371cdbd7Dan Stoza            [](RenderProperties& props, RecordingCanvas& canvas) {
5257dde599bf1a0dbef7390d91c2689d506371cdbd7Dan Stoza        SkPaint textPaint;
526ccdfd60d79a8b7f1ed6401d0f2e8e29166a10584Pablo Ceballos        textPaint.setAntiAlias(true);
5273559fbf93801e2c0d9d8fb246fb9b867a361b464Pablo Ceballos        textPaint.setTextSize(20);
528ccdfd60d79a8b7f1ed6401d0f2e8e29166a10584Pablo Ceballos        textPaint.setStrikeThruText(true);
5293559fbf93801e2c0d9d8fb246fb9b867a361b464Pablo Ceballos        textPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
530295a9fc8aa87daa2cded5c1a279b8cd24e9a9a9fPablo Ceballos        for (int i = 0; i < LOOPS; i++) {
531295a9fc8aa87daa2cded5c1a279b8cd24e9a9a9fPablo Ceballos            TestUtils::drawUtf8ToCanvas(&canvas, "test text", textPaint, 10, 100 * (i + 1));
532295a9fc8aa87daa2cded5c1a279b8cd24e9a9a9fPablo Ceballos        }
5333559fbf93801e2c0d9d8fb246fb9b867a361b464Pablo Ceballos    });
534127fc63e8a15366b4395f1363e8e18eb058d1709Dan Stoza
535ff95aabbcc6e8606acbd7933c90eeb9b8b382a21Pablo Ceballos    FrameBuilder frameBuilder(SkRect::MakeWH(200, 2000), 200, 2000,
536ff95aabbcc6e8606acbd7933c90eeb9b8b382a21Pablo Ceballos            sLightGeometry, Caches::getInstance());
5373559fbf93801e2c0d9d8fb246fb9b867a361b464Pablo Ceballos    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
538ff95aabbcc6e8606acbd7933c90eeb9b8b382a21Pablo Ceballos
539ff95aabbcc6e8606acbd7933c90eeb9b8b382a21Pablo Ceballos    TextStrikethroughTestRenderer renderer;
540ff95aabbcc6e8606acbd7933c90eeb9b8b382a21Pablo Ceballos    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
541ff95aabbcc6e8606acbd7933c90eeb9b8b382a21Pablo Ceballos    EXPECT_EQ(2 * LOOPS, renderer.getIndex())
542ff95aabbcc6e8606acbd7933c90eeb9b8b382a21Pablo Ceballos            << "Expect number of ops = 2 * loop count";
543127fc63e8a15366b4395f1363e8e18eb058d1709Dan Stoza}
544127fc63e8a15366b4395f1363e8e18eb058d1709Dan Stoza
545127fc63e8a15366b4395f1363e8e18eb058d1709Dan Stozastatic auto styles = {
546127fc63e8a15366b4395f1363e8e18eb058d1709Dan Stoza        SkPaint::kFill_Style, SkPaint::kStroke_Style, SkPaint::kStrokeAndFill_Style };
547127fc63e8a15366b4395f1363e8e18eb058d1709Dan Stoza
548127fc63e8a15366b4395f1363e8e18eb058d1709Dan StozaRENDERTHREAD_TEST(FrameBuilder, textStyle) {
549127fc63e8a15366b4395f1363e8e18eb058d1709Dan Stoza    class TextStyleTestRenderer : public TestRendererBase {
550127fc63e8a15366b4395f1363e8e18eb058d1709Dan Stoza    public:
551127fc63e8a15366b4395f1363e8e18eb058d1709Dan Stoza        void onMergedTextOps(const MergedBakedOpList& opList) override {
552127fc63e8a15366b4395f1363e8e18eb058d1709Dan Stoza            ASSERT_EQ(0, mIndex);
553127fc63e8a15366b4395f1363e8e18eb058d1709Dan Stoza            ASSERT_EQ(3u, opList.count);
55423b4abe024ea88c45e0b94c80e1fb537a573b143Pablo Ceballos            mIndex += opList.count;
55523b4abe024ea88c45e0b94c80e1fb537a573b143Pablo Ceballos
55623b4abe024ea88c45e0b94c80e1fb537a573b143Pablo Ceballos            int index = 0;
55723b4abe024ea88c45e0b94c80e1fb537a573b143Pablo Ceballos            for (auto style : styles) {
55823b4abe024ea88c45e0b94c80e1fb537a573b143Pablo Ceballos                auto state = opList.states[index++];
559127fc63e8a15366b4395f1363e8e18eb058d1709Dan Stoza                ASSERT_EQ(style, state->op->paint->getStyle())
5608ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis                        << "Remainder of validation relies upon stable merged order";
5618ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis                ASSERT_EQ(0, state->computedState.clipSideFlags)
5628ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis                        << "Clipped bounds validation requires unclipped ops";
5638ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis            }
5642adaf04fab35cf47c824d74d901b54094e01ccd3Andy McFadden
5658ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis            Rect fill = opList.states[0]->computedState.clippedBounds;
5668ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis            Rect stroke = opList.states[1]->computedState.clippedBounds;
5678ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis            EXPECT_EQ(stroke, opList.states[2]->computedState.clippedBounds)
5688ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis                    << "Stroke+Fill should be same as stroke";
5698ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis
5708ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis            EXPECT_TRUE(stroke.contains(fill));
5718ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis            EXPECT_FALSE(fill.contains(stroke));
5728ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis
5738ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis            // outset by half the stroke width
5748ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis            Rect outsetFill(fill);
5758ba32fade11abb73f3fd47ea0953c9528eb5b91fJamie Gennis            outsetFill.outset(5);
5762adaf04fab35cf47c824d74d901b54094e01ccd3Andy McFadden            EXPECT_EQ(stroke, outsetFill);
577        }
578    };
579    auto node = TestUtils::createNode(0, 0, 400, 400,
580            [](RenderProperties& props, TestCanvas& canvas) {
581        SkPaint paint;
582        paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
583        paint.setAntiAlias(true);
584        paint.setTextSize(50);
585        paint.setStrokeWidth(10);
586
587        // draw 3 copies of the same text overlapping, each with a different style.
588        // They'll get merged, but with
589        for (auto style : styles) {
590            paint.setStyle(style);
591            TestUtils::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 100);
592        }
593    });
594    FrameBuilder frameBuilder(SkRect::MakeWH(400, 400), 400, 400,
595            sLightGeometry, Caches::getInstance());
596    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
597    TextStyleTestRenderer renderer;
598    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
599    EXPECT_EQ(3, renderer.getIndex()) << "Expect 3 ops";
600}
601
602RENDERTHREAD_TEST(FrameBuilder, textureLayer_clipLocalMatrix) {
603    class TextureLayerClipLocalMatrixTestRenderer : public TestRendererBase {
604    public:
605        void onTextureLayerOp(const TextureLayerOp& op, const BakedOpState& state) override {
606            EXPECT_EQ(0, mIndex++);
607            EXPECT_EQ(Rect(50, 50, 150, 150), state.computedState.clipRect());
608            EXPECT_EQ(Rect(50, 50, 105, 105), state.computedState.clippedBounds);
609
610            Matrix4 expected;
611            expected.loadTranslate(5, 5, 0);
612            EXPECT_MATRIX_APPROX_EQ(expected, state.computedState.transform);
613        }
614    };
615
616    auto layerUpdater = TestUtils::createTextureLayerUpdater(renderThread, 100, 100,
617            SkMatrix::MakeTrans(5, 5));
618
619    auto node = TestUtils::createNode(0, 0, 200, 200,
620            [&layerUpdater](RenderProperties& props, RecordingCanvas& canvas) {
621        canvas.save(SaveFlags::MatrixClip);
622        canvas.clipRect(50, 50, 150, 150, SkRegion::kIntersect_Op);
623        canvas.drawLayer(layerUpdater.get());
624        canvas.restore();
625    });
626
627    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
628            sLightGeometry, Caches::getInstance());
629    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
630
631    TextureLayerClipLocalMatrixTestRenderer renderer;
632    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
633    EXPECT_EQ(1, renderer.getIndex());
634}
635
636RENDERTHREAD_TEST(FrameBuilder, textureLayer_combineMatrices) {
637    class TextureLayerCombineMatricesTestRenderer : public TestRendererBase {
638    public:
639        void onTextureLayerOp(const TextureLayerOp& op, const BakedOpState& state) override {
640            EXPECT_EQ(0, mIndex++);
641
642            Matrix4 expected;
643            expected.loadTranslate(35, 45, 0);
644            EXPECT_MATRIX_APPROX_EQ(expected, state.computedState.transform);
645        }
646    };
647
648    auto layerUpdater = TestUtils::createTextureLayerUpdater(renderThread, 100, 100,
649            SkMatrix::MakeTrans(5, 5));
650
651    auto node = TestUtils::createNode(0, 0, 200, 200,
652            [&layerUpdater](RenderProperties& props, RecordingCanvas& canvas) {
653        canvas.save(SaveFlags::MatrixClip);
654        canvas.translate(30, 40);
655        canvas.drawLayer(layerUpdater.get());
656        canvas.restore();
657    });
658
659    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
660            sLightGeometry, Caches::getInstance());
661    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
662
663    TextureLayerCombineMatricesTestRenderer renderer;
664    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
665    EXPECT_EQ(1, renderer.getIndex());
666}
667
668RENDERTHREAD_TEST(FrameBuilder, textureLayer_reject) {
669    auto layerUpdater = TestUtils::createTextureLayerUpdater(renderThread, 100, 100,
670            SkMatrix::MakeTrans(5, 5));
671    layerUpdater->backingLayer()->setRenderTarget(GL_NONE); // Should be rejected
672
673    auto node = TestUtils::createNode(0, 0, 200, 200,
674            [&layerUpdater](RenderProperties& props, RecordingCanvas& canvas) {
675        canvas.drawLayer(layerUpdater.get());
676    });
677
678    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
679            sLightGeometry, Caches::getInstance());
680    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
681
682    FailRenderer renderer;
683    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
684}
685
686RENDERTHREAD_TEST(FrameBuilder, functor_reject) {
687    class FunctorTestRenderer : public TestRendererBase {
688    public:
689        void onFunctorOp(const FunctorOp& op, const BakedOpState& state) override {
690            EXPECT_EQ(0, mIndex++);
691        }
692    };
693    Functor noopFunctor;
694
695    // 1 million pixel tall view, scrolled down 80%
696    auto scrolledFunctorView = TestUtils::createNode(0, 0, 400, 1000000,
697            [&noopFunctor](RenderProperties& props, RecordingCanvas& canvas) {
698        canvas.translate(0, -800000);
699        canvas.callDrawGLFunction(&noopFunctor, nullptr);
700    });
701
702    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
703            sLightGeometry, Caches::getInstance());
704    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(scrolledFunctorView));
705
706    FunctorTestRenderer renderer;
707    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
708    EXPECT_EQ(1, renderer.getIndex()) << "Functor should not be rejected";
709}
710
711RENDERTHREAD_TEST(FrameBuilder, deferColorOp_unbounded) {
712    class ColorTestRenderer : public TestRendererBase {
713    public:
714        void onColorOp(const ColorOp& op, const BakedOpState& state) override {
715            EXPECT_EQ(0, mIndex++);
716            EXPECT_EQ(Rect(200, 200), state.computedState.clippedBounds)
717                    << "Color op should be expanded to bounds of surrounding";
718        }
719    };
720
721    auto unclippedColorView = TestUtils::createNode(0, 0, 10, 10,
722            [](RenderProperties& props, RecordingCanvas& canvas) {
723        props.setClipToBounds(false);
724        canvas.drawColor(SK_ColorWHITE, SkXfermode::Mode::kSrcOver_Mode);
725    });
726
727    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
728            sLightGeometry, Caches::getInstance());
729    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(unclippedColorView));
730
731    ColorTestRenderer renderer;
732    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
733    EXPECT_EQ(1, renderer.getIndex()) << "ColorOp should not be rejected";
734}
735
736TEST(FrameBuilder, renderNode) {
737    class RenderNodeTestRenderer : public TestRendererBase {
738    public:
739        void onRectOp(const RectOp& op, const BakedOpState& state) override {
740            switch(mIndex++) {
741            case 0:
742                EXPECT_EQ(Rect(200, 200), state.computedState.clippedBounds);
743                EXPECT_EQ(SK_ColorDKGRAY, op.paint->getColor());
744                break;
745            case 1:
746                EXPECT_EQ(Rect(50, 50, 150, 150), state.computedState.clippedBounds);
747                EXPECT_EQ(SK_ColorWHITE, op.paint->getColor());
748                break;
749            default:
750                ADD_FAILURE();
751            }
752        }
753    };
754
755    auto child = TestUtils::createNode(10, 10, 110, 110,
756            [](RenderProperties& props, RecordingCanvas& canvas) {
757        SkPaint paint;
758        paint.setColor(SK_ColorWHITE);
759        canvas.drawRect(0, 0, 100, 100, paint);
760    });
761
762    auto parent = TestUtils::createNode(0, 0, 200, 200,
763            [&child](RenderProperties& props, RecordingCanvas& canvas) {
764        SkPaint paint;
765        paint.setColor(SK_ColorDKGRAY);
766        canvas.drawRect(0, 0, 200, 200, paint);
767
768        canvas.save(SaveFlags::MatrixClip);
769        canvas.translate(40, 40);
770        canvas.drawRenderNode(child.get());
771        canvas.restore();
772    });
773
774    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
775            sLightGeometry, Caches::getInstance());
776    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent));
777
778    RenderNodeTestRenderer renderer;
779    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
780    EXPECT_EQ(2, renderer.getIndex());
781}
782
783RENDERTHREAD_TEST(FrameBuilder, clipped) {
784    class ClippedTestRenderer : public TestRendererBase {
785    public:
786        void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override {
787            EXPECT_EQ(0, mIndex++);
788            EXPECT_EQ(Rect(10, 20, 30, 40), state.computedState.clippedBounds);
789            EXPECT_EQ(Rect(10, 20, 30, 40), state.computedState.clipRect());
790            EXPECT_TRUE(state.computedState.transform.isIdentity());
791        }
792    };
793
794    auto node = TestUtils::createNode(0, 0, 200, 200,
795            [](RenderProperties& props, RecordingCanvas& canvas) {
796        SkBitmap bitmap = TestUtils::createSkBitmap(200, 200);
797        canvas.drawBitmap(bitmap, 0, 0, nullptr);
798    });
799
800    // clip to small area, should see in receiver
801    FrameBuilder frameBuilder(SkRect::MakeLTRB(10, 20, 30, 40), 200, 200,
802            sLightGeometry, Caches::getInstance());
803    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
804
805    ClippedTestRenderer renderer;
806    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
807}
808
809RENDERTHREAD_TEST(FrameBuilder, saveLayer_simple) {
810    class SaveLayerSimpleTestRenderer : public TestRendererBase {
811    public:
812        OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override {
813            EXPECT_EQ(0, mIndex++);
814            EXPECT_EQ(180u, width);
815            EXPECT_EQ(180u, height);
816            return nullptr;
817        }
818        void endLayer() override {
819            EXPECT_EQ(2, mIndex++);
820        }
821        void onRectOp(const RectOp& op, const BakedOpState& state) override {
822            EXPECT_EQ(1, mIndex++);
823            EXPECT_EQ(Rect(10, 10, 190, 190), op.unmappedBounds);
824            EXPECT_EQ(Rect(180, 180), state.computedState.clippedBounds);
825            EXPECT_EQ(Rect(180, 180), state.computedState.clipRect());
826
827            Matrix4 expectedTransform;
828            expectedTransform.loadTranslate(-10, -10, 0);
829            EXPECT_MATRIX_APPROX_EQ(expectedTransform, state.computedState.transform);
830        }
831        void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
832            EXPECT_EQ(3, mIndex++);
833            EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds);
834            EXPECT_EQ(Rect(200, 200), state.computedState.clipRect());
835            EXPECT_TRUE(state.computedState.transform.isIdentity());
836        }
837        void recycleTemporaryLayer(OffscreenBuffer* offscreenBuffer) override {
838            EXPECT_EQ(4, mIndex++);
839            EXPECT_EQ(nullptr, offscreenBuffer);
840        }
841    };
842
843    auto node = TestUtils::createNode(0, 0, 200, 200,
844            [](RenderProperties& props, RecordingCanvas& canvas) {
845        canvas.saveLayerAlpha(10, 10, 190, 190, 128, SaveFlags::ClipToLayer);
846        canvas.drawRect(10, 10, 190, 190, SkPaint());
847        canvas.restore();
848    });
849
850    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
851            sLightGeometry, Caches::getInstance());
852    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
853
854    SaveLayerSimpleTestRenderer renderer;
855    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
856    EXPECT_EQ(5, renderer.getIndex());
857}
858
859RENDERTHREAD_TEST(FrameBuilder, saveLayer_nested) {
860    /* saveLayer1 { rect1, saveLayer2 { rect2 } } will play back as:
861     * - startTemporaryLayer2, rect2 endLayer2
862     * - startTemporaryLayer1, rect1, drawLayer2, endLayer1
863     * - startFrame, layerOp1, endFrame
864     */
865    class SaveLayerNestedTestRenderer : public TestRendererBase {
866    public:
867        OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override {
868            const int index = mIndex++;
869            if (index == 0) {
870                EXPECT_EQ(400u, width);
871                EXPECT_EQ(400u, height);
872                return (OffscreenBuffer*) 0x400;
873            } else if (index == 3) {
874                EXPECT_EQ(800u, width);
875                EXPECT_EQ(800u, height);
876                return (OffscreenBuffer*) 0x800;
877            } else { ADD_FAILURE(); }
878            return (OffscreenBuffer*) nullptr;
879        }
880        void endLayer() override {
881            int index = mIndex++;
882            EXPECT_TRUE(index == 2 || index == 6);
883        }
884        void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
885            EXPECT_EQ(7, mIndex++);
886        }
887        void endFrame(const Rect& repaintRect) override {
888            EXPECT_EQ(9, mIndex++);
889        }
890        void onRectOp(const RectOp& op, const BakedOpState& state) override {
891            const int index = mIndex++;
892            if (index == 1) {
893                EXPECT_EQ(Rect(400, 400), op.unmappedBounds); // inner rect
894            } else if (index == 4) {
895                EXPECT_EQ(Rect(800, 800), op.unmappedBounds); // outer rect
896            } else { ADD_FAILURE(); }
897        }
898        void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
899            const int index = mIndex++;
900            if (index == 5) {
901                EXPECT_EQ((OffscreenBuffer*)0x400, *op.layerHandle);
902                EXPECT_EQ(Rect(400, 400), op.unmappedBounds); // inner layer
903            } else if (index == 8) {
904                EXPECT_EQ((OffscreenBuffer*)0x800, *op.layerHandle);
905                EXPECT_EQ(Rect(800, 800), op.unmappedBounds); // outer layer
906            } else { ADD_FAILURE(); }
907        }
908        void recycleTemporaryLayer(OffscreenBuffer* offscreenBuffer) override {
909            const int index = mIndex++;
910            // order isn't important, but we need to see both
911            if (index == 10) {
912                EXPECT_EQ((OffscreenBuffer*)0x400, offscreenBuffer);
913            } else if (index == 11) {
914                EXPECT_EQ((OffscreenBuffer*)0x800, offscreenBuffer);
915            } else { ADD_FAILURE(); }
916        }
917    };
918
919    auto node = TestUtils::createNode(0, 0, 800, 800,
920            [](RenderProperties& props, RecordingCanvas& canvas) {
921        canvas.saveLayerAlpha(0, 0, 800, 800, 128, SaveFlags::ClipToLayer);
922        {
923            canvas.drawRect(0, 0, 800, 800, SkPaint());
924            canvas.saveLayerAlpha(0, 0, 400, 400, 128, SaveFlags::ClipToLayer);
925            {
926                canvas.drawRect(0, 0, 400, 400, SkPaint());
927            }
928            canvas.restore();
929        }
930        canvas.restore();
931    });
932
933    FrameBuilder frameBuilder(SkRect::MakeWH(800, 800), 800, 800,
934            sLightGeometry, Caches::getInstance());
935    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
936
937    SaveLayerNestedTestRenderer renderer;
938    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
939    EXPECT_EQ(12, renderer.getIndex());
940}
941
942RENDERTHREAD_TEST(FrameBuilder, saveLayer_contentRejection) {
943        auto node = TestUtils::createNode(0, 0, 200, 200,
944                [](RenderProperties& props, RecordingCanvas& canvas) {
945        canvas.save(SaveFlags::MatrixClip);
946        canvas.clipRect(200, 200, 400, 400, SkRegion::kIntersect_Op);
947        canvas.saveLayerAlpha(200, 200, 400, 400, 128, SaveFlags::ClipToLayer);
948
949        // draw within save layer may still be recorded, but shouldn't be drawn
950        canvas.drawRect(200, 200, 400, 400, SkPaint());
951
952        canvas.restore();
953        canvas.restore();
954    });
955
956    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
957            sLightGeometry, Caches::getInstance());
958    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
959
960    FailRenderer renderer;
961    // should see no ops, even within the layer, since the layer should be rejected
962    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
963}
964
965RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_simple) {
966    class SaveLayerUnclippedSimpleTestRenderer : public TestRendererBase {
967    public:
968        void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override {
969            EXPECT_EQ(0, mIndex++);
970            EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds);
971            EXPECT_CLIP_RECT(Rect(200, 200), state.computedState.clipState);
972            EXPECT_TRUE(state.computedState.transform.isIdentity());
973        }
974        void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override {
975            EXPECT_EQ(1, mIndex++);
976            ASSERT_NE(nullptr, op.paint);
977            ASSERT_EQ(SkXfermode::kClear_Mode, PaintUtils::getXfermodeDirect(op.paint));
978        }
979        void onRectOp(const RectOp& op, const BakedOpState& state) override {
980            EXPECT_EQ(2, mIndex++);
981            EXPECT_EQ(Rect(200, 200), op.unmappedBounds);
982            EXPECT_EQ(Rect(200, 200), state.computedState.clippedBounds);
983            EXPECT_EQ(Rect(200, 200), state.computedState.clipRect());
984            EXPECT_TRUE(state.computedState.transform.isIdentity());
985        }
986        void onCopyFromLayerOp(const CopyFromLayerOp& op, const BakedOpState& state) override {
987            EXPECT_EQ(3, mIndex++);
988            EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds);
989            EXPECT_CLIP_RECT(Rect(200, 200), state.computedState.clipState);
990            EXPECT_TRUE(state.computedState.transform.isIdentity());
991        }
992    };
993
994    auto node = TestUtils::createNode(0, 0, 200, 200,
995            [](RenderProperties& props, RecordingCanvas& canvas) {
996        canvas.saveLayerAlpha(10, 10, 190, 190, 128, (SaveFlags::Flags)(0));
997        canvas.drawRect(0, 0, 200, 200, SkPaint());
998        canvas.restore();
999    });
1000
1001    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
1002            sLightGeometry, Caches::getInstance());
1003    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
1004
1005    SaveLayerUnclippedSimpleTestRenderer renderer;
1006    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1007    EXPECT_EQ(4, renderer.getIndex());
1008}
1009
1010RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_round) {
1011    class SaveLayerUnclippedRoundTestRenderer : public TestRendererBase {
1012    public:
1013        void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override {
1014            EXPECT_EQ(0, mIndex++);
1015            EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds)
1016                    << "Bounds rect should round out";
1017        }
1018        void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override {}
1019        void onRectOp(const RectOp& op, const BakedOpState& state) override {}
1020        void onCopyFromLayerOp(const CopyFromLayerOp& op, const BakedOpState& state) override {
1021            EXPECT_EQ(1, mIndex++);
1022            EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds)
1023                    << "Bounds rect should round out";
1024        }
1025    };
1026
1027    auto node = TestUtils::createNode(0, 0, 200, 200,
1028            [](RenderProperties& props, RecordingCanvas& canvas) {
1029        canvas.saveLayerAlpha(10.95f, 10.5f, 189.75f, 189.25f, // values should all round out
1030                128, (SaveFlags::Flags)(0));
1031        canvas.drawRect(0, 0, 200, 200, SkPaint());
1032        canvas.restore();
1033    });
1034
1035    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
1036            sLightGeometry, Caches::getInstance());
1037    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
1038
1039    SaveLayerUnclippedRoundTestRenderer renderer;
1040    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1041    EXPECT_EQ(2, renderer.getIndex());
1042}
1043
1044RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_mergedClears) {
1045    class SaveLayerUnclippedMergedClearsTestRenderer : public TestRendererBase {
1046    public:
1047        void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override {
1048            int index = mIndex++;
1049            EXPECT_GT(4, index);
1050            EXPECT_EQ(5, op.unmappedBounds.getWidth());
1051            EXPECT_EQ(5, op.unmappedBounds.getHeight());
1052            if (index == 0) {
1053                EXPECT_EQ(Rect(10, 10), state.computedState.clippedBounds);
1054            } else if (index == 1) {
1055                EXPECT_EQ(Rect(190, 0, 200, 10), state.computedState.clippedBounds);
1056            } else if (index == 2) {
1057                EXPECT_EQ(Rect(0, 190, 10, 200), state.computedState.clippedBounds);
1058            } else if (index == 3) {
1059                EXPECT_EQ(Rect(190, 190, 200, 200), state.computedState.clippedBounds);
1060            }
1061        }
1062        void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override {
1063            EXPECT_EQ(4, mIndex++);
1064            ASSERT_EQ(op.vertexCount, 16u);
1065            for (size_t i = 0; i < op.vertexCount; i++) {
1066                auto v = op.vertices[i];
1067                EXPECT_TRUE(v.x == 0 || v.x == 10 || v.x == 190 || v.x == 200);
1068                EXPECT_TRUE(v.y == 0 || v.y == 10 || v.y == 190 || v.y == 200);
1069            }
1070        }
1071        void onRectOp(const RectOp& op, const BakedOpState& state) override {
1072            EXPECT_EQ(5, mIndex++);
1073        }
1074        void onCopyFromLayerOp(const CopyFromLayerOp& op, const BakedOpState& state) override {
1075            EXPECT_LT(5, mIndex++);
1076        }
1077    };
1078
1079    auto node = TestUtils::createNode(0, 0, 200, 200,
1080            [](RenderProperties& props, RecordingCanvas& canvas) {
1081
1082        int restoreTo = canvas.save(SaveFlags::MatrixClip);
1083        canvas.scale(2, 2);
1084        canvas.saveLayerAlpha(0, 0, 5, 5, 128, SaveFlags::MatrixClip);
1085        canvas.saveLayerAlpha(95, 0, 100, 5, 128, SaveFlags::MatrixClip);
1086        canvas.saveLayerAlpha(0, 95, 5, 100, 128, SaveFlags::MatrixClip);
1087        canvas.saveLayerAlpha(95, 95, 100, 100, 128, SaveFlags::MatrixClip);
1088        canvas.drawRect(0, 0, 100, 100, SkPaint());
1089        canvas.restoreToCount(restoreTo);
1090    });
1091
1092    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
1093            sLightGeometry, Caches::getInstance());
1094    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
1095
1096    SaveLayerUnclippedMergedClearsTestRenderer renderer;
1097    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1098    EXPECT_EQ(10, renderer.getIndex())
1099            << "Expect 4 copyTos, 4 copyFroms, 1 clear SimpleRects, and 1 rect.";
1100}
1101
1102RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_clearClip) {
1103    class SaveLayerUnclippedClearClipTestRenderer : public TestRendererBase {
1104    public:
1105        void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override {
1106            EXPECT_EQ(0, mIndex++);
1107        }
1108        void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override {
1109            EXPECT_EQ(1, mIndex++);
1110            ASSERT_NE(nullptr, op.paint);
1111            EXPECT_EQ(SkXfermode::kClear_Mode, PaintUtils::getXfermodeDirect(op.paint));
1112            EXPECT_EQ(Rect(50, 50, 150, 150), state.computedState.clippedBounds)
1113                    << "Expect dirty rect as clip";
1114            ASSERT_NE(nullptr, state.computedState.clipState);
1115            EXPECT_EQ(Rect(50, 50, 150, 150), state.computedState.clipState->rect);
1116            EXPECT_EQ(ClipMode::Rectangle, state.computedState.clipState->mode);
1117        }
1118        void onRectOp(const RectOp& op, const BakedOpState& state) override {
1119            EXPECT_EQ(2, mIndex++);
1120        }
1121        void onCopyFromLayerOp(const CopyFromLayerOp& op, const BakedOpState& state) override {
1122            EXPECT_EQ(3, mIndex++);
1123        }
1124    };
1125
1126    auto node = TestUtils::createNode(0, 0, 200, 200,
1127            [](RenderProperties& props, RecordingCanvas& canvas) {
1128        // save smaller than clip, so we get unclipped behavior
1129        canvas.saveLayerAlpha(10, 10, 190, 190, 128, (SaveFlags::Flags)(0));
1130        canvas.drawRect(0, 0, 200, 200, SkPaint());
1131        canvas.restore();
1132    });
1133
1134    // draw with partial screen dirty, and assert we see that rect later
1135    FrameBuilder frameBuilder(SkRect::MakeLTRB(50, 50, 150, 150), 200, 200,
1136            sLightGeometry, Caches::getInstance());
1137    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
1138
1139    SaveLayerUnclippedClearClipTestRenderer renderer;
1140    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1141    EXPECT_EQ(4, renderer.getIndex());
1142}
1143
1144RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_reject) {
1145    auto node = TestUtils::createNode(0, 0, 200, 200,
1146            [](RenderProperties& props, RecordingCanvas& canvas) {
1147        // unclipped savelayer + rect both in area that won't intersect with dirty
1148        canvas.saveLayerAlpha(100, 100, 200, 200, 128, (SaveFlags::Flags)(0));
1149        canvas.drawRect(100, 100, 200, 200, SkPaint());
1150        canvas.restore();
1151    });
1152
1153    // draw with partial screen dirty that doesn't intersect with savelayer
1154    FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 200, 200,
1155            sLightGeometry, Caches::getInstance());
1156    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
1157
1158    FailRenderer renderer;
1159    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1160}
1161
1162/* saveLayerUnclipped { saveLayer { saveLayerUnclipped { rect } } } will play back as:
1163 * - startTemporaryLayer, onCopyToLayer, onSimpleRects, onRect, onCopyFromLayer, endLayer
1164 * - startFrame, onCopyToLayer, onSimpleRects, drawLayer, onCopyFromLayer, endframe
1165 */
1166RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_complex) {
1167    class SaveLayerUnclippedComplexTestRenderer : public TestRendererBase {
1168    public:
1169        OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) {
1170            EXPECT_EQ(0, mIndex++); // savelayer first
1171            return (OffscreenBuffer*)0xabcd;
1172        }
1173        void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override {
1174            int index = mIndex++;
1175            EXPECT_TRUE(index == 1 || index == 7);
1176        }
1177        void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override {
1178            int index = mIndex++;
1179            EXPECT_TRUE(index == 2 || index == 8);
1180        }
1181        void onRectOp(const RectOp& op, const BakedOpState& state) override {
1182            EXPECT_EQ(3, mIndex++);
1183            Matrix4 expected;
1184            expected.loadTranslate(-100, -100, 0);
1185            EXPECT_EQ(Rect(100, 100, 200, 200), state.computedState.clippedBounds);
1186            EXPECT_MATRIX_APPROX_EQ(expected, state.computedState.transform);
1187        }
1188        void onCopyFromLayerOp(const CopyFromLayerOp& op, const BakedOpState& state) override {
1189            int index = mIndex++;
1190            EXPECT_TRUE(index == 4 || index == 10);
1191        }
1192        void endLayer() override {
1193            EXPECT_EQ(5, mIndex++);
1194        }
1195        void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
1196            EXPECT_EQ(6, mIndex++);
1197        }
1198        void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
1199            EXPECT_EQ(9, mIndex++);
1200            EXPECT_EQ((OffscreenBuffer*)0xabcd, *op.layerHandle);
1201        }
1202        void endFrame(const Rect& repaintRect) override {
1203            EXPECT_EQ(11, mIndex++);
1204        }
1205        void recycleTemporaryLayer(OffscreenBuffer* offscreenBuffer) override {
1206            EXPECT_EQ(12, mIndex++);
1207            EXPECT_EQ((OffscreenBuffer*)0xabcd, offscreenBuffer);
1208        }
1209    };
1210
1211    auto node = TestUtils::createNode(0, 0, 600, 600, // 500x500 triggers clipping
1212            [](RenderProperties& props, RecordingCanvas& canvas) {
1213        canvas.saveLayerAlpha(0, 0, 500, 500, 128, (SaveFlags::Flags)0); // unclipped
1214        canvas.saveLayerAlpha(100, 100, 400, 400, 128, SaveFlags::ClipToLayer); // clipped
1215        canvas.saveLayerAlpha(200, 200, 300, 300, 128, (SaveFlags::Flags)0); // unclipped
1216        canvas.drawRect(200, 200, 300, 300, SkPaint());
1217        canvas.restore();
1218        canvas.restore();
1219        canvas.restore();
1220    });
1221
1222    FrameBuilder frameBuilder(SkRect::MakeWH(600, 600), 600, 600,
1223            sLightGeometry, Caches::getInstance());
1224    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
1225
1226    SaveLayerUnclippedComplexTestRenderer renderer;
1227    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1228    EXPECT_EQ(13, renderer.getIndex());
1229}
1230
1231RENDERTHREAD_TEST(FrameBuilder, hwLayer_simple) {
1232    class HwLayerSimpleTestRenderer : public TestRendererBase {
1233    public:
1234        void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override {
1235            EXPECT_EQ(0, mIndex++);
1236            EXPECT_EQ(100u, offscreenBuffer->viewportWidth);
1237            EXPECT_EQ(100u, offscreenBuffer->viewportHeight);
1238            EXPECT_EQ(Rect(25, 25, 75, 75), repaintRect);
1239        }
1240        void onRectOp(const RectOp& op, const BakedOpState& state) override {
1241            EXPECT_EQ(1, mIndex++);
1242
1243            EXPECT_TRUE(state.computedState.transform.isIdentity())
1244                    << "Transform should be reset within layer";
1245
1246            EXPECT_EQ(Rect(25, 25, 75, 75), state.computedState.clipRect())
1247                    << "Damage rect should be used to clip layer content";
1248        }
1249        void endLayer() override {
1250            EXPECT_EQ(2, mIndex++);
1251        }
1252        void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
1253            EXPECT_EQ(3, mIndex++);
1254        }
1255        void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
1256            EXPECT_EQ(4, mIndex++);
1257        }
1258        void endFrame(const Rect& repaintRect) override {
1259            EXPECT_EQ(5, mIndex++);
1260        }
1261    };
1262
1263    auto node = TestUtils::createNode(10, 10, 110, 110,
1264            [](RenderProperties& props, RecordingCanvas& canvas) {
1265        props.mutateLayerProperties().setType(LayerType::RenderLayer);
1266        SkPaint paint;
1267        paint.setColor(SK_ColorWHITE);
1268        canvas.drawRect(0, 0, 100, 100, paint);
1269    });
1270    OffscreenBuffer** layerHandle = node->getLayerHandle();
1271
1272    // create RenderNode's layer here in same way prepareTree would
1273    OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 100, 100);
1274    *layerHandle = &layer;
1275
1276    auto syncedNode = TestUtils::getSyncedNode(node);
1277
1278    // only enqueue partial damage
1279    LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
1280    layerUpdateQueue.enqueueLayerWithDamage(node.get(), Rect(25, 25, 75, 75));
1281
1282    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
1283            sLightGeometry, Caches::getInstance());
1284    frameBuilder.deferLayers(layerUpdateQueue);
1285    frameBuilder.deferRenderNode(*syncedNode);
1286
1287    HwLayerSimpleTestRenderer renderer;
1288    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1289    EXPECT_EQ(6, renderer.getIndex());
1290
1291    // clean up layer pointer, so we can safely destruct RenderNode
1292    *layerHandle = nullptr;
1293}
1294
1295RENDERTHREAD_TEST(FrameBuilder, hwLayer_complex) {
1296    /* parentLayer { greyRect, saveLayer { childLayer { whiteRect } } } will play back as:
1297     * - startRepaintLayer(child), rect(grey), endLayer
1298     * - startTemporaryLayer, drawLayer(child), endLayer
1299     * - startRepaintLayer(parent), rect(white), drawLayer(saveLayer), endLayer
1300     * - startFrame, drawLayer(parent), endLayerb
1301     */
1302    class HwLayerComplexTestRenderer : public TestRendererBase {
1303    public:
1304        OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) {
1305            EXPECT_EQ(3, mIndex++); // savelayer first
1306            return (OffscreenBuffer*)0xabcd;
1307        }
1308        void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override {
1309            int index = mIndex++;
1310            if (index == 0) {
1311                // starting inner layer
1312                EXPECT_EQ(100u, offscreenBuffer->viewportWidth);
1313                EXPECT_EQ(100u, offscreenBuffer->viewportHeight);
1314            } else if (index == 6) {
1315                // starting outer layer
1316                EXPECT_EQ(200u, offscreenBuffer->viewportWidth);
1317                EXPECT_EQ(200u, offscreenBuffer->viewportHeight);
1318            } else { ADD_FAILURE(); }
1319        }
1320        void onRectOp(const RectOp& op, const BakedOpState& state) override {
1321            int index = mIndex++;
1322            if (index == 1) {
1323                // inner layer's rect (white)
1324                EXPECT_EQ(SK_ColorWHITE, op.paint->getColor());
1325            } else if (index == 7) {
1326                // outer layer's rect (grey)
1327                EXPECT_EQ(SK_ColorDKGRAY, op.paint->getColor());
1328            } else { ADD_FAILURE(); }
1329        }
1330        void endLayer() override {
1331            int index = mIndex++;
1332            EXPECT_TRUE(index == 2 || index == 5 || index == 9);
1333        }
1334        void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
1335            EXPECT_EQ(10, mIndex++);
1336        }
1337        void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
1338            OffscreenBuffer* layer = *op.layerHandle;
1339            int index = mIndex++;
1340            if (index == 4) {
1341                EXPECT_EQ(100u, layer->viewportWidth);
1342                EXPECT_EQ(100u, layer->viewportHeight);
1343            } else if (index == 8) {
1344                EXPECT_EQ((OffscreenBuffer*)0xabcd, *op.layerHandle);
1345            } else if (index == 11) {
1346                EXPECT_EQ(200u, layer->viewportWidth);
1347                EXPECT_EQ(200u, layer->viewportHeight);
1348            } else { ADD_FAILURE(); }
1349        }
1350        void endFrame(const Rect& repaintRect) override {
1351            EXPECT_EQ(12, mIndex++);
1352        }
1353        void recycleTemporaryLayer(OffscreenBuffer* offscreenBuffer) override {
1354            EXPECT_EQ(13, mIndex++);
1355        }
1356    };
1357
1358    auto child = TestUtils::createNode(50, 50, 150, 150,
1359            [](RenderProperties& props, RecordingCanvas& canvas) {
1360        props.mutateLayerProperties().setType(LayerType::RenderLayer);
1361        SkPaint paint;
1362        paint.setColor(SK_ColorWHITE);
1363        canvas.drawRect(0, 0, 100, 100, paint);
1364    });
1365    OffscreenBuffer childLayer(renderThread.renderState(), Caches::getInstance(), 100, 100);
1366    *(child->getLayerHandle()) = &childLayer;
1367
1368    RenderNode* childPtr = child.get();
1369    auto parent = TestUtils::createNode(0, 0, 200, 200,
1370            [childPtr](RenderProperties& props, RecordingCanvas& canvas) {
1371        props.mutateLayerProperties().setType(LayerType::RenderLayer);
1372        SkPaint paint;
1373        paint.setColor(SK_ColorDKGRAY);
1374        canvas.drawRect(0, 0, 200, 200, paint);
1375
1376        canvas.saveLayerAlpha(50, 50, 150, 150, 128, SaveFlags::ClipToLayer);
1377        canvas.drawRenderNode(childPtr);
1378        canvas.restore();
1379    });
1380    OffscreenBuffer parentLayer(renderThread.renderState(), Caches::getInstance(), 200, 200);
1381    *(parent->getLayerHandle()) = &parentLayer;
1382
1383    auto syncedNode = TestUtils::getSyncedNode(parent);
1384
1385    LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
1386    layerUpdateQueue.enqueueLayerWithDamage(child.get(), Rect(100, 100));
1387    layerUpdateQueue.enqueueLayerWithDamage(parent.get(), Rect(200, 200));
1388
1389    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
1390            sLightGeometry, Caches::getInstance());
1391    frameBuilder.deferLayers(layerUpdateQueue);
1392    frameBuilder.deferRenderNode(*syncedNode);
1393
1394    HwLayerComplexTestRenderer renderer;
1395    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1396    EXPECT_EQ(14, renderer.getIndex());
1397
1398    // clean up layer pointers, so we can safely destruct RenderNodes
1399    *(child->getLayerHandle()) = nullptr;
1400    *(parent->getLayerHandle()) = nullptr;
1401}
1402
1403
1404RENDERTHREAD_TEST(FrameBuilder, buildLayer) {
1405    class BuildLayerTestRenderer : public TestRendererBase {
1406    public:
1407        void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override {
1408            EXPECT_EQ(0, mIndex++);
1409            EXPECT_EQ(100u, offscreenBuffer->viewportWidth);
1410            EXPECT_EQ(100u, offscreenBuffer->viewportHeight);
1411            EXPECT_EQ(Rect(25, 25, 75, 75), repaintRect);
1412        }
1413        void onColorOp(const ColorOp& op, const BakedOpState& state) override {
1414            EXPECT_EQ(1, mIndex++);
1415
1416            EXPECT_TRUE(state.computedState.transform.isIdentity())
1417                    << "Transform should be reset within layer";
1418
1419            EXPECT_EQ(Rect(25, 25, 75, 75), state.computedState.clipRect())
1420                    << "Damage rect should be used to clip layer content";
1421        }
1422        void endLayer() override {
1423            EXPECT_EQ(2, mIndex++);
1424        }
1425        void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
1426            ADD_FAILURE() << "Primary frame draw not expected in this test";
1427        }
1428        void endFrame(const Rect& repaintRect) override {
1429            ADD_FAILURE() << "Primary frame draw not expected in this test";
1430        }
1431    };
1432
1433    auto node = TestUtils::createNode(10, 10, 110, 110,
1434            [](RenderProperties& props, RecordingCanvas& canvas) {
1435        props.mutateLayerProperties().setType(LayerType::RenderLayer);
1436        canvas.drawColor(SK_ColorWHITE, SkXfermode::Mode::kSrcOver_Mode);
1437    });
1438    OffscreenBuffer** layerHandle = node->getLayerHandle();
1439
1440    // create RenderNode's layer here in same way prepareTree would
1441    OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 100, 100);
1442    *layerHandle = &layer;
1443
1444    TestUtils::syncHierarchyPropertiesAndDisplayList(node);
1445
1446    // only enqueue partial damage
1447    LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
1448    layerUpdateQueue.enqueueLayerWithDamage(node.get(), Rect(25, 25, 75, 75));
1449
1450    // Draw, but pass empty node list, so no work is done for primary frame
1451    FrameBuilder frameBuilder(layerUpdateQueue, sLightGeometry, Caches::getInstance());
1452    BuildLayerTestRenderer renderer;
1453    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1454    EXPECT_EQ(3, renderer.getIndex());
1455
1456    // clean up layer pointer, so we can safely destruct RenderNode
1457    *layerHandle = nullptr;
1458}
1459
1460static void drawOrderedRect(RecordingCanvas* canvas, uint8_t expectedDrawOrder) {
1461    SkPaint paint;
1462    // order put in blue channel, transparent so overlapped content doesn't get rejected
1463    paint.setColor(SkColorSetARGB(1, 0, 0, expectedDrawOrder));
1464    canvas->drawRect(0, 0, 100, 100, paint);
1465}
1466static void drawOrderedNode(RecordingCanvas* canvas, uint8_t expectedDrawOrder, float z) {
1467    auto node = TestUtils::createNode(0, 0, 100, 100,
1468            [expectedDrawOrder](RenderProperties& props, RecordingCanvas& canvas) {
1469        drawOrderedRect(&canvas, expectedDrawOrder);
1470    });
1471    node->mutateStagingProperties().setTranslationZ(z);
1472    node->setPropertyFieldsDirty(RenderNode::TRANSLATION_Z);
1473    canvas->drawRenderNode(node.get()); // canvas takes reference/sole ownership
1474}
1475RENDERTHREAD_TEST(FrameBuilder, zReorder) {
1476    class ZReorderTestRenderer : public TestRendererBase {
1477    public:
1478        void onRectOp(const RectOp& op, const BakedOpState& state) override {
1479            int expectedOrder = SkColorGetB(op.paint->getColor()); // extract order from blue channel
1480            EXPECT_EQ(expectedOrder, mIndex++) << "An op was drawn out of order";
1481        }
1482    };
1483
1484    auto parent = TestUtils::createNode(0, 0, 100, 100,
1485            [](RenderProperties& props, RecordingCanvas& canvas) {
1486        drawOrderedNode(&canvas, 0, 10.0f); // in reorder=false at this point, so played inorder
1487        drawOrderedRect(&canvas, 1);
1488        canvas.insertReorderBarrier(true);
1489        drawOrderedNode(&canvas, 6, 2.0f);
1490        drawOrderedRect(&canvas, 3);
1491        drawOrderedNode(&canvas, 4, 0.0f);
1492        drawOrderedRect(&canvas, 5);
1493        drawOrderedNode(&canvas, 2, -2.0f);
1494        drawOrderedNode(&canvas, 7, 2.0f);
1495        canvas.insertReorderBarrier(false);
1496        drawOrderedRect(&canvas, 8);
1497        drawOrderedNode(&canvas, 9, -10.0f); // in reorder=false at this point, so played inorder
1498    });
1499    FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100,
1500            sLightGeometry, Caches::getInstance());
1501    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent));
1502
1503    ZReorderTestRenderer renderer;
1504    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1505    EXPECT_EQ(10, renderer.getIndex());
1506};
1507
1508RENDERTHREAD_TEST(FrameBuilder, projectionReorder) {
1509    static const int scrollX = 5;
1510    static const int scrollY = 10;
1511    class ProjectionReorderTestRenderer : public TestRendererBase {
1512    public:
1513        void onRectOp(const RectOp& op, const BakedOpState& state) override {
1514            const int index = mIndex++;
1515
1516            Matrix4 expectedMatrix;
1517            switch (index) {
1518            case 0:
1519                EXPECT_EQ(Rect(100, 100), op.unmappedBounds);
1520                EXPECT_EQ(SK_ColorWHITE, op.paint->getColor());
1521                expectedMatrix.loadIdentity();
1522                EXPECT_EQ(nullptr, state.computedState.localProjectionPathMask);
1523                break;
1524            case 1:
1525                EXPECT_EQ(Rect(-10, -10, 60, 60), op.unmappedBounds);
1526                EXPECT_EQ(SK_ColorDKGRAY, op.paint->getColor());
1527                expectedMatrix.loadTranslate(50 - scrollX, 50 - scrollY, 0);
1528                ASSERT_NE(nullptr, state.computedState.localProjectionPathMask);
1529                EXPECT_EQ(Rect(-35, -30, 45, 50),
1530                        Rect(state.computedState.localProjectionPathMask->getBounds()));
1531                break;
1532            case 2:
1533                EXPECT_EQ(Rect(100, 50), op.unmappedBounds);
1534                EXPECT_EQ(SK_ColorBLUE, op.paint->getColor());
1535                expectedMatrix.loadTranslate(-scrollX, 50 - scrollY, 0);
1536                EXPECT_EQ(nullptr, state.computedState.localProjectionPathMask);
1537                break;
1538            default:
1539                ADD_FAILURE();
1540            }
1541            EXPECT_EQ(expectedMatrix, state.computedState.transform);
1542        }
1543    };
1544
1545    /**
1546     * Construct a tree of nodes, where the root (A) has a receiver background (B), and a child (C)
1547     * with a projecting child (P) of its own. P would normally draw between B and C's "background"
1548     * draw, but because it is projected backwards, it's drawn in between B and C.
1549     *
1550     * The parent is scrolled by scrollX/scrollY, but this does not affect the background
1551     * (which isn't affected by scroll).
1552     */
1553    auto receiverBackground = TestUtils::createNode(0, 0, 100, 100,
1554            [](RenderProperties& properties, RecordingCanvas& canvas) {
1555        properties.setProjectionReceiver(true);
1556        // scroll doesn't apply to background, so undone via translationX/Y
1557        // NOTE: translationX/Y only! no other transform properties may be set for a proj receiver!
1558        properties.setTranslationX(scrollX);
1559        properties.setTranslationY(scrollY);
1560
1561        SkPaint paint;
1562        paint.setColor(SK_ColorWHITE);
1563        canvas.drawRect(0, 0, 100, 100, paint);
1564    });
1565    auto projectingRipple = TestUtils::createNode(50, 0, 100, 50,
1566            [](RenderProperties& properties, RecordingCanvas& canvas) {
1567        properties.setProjectBackwards(true);
1568        properties.setClipToBounds(false);
1569        SkPaint paint;
1570        paint.setColor(SK_ColorDKGRAY);
1571        canvas.drawRect(-10, -10, 60, 60, paint);
1572    });
1573    auto child = TestUtils::createNode(0, 50, 100, 100,
1574            [&projectingRipple](RenderProperties& properties, RecordingCanvas& canvas) {
1575        SkPaint paint;
1576        paint.setColor(SK_ColorBLUE);
1577        canvas.drawRect(0, 0, 100, 50, paint);
1578        canvas.drawRenderNode(projectingRipple.get());
1579    });
1580    auto parent = TestUtils::createNode(0, 0, 100, 100,
1581            [&receiverBackground, &child](RenderProperties& properties, RecordingCanvas& canvas) {
1582        // Set a rect outline for the projecting ripple to be masked against.
1583        properties.mutableOutline().setRoundRect(10, 10, 90, 90, 5, 1.0f);
1584
1585        canvas.save(SaveFlags::MatrixClip);
1586        canvas.translate(-scrollX, -scrollY); // Apply scroll (note: bg undoes this internally)
1587        canvas.drawRenderNode(receiverBackground.get());
1588        canvas.drawRenderNode(child.get());
1589        canvas.restore();
1590    });
1591
1592    FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100,
1593            sLightGeometry, Caches::getInstance());
1594    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent));
1595
1596    ProjectionReorderTestRenderer renderer;
1597    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1598    EXPECT_EQ(3, renderer.getIndex());
1599}
1600
1601RENDERTHREAD_TEST(FrameBuilder, projectionHwLayer) {
1602    static const int scrollX = 5;
1603    static const int scrollY = 10;
1604    class ProjectionHwLayerTestRenderer : public TestRendererBase {
1605    public:
1606        void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override {
1607            EXPECT_EQ(0, mIndex++);
1608        }
1609        void onArcOp(const ArcOp& op, const BakedOpState& state) override {
1610            EXPECT_EQ(1, mIndex++);
1611            ASSERT_EQ(nullptr, state.computedState.localProjectionPathMask);
1612        }
1613        void endLayer() override {
1614            EXPECT_EQ(2, mIndex++);
1615        }
1616        void onRectOp(const RectOp& op, const BakedOpState& state) override {
1617            EXPECT_EQ(3, mIndex++);
1618            ASSERT_EQ(nullptr, state.computedState.localProjectionPathMask);
1619        }
1620        void onOvalOp(const OvalOp& op, const BakedOpState& state) override {
1621            EXPECT_EQ(4, mIndex++);
1622            ASSERT_NE(nullptr, state.computedState.localProjectionPathMask);
1623            Matrix4 expected;
1624            expected.loadTranslate(100 - scrollX, 100 - scrollY, 0);
1625            EXPECT_EQ(expected, state.computedState.transform);
1626            EXPECT_EQ(Rect(-85, -80, 295, 300),
1627                    Rect(state.computedState.localProjectionPathMask->getBounds()));
1628        }
1629        void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
1630            EXPECT_EQ(5, mIndex++);
1631            ASSERT_EQ(nullptr, state.computedState.localProjectionPathMask);
1632        }
1633    };
1634    auto receiverBackground = TestUtils::createNode(0, 0, 400, 400,
1635            [](RenderProperties& properties, RecordingCanvas& canvas) {
1636        properties.setProjectionReceiver(true);
1637        // scroll doesn't apply to background, so undone via translationX/Y
1638        // NOTE: translationX/Y only! no other transform properties may be set for a proj receiver!
1639        properties.setTranslationX(scrollX);
1640        properties.setTranslationY(scrollY);
1641
1642        canvas.drawRect(0, 0, 400, 400, SkPaint());
1643    });
1644    auto projectingRipple = TestUtils::createNode(0, 0, 200, 200,
1645            [](RenderProperties& properties, RecordingCanvas& canvas) {
1646        properties.setProjectBackwards(true);
1647        properties.setClipToBounds(false);
1648        canvas.drawOval(100, 100, 300, 300, SkPaint()); // drawn mostly out of layer bounds
1649    });
1650    auto child = TestUtils::createNode(100, 100, 300, 300,
1651            [&projectingRipple](RenderProperties& properties, RecordingCanvas& canvas) {
1652        properties.mutateLayerProperties().setType(LayerType::RenderLayer);
1653        canvas.drawRenderNode(projectingRipple.get());
1654        canvas.drawArc(0, 0, 200, 200, 0.0f, 280.0f, true, SkPaint());
1655    });
1656    auto parent = TestUtils::createNode(0, 0, 400, 400,
1657            [&receiverBackground, &child](RenderProperties& properties, RecordingCanvas& canvas) {
1658        // Set a rect outline for the projecting ripple to be masked against.
1659        properties.mutableOutline().setRoundRect(10, 10, 390, 390, 0, 1.0f);
1660        canvas.translate(-scrollX, -scrollY); // Apply scroll (note: bg undoes this internally)
1661        canvas.drawRenderNode(receiverBackground.get());
1662        canvas.drawRenderNode(child.get());
1663    });
1664
1665    OffscreenBuffer** layerHandle = child->getLayerHandle();
1666
1667    // create RenderNode's layer here in same way prepareTree would, setting windowTransform
1668    OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 200, 200);
1669    Matrix4 windowTransform;
1670    windowTransform.loadTranslate(100, 100, 0); // total transform of layer's origin
1671    layer.setWindowTransform(windowTransform);
1672    *layerHandle = &layer;
1673
1674    auto syncedNode = TestUtils::getSyncedNode(parent);
1675
1676    LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
1677    layerUpdateQueue.enqueueLayerWithDamage(child.get(), Rect(200, 200));
1678
1679    FrameBuilder frameBuilder(SkRect::MakeWH(400, 400), 400, 400,
1680            sLightGeometry, Caches::getInstance());
1681    frameBuilder.deferLayers(layerUpdateQueue);
1682    frameBuilder.deferRenderNode(*syncedNode);
1683
1684    ProjectionHwLayerTestRenderer renderer;
1685    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1686    EXPECT_EQ(6, renderer.getIndex());
1687
1688    // clean up layer pointer, so we can safely destruct RenderNode
1689    *layerHandle = nullptr;
1690}
1691
1692RENDERTHREAD_TEST(FrameBuilder, projectionChildScroll) {
1693    static const int scrollX = 500000;
1694    static const int scrollY = 0;
1695    class ProjectionChildScrollTestRenderer : public TestRendererBase {
1696    public:
1697        void onRectOp(const RectOp& op, const BakedOpState& state) override {
1698            EXPECT_EQ(0, mIndex++);
1699            EXPECT_TRUE(state.computedState.transform.isIdentity());
1700        }
1701        void onOvalOp(const OvalOp& op, const BakedOpState& state) override {
1702            EXPECT_EQ(1, mIndex++);
1703            ASSERT_NE(nullptr, state.computedState.clipState);
1704            ASSERT_EQ(ClipMode::Rectangle, state.computedState.clipState->mode);
1705            ASSERT_EQ(Rect(400, 400), state.computedState.clipState->rect);
1706            EXPECT_TRUE(state.computedState.transform.isIdentity());
1707        }
1708    };
1709    auto receiverBackground = TestUtils::createNode(0, 0, 400, 400,
1710            [](RenderProperties& properties, RecordingCanvas& canvas) {
1711        properties.setProjectionReceiver(true);
1712        canvas.drawRect(0, 0, 400, 400, SkPaint());
1713    });
1714    auto projectingRipple = TestUtils::createNode(0, 0, 200, 200,
1715            [](RenderProperties& properties, RecordingCanvas& canvas) {
1716        // scroll doesn't apply to background, so undone via translationX/Y
1717        // NOTE: translationX/Y only! no other transform properties may be set for a proj receiver!
1718        properties.setTranslationX(scrollX);
1719        properties.setTranslationY(scrollY);
1720        properties.setProjectBackwards(true);
1721        properties.setClipToBounds(false);
1722        canvas.drawOval(0, 0, 200, 200, SkPaint());
1723    });
1724    auto child = TestUtils::createNode(0, 0, 400, 400,
1725            [&projectingRipple](RenderProperties& properties, RecordingCanvas& canvas) {
1726        // Record time clip will be ignored by projectee
1727        canvas.clipRect(100, 100, 300, 300, SkRegion::kIntersect_Op);
1728
1729        canvas.translate(-scrollX, -scrollY); // Apply scroll (note: bg undoes this internally)
1730        canvas.drawRenderNode(projectingRipple.get());
1731    });
1732    auto parent = TestUtils::createNode(0, 0, 400, 400,
1733            [&receiverBackground, &child](RenderProperties& properties, RecordingCanvas& canvas) {
1734        canvas.drawRenderNode(receiverBackground.get());
1735        canvas.drawRenderNode(child.get());
1736    });
1737
1738    FrameBuilder frameBuilder(SkRect::MakeWH(400, 400), 400, 400,
1739            sLightGeometry, Caches::getInstance());
1740    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent));
1741
1742    ProjectionChildScrollTestRenderer renderer;
1743    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1744    EXPECT_EQ(2, renderer.getIndex());
1745}
1746
1747// creates a 100x100 shadow casting node with provided translationZ
1748static sp<RenderNode> createWhiteRectShadowCaster(float translationZ) {
1749    return TestUtils::createNode(0, 0, 100, 100,
1750            [translationZ](RenderProperties& properties, RecordingCanvas& canvas) {
1751        properties.setTranslationZ(translationZ);
1752        properties.mutableOutline().setRoundRect(0, 0, 100, 100, 0.0f, 1.0f);
1753        SkPaint paint;
1754        paint.setColor(SK_ColorWHITE);
1755        canvas.drawRect(0, 0, 100, 100, paint);
1756    });
1757}
1758
1759RENDERTHREAD_TEST(FrameBuilder, shadow) {
1760    class ShadowTestRenderer : public TestRendererBase {
1761    public:
1762        void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
1763            EXPECT_EQ(0, mIndex++);
1764            EXPECT_FLOAT_EQ(1.0f, op.casterAlpha);
1765            EXPECT_TRUE(op.shadowTask->casterPerimeter.isRect(nullptr));
1766            EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), op.shadowTask->transformXY);
1767
1768            Matrix4 expectedZ;
1769            expectedZ.loadTranslate(0, 0, 5);
1770            EXPECT_MATRIX_APPROX_EQ(expectedZ, op.shadowTask->transformZ);
1771        }
1772        void onRectOp(const RectOp& op, const BakedOpState& state) override {
1773            EXPECT_EQ(1, mIndex++);
1774        }
1775    };
1776
1777    auto parent = TestUtils::createNode(0, 0, 200, 200,
1778            [](RenderProperties& props, RecordingCanvas& canvas) {
1779        canvas.insertReorderBarrier(true);
1780        canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
1781    });
1782
1783    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
1784            sLightGeometry, Caches::getInstance());
1785    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent));
1786
1787    ShadowTestRenderer renderer;
1788    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1789    EXPECT_EQ(2, renderer.getIndex());
1790}
1791
1792RENDERTHREAD_TEST(FrameBuilder, shadowSaveLayer) {
1793    class ShadowSaveLayerTestRenderer : public TestRendererBase {
1794    public:
1795        OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override {
1796            EXPECT_EQ(0, mIndex++);
1797            return nullptr;
1798        }
1799        void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
1800            EXPECT_EQ(1, mIndex++);
1801            EXPECT_FLOAT_EQ(50, op.shadowTask->lightCenter.x);
1802            EXPECT_FLOAT_EQ(40, op.shadowTask->lightCenter.y);
1803        }
1804        void onRectOp(const RectOp& op, const BakedOpState& state) override {
1805            EXPECT_EQ(2, mIndex++);
1806        }
1807        void endLayer() override {
1808            EXPECT_EQ(3, mIndex++);
1809        }
1810        void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
1811            EXPECT_EQ(4, mIndex++);
1812        }
1813        void recycleTemporaryLayer(OffscreenBuffer* offscreenBuffer) override {
1814            EXPECT_EQ(5, mIndex++);
1815        }
1816    };
1817
1818    auto parent = TestUtils::createNode(0, 0, 200, 200,
1819            [](RenderProperties& props, RecordingCanvas& canvas) {
1820        // save/restore outside of reorderBarrier, so they don't get moved out of place
1821        canvas.translate(20, 10);
1822        int count = canvas.saveLayerAlpha(30, 50, 130, 150, 128, SaveFlags::ClipToLayer);
1823        canvas.insertReorderBarrier(true);
1824        canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
1825        canvas.insertReorderBarrier(false);
1826        canvas.restoreToCount(count);
1827    });
1828
1829    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
1830            (FrameBuilder::LightGeometry) {{ 100, 100, 100 }, 50}, Caches::getInstance());
1831    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent));
1832
1833    ShadowSaveLayerTestRenderer renderer;
1834    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1835    EXPECT_EQ(6, renderer.getIndex());
1836}
1837
1838RENDERTHREAD_TEST(FrameBuilder, shadowHwLayer) {
1839    class ShadowHwLayerTestRenderer : public TestRendererBase {
1840    public:
1841        void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override {
1842            EXPECT_EQ(0, mIndex++);
1843        }
1844        void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
1845            EXPECT_EQ(1, mIndex++);
1846            EXPECT_FLOAT_EQ(50, op.shadowTask->lightCenter.x);
1847            EXPECT_FLOAT_EQ(40, op.shadowTask->lightCenter.y);
1848            EXPECT_FLOAT_EQ(30, op.shadowTask->lightRadius);
1849        }
1850        void onRectOp(const RectOp& op, const BakedOpState& state) override {
1851            EXPECT_EQ(2, mIndex++);
1852        }
1853        void endLayer() override {
1854            EXPECT_EQ(3, mIndex++);
1855        }
1856        void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
1857            EXPECT_EQ(4, mIndex++);
1858        }
1859    };
1860
1861    auto parent = TestUtils::createNode(50, 60, 150, 160,
1862            [](RenderProperties& props, RecordingCanvas& canvas) {
1863        props.mutateLayerProperties().setType(LayerType::RenderLayer);
1864        canvas.insertReorderBarrier(true);
1865        canvas.save(SaveFlags::MatrixClip);
1866        canvas.translate(20, 10);
1867        canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
1868        canvas.restore();
1869    });
1870    OffscreenBuffer** layerHandle = parent->getLayerHandle();
1871
1872    // create RenderNode's layer here in same way prepareTree would, setting windowTransform
1873    OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 100, 100);
1874    Matrix4 windowTransform;
1875    windowTransform.loadTranslate(50, 60, 0); // total transform of layer's origin
1876    layer.setWindowTransform(windowTransform);
1877    *layerHandle = &layer;
1878
1879    auto syncedNode = TestUtils::getSyncedNode(parent);
1880    LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
1881    layerUpdateQueue.enqueueLayerWithDamage(parent.get(), Rect(100, 100));
1882
1883    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
1884            (FrameBuilder::LightGeometry) {{ 100, 100, 100 }, 30}, Caches::getInstance());
1885    frameBuilder.deferLayers(layerUpdateQueue);
1886    frameBuilder.deferRenderNode(*syncedNode);
1887
1888    ShadowHwLayerTestRenderer renderer;
1889    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1890    EXPECT_EQ(5, renderer.getIndex());
1891
1892    // clean up layer pointer, so we can safely destruct RenderNode
1893    *layerHandle = nullptr;
1894}
1895
1896RENDERTHREAD_TEST(FrameBuilder, shadowLayering) {
1897    class ShadowLayeringTestRenderer : public TestRendererBase {
1898    public:
1899        void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
1900            int index = mIndex++;
1901            EXPECT_TRUE(index == 0 || index == 1);
1902        }
1903        void onRectOp(const RectOp& op, const BakedOpState& state) override {
1904            int index = mIndex++;
1905            EXPECT_TRUE(index == 2 || index == 3);
1906        }
1907    };
1908    auto parent = TestUtils::createNode(0, 0, 200, 200,
1909            [](RenderProperties& props, RecordingCanvas& canvas) {
1910        canvas.insertReorderBarrier(true);
1911        canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
1912        canvas.drawRenderNode(createWhiteRectShadowCaster(5.0001f).get());
1913    });
1914    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
1915            (FrameBuilder::LightGeometry) {{ 100, 100, 100 }, 50}, Caches::getInstance());
1916    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent));
1917
1918    ShadowLayeringTestRenderer renderer;
1919    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1920    EXPECT_EQ(4, renderer.getIndex());
1921}
1922
1923RENDERTHREAD_TEST(FrameBuilder, shadowClipping) {
1924    class ShadowClippingTestRenderer : public TestRendererBase {
1925    public:
1926        void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
1927            EXPECT_EQ(0, mIndex++);
1928            EXPECT_EQ(Rect(25, 25, 75, 75), state.computedState.clipState->rect)
1929                    << "Shadow must respect pre-barrier canvas clip value.";
1930        }
1931        void onRectOp(const RectOp& op, const BakedOpState& state) override {
1932            EXPECT_EQ(1, mIndex++);
1933        }
1934    };
1935    auto parent = TestUtils::createNode(0, 0, 100, 100,
1936            [](RenderProperties& props, RecordingCanvas& canvas) {
1937        // Apply a clip before the reorder barrier/shadow casting child is drawn.
1938        // This clip must be applied to the shadow cast by the child.
1939        canvas.clipRect(25, 25, 75, 75, SkRegion::kIntersect_Op);
1940        canvas.insertReorderBarrier(true);
1941        canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
1942    });
1943
1944    FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100,
1945            (FrameBuilder::LightGeometry) {{ 100, 100, 100 }, 50}, Caches::getInstance());
1946    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent));
1947
1948    ShadowClippingTestRenderer renderer;
1949    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1950    EXPECT_EQ(2, renderer.getIndex());
1951}
1952
1953static void testProperty(std::function<void(RenderProperties&)> propSetupCallback,
1954        std::function<void(const RectOp&, const BakedOpState&)> opValidateCallback) {
1955    class PropertyTestRenderer : public TestRendererBase {
1956    public:
1957        explicit PropertyTestRenderer(std::function<void(const RectOp&, const BakedOpState&)> callback)
1958                : mCallback(callback) {}
1959        void onRectOp(const RectOp& op, const BakedOpState& state) override {
1960            EXPECT_EQ(mIndex++, 0);
1961            mCallback(op, state);
1962        }
1963        std::function<void(const RectOp&, const BakedOpState&)> mCallback;
1964    };
1965
1966    auto node = TestUtils::createNode(0, 0, 100, 100,
1967            [propSetupCallback](RenderProperties& props, RecordingCanvas& canvas) {
1968        propSetupCallback(props);
1969        SkPaint paint;
1970        paint.setColor(SK_ColorWHITE);
1971        canvas.drawRect(0, 0, 100, 100, paint);
1972    });
1973
1974    FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 200, 200,
1975            sLightGeometry, Caches::getInstance());
1976    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
1977
1978    PropertyTestRenderer renderer(opValidateCallback);
1979    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1980    EXPECT_EQ(1, renderer.getIndex()) << "Should have seen one op";
1981}
1982
1983RENDERTHREAD_TEST(FrameBuilder, renderPropOverlappingRenderingAlpha) {
1984    testProperty([](RenderProperties& properties) {
1985        properties.setAlpha(0.5f);
1986        properties.setHasOverlappingRendering(false);
1987    }, [](const RectOp& op, const BakedOpState& state) {
1988        EXPECT_EQ(0.5f, state.alpha) << "Alpha should be applied directly to op";
1989    });
1990}
1991
1992RENDERTHREAD_TEST(FrameBuilder, renderPropClipping) {
1993    testProperty([](RenderProperties& properties) {
1994        properties.setClipToBounds(true);
1995        properties.setClipBounds(Rect(10, 20, 300, 400));
1996    }, [](const RectOp& op, const BakedOpState& state) {
1997        EXPECT_EQ(Rect(10, 20, 100, 100), state.computedState.clippedBounds)
1998                << "Clip rect should be intersection of node bounds and clip bounds";
1999    });
2000}
2001
2002RENDERTHREAD_TEST(FrameBuilder, renderPropRevealClip) {
2003    testProperty([](RenderProperties& properties) {
2004        properties.mutableRevealClip().set(true, 50, 50, 25);
2005    }, [](const RectOp& op, const BakedOpState& state) {
2006        ASSERT_NE(nullptr, state.roundRectClipState);
2007        EXPECT_TRUE(state.roundRectClipState->highPriority);
2008        EXPECT_EQ(25, state.roundRectClipState->radius);
2009        EXPECT_EQ(Rect(50, 50, 50, 50), state.roundRectClipState->innerRect);
2010    });
2011}
2012
2013RENDERTHREAD_TEST(FrameBuilder, renderPropOutlineClip) {
2014    testProperty([](RenderProperties& properties) {
2015        properties.mutableOutline().setShouldClip(true);
2016        properties.mutableOutline().setRoundRect(10, 20, 30, 40, 5.0f, 0.5f);
2017    }, [](const RectOp& op, const BakedOpState& state) {
2018        ASSERT_NE(nullptr, state.roundRectClipState);
2019        EXPECT_FALSE(state.roundRectClipState->highPriority);
2020        EXPECT_EQ(5, state.roundRectClipState->radius);
2021        EXPECT_EQ(Rect(15, 25, 25, 35), state.roundRectClipState->innerRect);
2022    });
2023}
2024
2025RENDERTHREAD_TEST(FrameBuilder, renderPropTransform) {
2026    testProperty([](RenderProperties& properties) {
2027        properties.setLeftTopRightBottom(10, 10, 110, 110);
2028
2029        SkMatrix staticMatrix = SkMatrix::MakeScale(1.2f, 1.2f);
2030        properties.setStaticMatrix(&staticMatrix);
2031
2032        // ignored, since static overrides animation
2033        SkMatrix animationMatrix = SkMatrix::MakeTrans(15, 15);
2034        properties.setAnimationMatrix(&animationMatrix);
2035
2036        properties.setTranslationX(10);
2037        properties.setTranslationY(20);
2038        properties.setScaleX(0.5f);
2039        properties.setScaleY(0.7f);
2040    }, [](const RectOp& op, const BakedOpState& state) {
2041        Matrix4 matrix;
2042        matrix.loadTranslate(10, 10, 0); // left, top
2043        matrix.scale(1.2f, 1.2f, 1); // static matrix
2044        // ignore animation matrix, since static overrides it
2045
2046        // translation xy
2047        matrix.translate(10, 20);
2048
2049        // scale xy (from default pivot - center)
2050        matrix.translate(50, 50);
2051        matrix.scale(0.5f, 0.7f, 1);
2052        matrix.translate(-50, -50);
2053        EXPECT_MATRIX_APPROX_EQ(matrix, state.computedState.transform)
2054                << "Op draw matrix must match expected combination of transformation properties";
2055    });
2056}
2057
2058struct SaveLayerAlphaData {
2059    uint32_t layerWidth = 0;
2060    uint32_t layerHeight = 0;
2061    Rect rectClippedBounds;
2062    Matrix4 rectMatrix;
2063    Matrix4 drawLayerMatrix;
2064};
2065/**
2066 * Constructs a view to hit the temporary layer alpha property implementation:
2067 *     a) 0 < alpha < 1
2068 *     b) too big for layer (larger than maxTextureSize)
2069 *     c) overlapping rendering content
2070 * returning observed data about layer size and content clip/transform.
2071 *
2072 * Used to validate clipping behavior of temporary layer, where requested layer size is reduced
2073 * (for efficiency, and to fit in layer size constraints) based on parent clip.
2074 */
2075void testSaveLayerAlphaClip(SaveLayerAlphaData* outObservedData,
2076        std::function<void(RenderProperties&)> propSetupCallback) {
2077    class SaveLayerAlphaClipTestRenderer : public TestRendererBase {
2078    public:
2079        explicit SaveLayerAlphaClipTestRenderer(SaveLayerAlphaData* outData)
2080                : mOutData(outData) {}
2081
2082        OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override {
2083            EXPECT_EQ(0, mIndex++);
2084            mOutData->layerWidth = width;
2085            mOutData->layerHeight = height;
2086            return nullptr;
2087        }
2088        void onRectOp(const RectOp& op, const BakedOpState& state) override {
2089            EXPECT_EQ(1, mIndex++);
2090
2091            mOutData->rectClippedBounds = state.computedState.clippedBounds;
2092            mOutData->rectMatrix = state.computedState.transform;
2093        }
2094        void endLayer() override {
2095            EXPECT_EQ(2, mIndex++);
2096        }
2097        void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
2098            EXPECT_EQ(3, mIndex++);
2099            mOutData->drawLayerMatrix = state.computedState.transform;
2100        }
2101        void recycleTemporaryLayer(OffscreenBuffer* offscreenBuffer) override {
2102            EXPECT_EQ(4, mIndex++);
2103        }
2104    private:
2105        SaveLayerAlphaData* mOutData;
2106    };
2107
2108    ASSERT_GT(10000, DeviceInfo::get()->maxTextureSize())
2109            << "Node must be bigger than max texture size to exercise saveLayer codepath";
2110    auto node = TestUtils::createNode(0, 0, 10000, 10000,
2111            [&propSetupCallback](RenderProperties& properties, RecordingCanvas& canvas) {
2112        properties.setHasOverlappingRendering(true);
2113        properties.setAlpha(0.5f); // force saveLayer, since too big for HW layer
2114        // apply other properties
2115        propSetupCallback(properties);
2116
2117        SkPaint paint;
2118        paint.setColor(SK_ColorWHITE);
2119        canvas.drawRect(0, 0, 10000, 10000, paint);
2120    });
2121    auto syncedNode = TestUtils::getSyncedNode(node); // sync before querying height
2122
2123    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
2124                sLightGeometry, Caches::getInstance());
2125    frameBuilder.deferRenderNode(*syncedNode);
2126
2127    SaveLayerAlphaClipTestRenderer renderer(outObservedData);
2128    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
2129
2130    // assert, since output won't be valid if we haven't seen a save layer triggered
2131    ASSERT_EQ(5, renderer.getIndex()) << "Test must trigger saveLayer alpha behavior.";
2132}
2133
2134RENDERTHREAD_TEST(FrameBuilder, renderPropSaveLayerAlphaClipBig) {
2135    SaveLayerAlphaData observedData;
2136    testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) {
2137        properties.setTranslationX(10); // offset rendering content
2138        properties.setTranslationY(-2000); // offset rendering content
2139    });
2140    EXPECT_EQ(190u, observedData.layerWidth);
2141    EXPECT_EQ(200u, observedData.layerHeight);
2142    EXPECT_EQ(Rect(190, 200), observedData.rectClippedBounds)
2143            << "expect content to be clipped to screen area";
2144    Matrix4 expected;
2145    expected.loadTranslate(0, -2000, 0);
2146    EXPECT_MATRIX_APPROX_EQ(expected, observedData.rectMatrix)
2147            << "expect content to be translated as part of being clipped";
2148    expected.loadTranslate(10, 0, 0);
2149    EXPECT_MATRIX_APPROX_EQ(expected, observedData.drawLayerMatrix)
2150                << "expect drawLayer to be translated as part of being clipped";
2151}
2152
2153RENDERTHREAD_TEST(FrameBuilder, renderPropSaveLayerAlphaRotate) {
2154    SaveLayerAlphaData observedData;
2155    testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) {
2156        // Translate and rotate the view so that the only visible part is the top left corner of
2157        // the view. It will form an isosceles right triangle with a long side length of 200 at the
2158        // bottom of the viewport.
2159        properties.setTranslationX(100);
2160        properties.setTranslationY(100);
2161        properties.setPivotX(0);
2162        properties.setPivotY(0);
2163        properties.setRotation(45);
2164    });
2165    // ceil(sqrt(2) / 2 * 200) = 142
2166    EXPECT_EQ(142u, observedData.layerWidth);
2167    EXPECT_EQ(142u, observedData.layerHeight);
2168    EXPECT_EQ(Rect(142, 142), observedData.rectClippedBounds);
2169    EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), observedData.rectMatrix);
2170}
2171
2172RENDERTHREAD_TEST(FrameBuilder, renderPropSaveLayerAlphaScale) {
2173    SaveLayerAlphaData observedData;
2174    testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) {
2175        properties.setPivotX(0);
2176        properties.setPivotY(0);
2177        properties.setScaleX(2);
2178        properties.setScaleY(0.5f);
2179    });
2180    EXPECT_EQ(100u, observedData.layerWidth);
2181    EXPECT_EQ(400u, observedData.layerHeight);
2182    EXPECT_EQ(Rect(100, 400), observedData.rectClippedBounds);
2183    EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), observedData.rectMatrix);
2184}
2185
2186RENDERTHREAD_TEST(FrameBuilder, clip_replace) {
2187    class ClipReplaceTestRenderer : public TestRendererBase {
2188    public:
2189        void onColorOp(const ColorOp& op, const BakedOpState& state) override {
2190            EXPECT_EQ(0, mIndex++);
2191            EXPECT_TRUE(op.localClip->intersectWithRoot);
2192            EXPECT_EQ(Rect(20, 10, 30, 40), state.computedState.clipState->rect)
2193                    << "Expect resolved clip to be intersection of viewport clip and clip op";
2194        }
2195    };
2196    auto node = TestUtils::createNode(20, 20, 30, 30,
2197            [](RenderProperties& props, RecordingCanvas& canvas) {
2198        canvas.clipRect(0, -20, 10, 30, SkRegion::kReplace_Op);
2199        canvas.drawColor(SK_ColorWHITE, SkXfermode::Mode::kSrcOver_Mode);
2200    });
2201
2202    FrameBuilder frameBuilder(SkRect::MakeLTRB(10, 10, 40, 40), 50, 50,
2203            sLightGeometry, Caches::getInstance());
2204    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
2205
2206    ClipReplaceTestRenderer renderer;
2207    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
2208    EXPECT_EQ(1, renderer.getIndex());
2209}
2210
2211} // namespace uirenderer
2212} // namespace android
2213