FrameBuilderTests.cpp revision 6246d27813f25b85f6e4b5cb1121fe8484bcce2d
1f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan/*
2f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan * Copyright (C) 2016 The Android Open Source Project
3f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan *
4f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan * Licensed under the Apache License, Version 2.0 (the "License");
5f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan * you may not use this file except in compliance with the License.
6f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan * You may obtain a copy of the License at
7f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan *
8f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan *      http://www.apache.org/licenses/LICENSE-2.0
9f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan *
10f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan * Unless required by applicable law or agreed to in writing, software
11f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan * distributed under the License is distributed on an "AS IS" BASIS,
12f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan * See the License for the specific language governing permissions and
14f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan * limitations under the License.
15f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan */
16f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan
17f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan#include <gtest/gtest.h>
18d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan
19d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan#include <BakedOpState.h>
20d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan#include <DeferredLayerUpdater.h>
21f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan#include <FrameBuilder.h>
22d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan#include <LayerUpdateQueue.h>
23f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan#include <RecordedOp.h>
24d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan#include <RecordingCanvas.h>
25d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan#include <tests/common/TestUtils.h>
26d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan
27d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan#include <unordered_map>
28d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan
29d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloannamespace android {
30d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloannamespace uirenderer {
31d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan
32f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloanconst LayerUpdateQueue sEmptyLayerUpdateQueue;
33d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloanconst std::vector< sp<RenderNode> > sEmptyNodeList;
34f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloanconst FrameBuilder::LightGeometry sLightGeometry = { {100, 100, 100}, 50};
35a12bf4695c4916207f946efafd9728eb941e3f0aRobert Sloan
36d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan
37a12bf4695c4916207f946efafd9728eb941e3f0aRobert Sloan/**
38d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan * Virtual class implemented by each test to redirect static operation / state transitions to
39d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan * virtual methods.
40d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan *
41d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan * Virtual dispatch allows for default behaviors to be specified (very common case in below tests),
42d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan * and allows Renderer vs Dispatching behavior to be merged.
43d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan *
44d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan * onXXXOp methods fail by default - tests should override ops they expect
45d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan * startRepaintLayer fails by default - tests should override if expected
46d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan * startFrame/endFrame do nothing by default - tests should override to intercept
47d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan */
48d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloanclass TestRendererBase {
49d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloanpublic:
50d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan    virtual ~TestRendererBase() {}
51d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan    virtual OffscreenBuffer* startTemporaryLayer(uint32_t, uint32_t) {
52f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan        ADD_FAILURE() << "Layer creation not expected in this test";
53d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan        return nullptr;
54f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan    }
55d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan    virtual void startRepaintLayer(OffscreenBuffer*, const Rect& repaintRect) {
56f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan        ADD_FAILURE() << "Layer repaint not expected in this test";
57d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan    }
58f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan    virtual void endLayer() {
59f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan        ADD_FAILURE() << "Layer updates not expected in this test";
60f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan    }
61f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan    virtual void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) {}
62f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan    virtual void endFrame(const Rect& repaintRect) {}
63f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan
64f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan    // define virtual defaults for single draw methods
65f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan#define X(Type) \
66f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan    virtual void on##Type(const Type&, const BakedOpState&) { \
67f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan        ADD_FAILURE() << #Type " not expected in this test"; \
68f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan    }
69f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan    MAP_RENDERABLE_OPS(X)
70f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan#undef X
71f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan
72f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan    // define virtual defaults for merged draw methods
73f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan#define X(Type) \
74f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan    virtual void onMerged##Type##s(const MergedBakedOpList& opList) { \
75f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan        ADD_FAILURE() << "Merged " #Type "s not expected in this test"; \
76f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan    }
77f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan    MAP_MERGEABLE_OPS(X)
78f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan#undef X
79f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan
80f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan    int getIndex() { return mIndex; }
81f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan
82f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloanprotected:
83f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan    int mIndex = 0;
84f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan};
85f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan
86f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan/**
87f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan * Dispatches all static methods to similar formed methods on renderer, which fail by default but
88f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan * are overridden by subclasses per test.
89f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan */
90f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloanclass TestDispatcher {
91f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloanpublic:
92f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan    // define single op methods, which redirect to TestRendererBase
93f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan#define X(Type) \
94f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan    static void on##Type(TestRendererBase& renderer, const Type& op, const BakedOpState& state) { \
95f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan        renderer.on##Type(op, state); \
96f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan    }
97f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan    MAP_RENDERABLE_OPS(X);
98f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan#undef X
99f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan
100f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan    // define merged op methods, which redirect to TestRendererBase
101f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan#define X(Type) \
102f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan    static void onMerged##Type##s(TestRendererBase& renderer, const MergedBakedOpList& opList) { \
103f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan        renderer.onMerged##Type##s(opList); \
104f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan    }
105f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan    MAP_MERGEABLE_OPS(X);
106f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan#undef X
107f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan};
108f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan
109f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloanclass FailRenderer : public TestRendererBase {};
110f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan
111f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert SloanRENDERTHREAD_TEST(FrameBuilder, simple) {
112f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan    class SimpleTestRenderer : public TestRendererBase {
113f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan    public:
114f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan        void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
115f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan            EXPECT_EQ(0, mIndex++);
116f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan            EXPECT_EQ(100u, width);
117f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan            EXPECT_EQ(200u, height);
118f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan        }
119f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan        void onRectOp(const RectOp& op, const BakedOpState& state) override {
120f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan            EXPECT_EQ(1, mIndex++);
121f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan        }
122f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan        void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override {
123f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan            EXPECT_EQ(2, mIndex++);
124f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan        }
125d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan        void endFrame(const Rect& repaintRect) override {
126f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan            EXPECT_EQ(3, mIndex++);
127f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan        }
128f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan    };
129f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan
130f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan    auto node = TestUtils::createNode(0, 0, 100, 200,
131f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan            [](RenderProperties& props, RecordingCanvas& canvas) {
132f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan        SkBitmap bitmap = TestUtils::createSkBitmap(25, 25);
133f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan        canvas.drawRect(0, 0, 100, 200, SkPaint());
134f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan        canvas.drawBitmap(bitmap, 10, 10, nullptr);
135f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan    });
136f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200,
137f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan            TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
138f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan    SimpleTestRenderer renderer;
139f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
140f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan    EXPECT_EQ(4, renderer.getIndex()); // 2 ops + start + end
141f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan}
142f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan
143f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert SloanRENDERTHREAD_TEST(FrameBuilder, simpleStroke) {
144f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan    class SimpleStrokeTestRenderer : public TestRendererBase {
145f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan    public:
146f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan        void onPointsOp(const PointsOp& op, const BakedOpState& state) override {
147f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan            EXPECT_EQ(0, mIndex++);
148f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan            // even though initial bounds are empty...
149f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan            EXPECT_TRUE(op.unmappedBounds.isEmpty())
150f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan                    << "initial bounds should be empty, since they're unstroked";
151f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan            EXPECT_EQ(Rect(45, 45, 55, 55), state.computedState.clippedBounds)
152f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan                    << "final bounds should account for stroke";
153f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan        }
154f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan    };
155f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan
156f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan    auto node = TestUtils::createNode(0, 0, 100, 200,
157f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan            [](RenderProperties& props, RecordingCanvas& canvas) {
158f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan        SkPaint strokedPaint;
159f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan        strokedPaint.setStrokeWidth(10);
160f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan        canvas.drawPoint(50, 50, strokedPaint);
161f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan    });
162f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200,
163f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan            TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
164f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan    SimpleStrokeTestRenderer renderer;
165f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
166f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan    EXPECT_EQ(1, renderer.getIndex());
167f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan}
168f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan
169f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert SloanRENDERTHREAD_TEST(FrameBuilder, simpleRejection) {
170f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan    auto node = TestUtils::createNode(0, 0, 200, 200,
171f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan            [](RenderProperties& props, RecordingCanvas& canvas) {
172f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan        canvas.save(SaveFlags::MatrixClip);
173f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan        canvas.clipRect(200, 200, 400, 400, SkRegion::kIntersect_Op); // intersection should be empty
174f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan        canvas.drawRect(0, 0, 400, 400, SkPaint());
175f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan        canvas.restore();
176f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan    });
177f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
178f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan            TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
179f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan
180f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan    FailRenderer renderer;
181f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
182f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan}
183f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan
184f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert SloanRENDERTHREAD_TEST(FrameBuilder, simpleBatching) {
185f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan    const int LOOPS = 5;
186f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan    class SimpleBatchingTestRenderer : public TestRendererBase {
187f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan    public:
188f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan        void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override {
189f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan            EXPECT_TRUE(mIndex++ >= LOOPS) << "Bitmaps should be above all rects";
190f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan        }
191f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan        void onRectOp(const RectOp& op, const BakedOpState& state) override {
192f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan            EXPECT_TRUE(mIndex++ < LOOPS) << "Rects should be below all bitmaps";
193f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan        }
194f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan    };
195f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan
196f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan    auto node = TestUtils::createNode(0, 0, 200, 200,
197f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan            [](RenderProperties& props, RecordingCanvas& canvas) {
198f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan        SkBitmap bitmap = TestUtils::createSkBitmap(10, 10,
199f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan                kAlpha_8_SkColorType); // Disable merging by using alpha 8 bitmap
200f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan
201f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan        // Alternate between drawing rects and bitmaps, with bitmaps overlapping rects.
202f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan        // Rects don't overlap bitmaps, so bitmaps should be brought to front as a group.
203f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan        canvas.save(SaveFlags::MatrixClip);
204f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan        for (int i = 0; i < LOOPS; i++) {
205f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan            canvas.translate(0, 10);
206f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan            canvas.drawRect(0, 0, 10, 10, SkPaint());
207f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan            canvas.drawBitmap(bitmap, 5, 0, nullptr);
208f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan        }
209f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan        canvas.restore();
210f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan    });
211f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan
212f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
213f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan            TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
214f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan    SimpleBatchingTestRenderer renderer;
215f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
216f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan    EXPECT_EQ(2 * LOOPS, renderer.getIndex())
217f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan            << "Expect number of ops = 2 * loop count";
218f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan}
219f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan
220f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert SloanRENDERTHREAD_TEST(FrameBuilder, empty_noFbo0) {
221f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan    class EmptyNoFbo0TestRenderer : public TestRendererBase {
222f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan    public:
223f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan        void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
224f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan            ADD_FAILURE() << "Primary frame draw not expected in this test";
225f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan        }
226f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan        void endFrame(const Rect& repaintRect) override {
227f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan            ADD_FAILURE() << "Primary frame draw not expected in this test";
228d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan        }
229d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan    };
230d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan
231d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan    // Pass empty node list, so no work is enqueued for Fbo0
232d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
233d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan            sEmptyNodeList, sLightGeometry, Caches::getInstance());
234d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan    EmptyNoFbo0TestRenderer renderer;
235d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
236d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan}
237d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan
238d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert SloanRENDERTHREAD_TEST(FrameBuilder, empty_withFbo0) {
239d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan    class EmptyWithFbo0TestRenderer : public TestRendererBase {
240d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan    public:
241d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan        void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
242d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan            EXPECT_EQ(0, mIndex++);
243d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan        }
244d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan        void endFrame(const Rect& repaintRect) override {
245d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan            EXPECT_EQ(1, mIndex++);
246d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan        }
247d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan    };
248d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan    auto node = TestUtils::createNode(10, 10, 110, 110,
249d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan            [](RenderProperties& props, RecordingCanvas& canvas) {
250d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan        // no drawn content
251d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan    });
252d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan    auto syncedNodeList = TestUtils::createSyncedNodeList(node);
253d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan
254d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan    // Draw, but pass empty node list, so no work is done for primary frame
255d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
256d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan            syncedNodeList, sLightGeometry, Caches::getInstance());
257d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan    EmptyWithFbo0TestRenderer renderer;
258d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
259d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan    EXPECT_EQ(2, renderer.getIndex()) << "No drawing content produced,"
260d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan            " but fbo0 update lifecycle should still be observed";
261d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan}
262d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan
263d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert SloanRENDERTHREAD_TEST(FrameBuilder, avoidOverdraw_rects) {
264d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan    class AvoidOverdrawRectsTestRenderer : public TestRendererBase {
265d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan    public:
266d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan        void onRectOp(const RectOp& op, const BakedOpState& state) override {
267d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan            EXPECT_EQ(mIndex++, 0) << "Should be one rect";
268d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan            EXPECT_EQ(Rect(10, 10, 190, 190), op.unmappedBounds)
269d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan                    << "Last rect should occlude others.";
270d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan        }
271d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan    };
272d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan    auto node = TestUtils::createNode(0, 0, 200, 200,
273d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan            [](RenderProperties& props, RecordingCanvas& canvas) {
274d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan        canvas.drawRect(0, 0, 200, 200, SkPaint());
275d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan        canvas.drawRect(0, 0, 200, 200, SkPaint());
276d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan        canvas.drawRect(10, 10, 190, 190, SkPaint());
277d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan    });
278d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan
279d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan    // Damage (and therefore clip) is same as last draw, subset of renderable area.
280d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan    // This means last op occludes other contents, and they'll be rejected to avoid overdraw.
281d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan    SkRect damageRect = SkRect::MakeLTRB(10, 10, 190, 190);
282d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, damageRect, 200, 200,
283d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan            TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
284d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan
285d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan    EXPECT_EQ(3u, node->getDisplayList()->getOps().size())
286d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan            << "Recording must not have rejected ops, in order for this test to be valid";
287d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan
288d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan    AvoidOverdrawRectsTestRenderer renderer;
289d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
290d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan    EXPECT_EQ(1, renderer.getIndex()) << "Expect exactly one op";
291d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan}
292d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan
293d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert SloanRENDERTHREAD_TEST(FrameBuilder, avoidOverdraw_bitmaps) {
294d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan    static SkBitmap opaqueBitmap = TestUtils::createSkBitmap(50, 50,
295d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan            SkColorType::kRGB_565_SkColorType);
296d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan    static SkBitmap transpBitmap = TestUtils::createSkBitmap(50, 50,
297d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan            SkColorType::kAlpha_8_SkColorType);
298d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan    class AvoidOverdrawBitmapsTestRenderer : public TestRendererBase {
299d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan    public:
300d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan        void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override {
301d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan            EXPECT_LT(mIndex++, 2) << "Should be two bitmaps";
302d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan            switch(mIndex++) {
303d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan            case 0:
304d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan                EXPECT_EQ(opaqueBitmap.pixelRef(), op.bitmap->pixelRef());
305d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan                break;
306d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan            case 1:
307d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan                EXPECT_EQ(transpBitmap.pixelRef(), op.bitmap->pixelRef());
308d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan                break;
309d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan            default:
310d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan                ADD_FAILURE() << "Only two ops expected.";
311d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan            }
312d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan        }
313d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan    };
314d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan
315d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan    auto node = TestUtils::createNode(0, 0, 50, 50,
316d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan            [](RenderProperties& props, RecordingCanvas& canvas) {
317d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan        canvas.drawRect(0, 0, 50, 50, SkPaint());
318d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan        canvas.drawRect(0, 0, 50, 50, SkPaint());
319d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan        canvas.drawBitmap(transpBitmap, 0, 0, nullptr);
320d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan
321d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan        // only the below draws should remain, since they're
322d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan        canvas.drawBitmap(opaqueBitmap, 0, 0, nullptr);
323d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan        canvas.drawBitmap(transpBitmap, 0, 0, nullptr);
324d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan    });
325d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan
326d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(50, 50), 50, 50,
327d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan            TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
328d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan
329d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan    EXPECT_EQ(5u, node->getDisplayList()->getOps().size())
330d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan            << "Recording must not have rejected ops, in order for this test to be valid";
331d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan
332d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan    AvoidOverdrawBitmapsTestRenderer renderer;
333d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
334d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan    EXPECT_EQ(2, renderer.getIndex()) << "Expect exactly one op";
335d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan}
336d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan
337d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert SloanRENDERTHREAD_TEST(FrameBuilder, clippedMerging) {
338d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan    class ClippedMergingTestRenderer : public TestRendererBase {
339d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan    public:
340d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan        void onMergedBitmapOps(const MergedBakedOpList& opList) override {
341d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan            EXPECT_EQ(0, mIndex);
342d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan            mIndex += opList.count;
343d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan            EXPECT_EQ(4u, opList.count);
344d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan            EXPECT_EQ(Rect(10, 10, 90, 90), opList.clip);
345d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan            EXPECT_EQ(OpClipSideFlags::Left | OpClipSideFlags::Top | OpClipSideFlags::Right,
346d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan                    opList.clipSideFlags);
347d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan        }
348d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan    };
349d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan    auto node = TestUtils::createNode(0, 0, 100, 100,
350d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan            [](RenderProperties& props, TestCanvas& canvas) {
351d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan        SkBitmap bitmap = TestUtils::createSkBitmap(20, 20);
352d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan
353d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan        // left side clipped (to inset left half)
354d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan        canvas.clipRect(10, 0, 50, 100, SkRegion::kReplace_Op);
355d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan        canvas.drawBitmap(bitmap, 0, 40, nullptr);
356d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan
357d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan        // top side clipped (to inset top half)
358d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan        canvas.clipRect(0, 10, 100, 50, SkRegion::kReplace_Op);
359d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan        canvas.drawBitmap(bitmap, 40, 0, nullptr);
360d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan
361d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan        // right side clipped (to inset right half)
362d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan        canvas.clipRect(50, 0, 90, 100, SkRegion::kReplace_Op);
363d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan        canvas.drawBitmap(bitmap, 80, 40, nullptr);
364d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan
365d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan        // bottom not clipped, just abutting (inset bottom half)
366d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan        canvas.clipRect(0, 50, 100, 90, SkRegion::kReplace_Op);
367d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan        canvas.drawBitmap(bitmap, 40, 70, nullptr);
368d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan    });
369d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan
370d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100,
371d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan            TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
372d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan    ClippedMergingTestRenderer renderer;
373d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
374d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan    EXPECT_EQ(4, renderer.getIndex());
375d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan}
376d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan
377d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert SloanRENDERTHREAD_TEST(FrameBuilder, textMerging) {
378d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan    class TextMergingTestRenderer : public TestRendererBase {
379d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan    public:
380d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan        void onMergedTextOps(const MergedBakedOpList& opList) override {
381d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan            EXPECT_EQ(0, mIndex);
382d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan            mIndex += opList.count;
383d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan            EXPECT_EQ(2u, opList.count);
384d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan            EXPECT_EQ(OpClipSideFlags::Top, opList.clipSideFlags);
385d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan            EXPECT_EQ(OpClipSideFlags::Top, opList.states[0]->computedState.clipSideFlags);
386d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan            EXPECT_EQ(OpClipSideFlags::None, opList.states[1]->computedState.clipSideFlags);
387d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan        }
388d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan    };
389d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan    auto node = TestUtils::createNode(0, 0, 400, 400,
390d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan            [](RenderProperties& props, TestCanvas& canvas) {
391d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan        SkPaint paint;
392d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan        paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
393d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan        paint.setAntiAlias(true);
394d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan        paint.setTextSize(50);
395d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan        TestUtils::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 0); // will be top clipped
396d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan        TestUtils::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 100); // not clipped
397d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan    });
398d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(400, 400), 400, 400,
399d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan            TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
400d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan    TextMergingTestRenderer renderer;
401d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
402d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan    EXPECT_EQ(2, renderer.getIndex()) << "Expect 2 ops";
403d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan}
404d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan
405d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert SloanRENDERTHREAD_TEST(FrameBuilder, textStrikethrough) {
406d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan    const int LOOPS = 5;
407d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan    class TextStrikethroughTestRenderer : public TestRendererBase {
408d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan    public:
409d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan        void onRectOp(const RectOp& op, const BakedOpState& state) override {
410d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan            EXPECT_TRUE(mIndex++ >= LOOPS) << "Strikethrough rects should be above all text";
411d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan        }
412d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan        void onMergedTextOps(const MergedBakedOpList& opList) override {
413d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan            EXPECT_EQ(0, mIndex);
414d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan            mIndex += opList.count;
415d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan            EXPECT_EQ(5u, opList.count);
416d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan        }
417d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan    };
418d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan    auto node = TestUtils::createNode(0, 0, 200, 2000,
419d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan            [](RenderProperties& props, RecordingCanvas& canvas) {
420d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan        SkPaint textPaint;
421d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan        textPaint.setAntiAlias(true);
422d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan        textPaint.setTextSize(20);
423d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan        textPaint.setStrikeThruText(true);
424d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan        textPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
425d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan        for (int i = 0; i < LOOPS; i++) {
426d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan            TestUtils::drawUtf8ToCanvas(&canvas, "test text", textPaint, 10, 100 * (i + 1));
427d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan        }
428d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan    });
429d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 2000), 200, 2000,
430d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan            TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
431d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan    TextStrikethroughTestRenderer renderer;
432d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
433d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan    EXPECT_EQ(2 * LOOPS, renderer.getIndex())
434d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan            << "Expect number of ops = 2 * loop count";
435d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan}
436d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan
437d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloanstatic auto styles = {
438d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan        SkPaint::kFill_Style, SkPaint::kStroke_Style, SkPaint::kStrokeAndFill_Style };
439d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan
440d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert SloanRENDERTHREAD_TEST(FrameBuilder, textStyle) {
441d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan    class TextStyleTestRenderer : public TestRendererBase {
442d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan    public:
443d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan        void onMergedTextOps(const MergedBakedOpList& opList) override {
444d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan            ASSERT_EQ(0, mIndex);
445d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan            ASSERT_EQ(3u, opList.count);
446d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan            mIndex += opList.count;
447d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan
448d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan            int index = 0;
449d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan            for (auto style : styles) {
450d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan                auto state = opList.states[index++];
451d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan                ASSERT_EQ(style, state->op->paint->getStyle())
452d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan                        << "Remainder of validation relies upon stable merged order";
453d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan                ASSERT_EQ(0, state->computedState.clipSideFlags)
454d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan                        << "Clipped bounds validation requires unclipped ops";
455d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan            }
456d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan
457d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan            Rect fill = opList.states[0]->computedState.clippedBounds;
458d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan            Rect stroke = opList.states[1]->computedState.clippedBounds;
459d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan            EXPECT_EQ(stroke, opList.states[2]->computedState.clippedBounds)
460d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan                    << "Stroke+Fill should be same as stroke";
461d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan
462d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan            EXPECT_TRUE(stroke.contains(fill));
463d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan            EXPECT_FALSE(fill.contains(stroke));
464d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan
465d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan            // outset by half the stroke width
466d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan            Rect outsetFill(fill);
467d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan            outsetFill.outset(5);
468d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan            EXPECT_EQ(stroke, outsetFill);
469d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan        }
470d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan    };
471d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan    auto node = TestUtils::createNode(0, 0, 400, 400,
472d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan            [](RenderProperties& props, TestCanvas& canvas) {
473d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan        SkPaint paint;
474d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan        paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
475d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan        paint.setAntiAlias(true);
476d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan        paint.setTextSize(50);
477d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan        paint.setStrokeWidth(10);
478d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan
479d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan        // draw 3 copies of the same text overlapping, each with a different style.
480d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan        // They'll get merged, but with
481d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan        for (auto style : styles) {
482d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan            paint.setStyle(style);
483d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan            TestUtils::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 100);
484d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan        }
485d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan    });
486d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(400, 400), 400, 400,
487d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan            TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
488d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan    TextStyleTestRenderer renderer;
489d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
490d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan    EXPECT_EQ(3, renderer.getIndex()) << "Expect 3 ops";
491d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan}
492d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan
493d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert SloanRENDERTHREAD_TEST(FrameBuilder, textureLayer_clipLocalMatrix) {
494d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan    class TextureLayerClipLocalMatrixTestRenderer : public TestRendererBase {
495d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan    public:
496d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan        void onTextureLayerOp(const TextureLayerOp& op, const BakedOpState& state) override {
497d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan            EXPECT_EQ(0, mIndex++);
498d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan            EXPECT_EQ(Rect(50, 50, 150, 150), state.computedState.clipRect());
499d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan            EXPECT_EQ(Rect(50, 50, 105, 105), state.computedState.clippedBounds);
500d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan
501d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan            Matrix4 expected;
502d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan            expected.loadTranslate(5, 5, 0);
503d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan            EXPECT_MATRIX_APPROX_EQ(expected, state.computedState.transform);
504d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan        }
505d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan    };
506d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan
507d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan    auto layerUpdater = TestUtils::createTextureLayerUpdater(renderThread, 100, 100,
508d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan            SkMatrix::MakeTrans(5, 5));
509d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan
510d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan    auto node = TestUtils::createNode(0, 0, 200, 200,
511d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan            [&layerUpdater](RenderProperties& props, RecordingCanvas& canvas) {
512d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan        canvas.save(SaveFlags::MatrixClip);
513d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan        canvas.clipRect(50, 50, 150, 150, SkRegion::kIntersect_Op);
514d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan        canvas.drawLayer(layerUpdater.get());
515d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan        canvas.restore();
516d1d118f9e67079ed7c29543924ad9a2a2c56e86aRobert Sloan    });
517f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
518f6200e70eccb73d7a8a6940d081918f5a2b98fadRobert Sloan            TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
519    TextureLayerClipLocalMatrixTestRenderer renderer;
520    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
521    EXPECT_EQ(1, renderer.getIndex());
522}
523
524RENDERTHREAD_TEST(FrameBuilder, textureLayer_combineMatrices) {
525    class TextureLayerCombineMatricesTestRenderer : public TestRendererBase {
526    public:
527        void onTextureLayerOp(const TextureLayerOp& op, const BakedOpState& state) override {
528            EXPECT_EQ(0, mIndex++);
529
530            Matrix4 expected;
531            expected.loadTranslate(35, 45, 0);
532            EXPECT_MATRIX_APPROX_EQ(expected, state.computedState.transform);
533        }
534    };
535
536    auto layerUpdater = TestUtils::createTextureLayerUpdater(renderThread, 100, 100,
537            SkMatrix::MakeTrans(5, 5));
538
539    auto node = TestUtils::createNode(0, 0, 200, 200,
540            [&layerUpdater](RenderProperties& props, RecordingCanvas& canvas) {
541        canvas.save(SaveFlags::MatrixClip);
542        canvas.translate(30, 40);
543        canvas.drawLayer(layerUpdater.get());
544        canvas.restore();
545    });
546
547    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
548            TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
549    TextureLayerCombineMatricesTestRenderer renderer;
550    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
551    EXPECT_EQ(1, renderer.getIndex());
552}
553
554RENDERTHREAD_TEST(FrameBuilder, textureLayer_reject) {
555    auto layerUpdater = TestUtils::createTextureLayerUpdater(renderThread, 100, 100,
556            SkMatrix::MakeTrans(5, 5));
557    layerUpdater->backingLayer()->setRenderTarget(GL_NONE); // Should be rejected
558
559    auto node = TestUtils::createNode(0, 0, 200, 200,
560            [&layerUpdater](RenderProperties& props, RecordingCanvas& canvas) {
561        canvas.drawLayer(layerUpdater.get());
562    });
563    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
564            TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
565    FailRenderer renderer;
566    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
567}
568
569RENDERTHREAD_TEST(FrameBuilder, functor_reject) {
570    class FunctorTestRenderer : public TestRendererBase {
571    public:
572        void onFunctorOp(const FunctorOp& op, const BakedOpState& state) override {
573            EXPECT_EQ(0, mIndex++);
574        }
575    };
576    Functor noopFunctor;
577
578    // 1 million pixel tall view, scrolled down 80%
579    auto scrolledFunctorView = TestUtils::createNode(0, 0, 400, 1000000,
580            [&noopFunctor](RenderProperties& props, RecordingCanvas& canvas) {
581        canvas.translate(0, -800000);
582        canvas.callDrawGLFunction(&noopFunctor);
583    });
584
585    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
586            TestUtils::createSyncedNodeList(scrolledFunctorView),
587            sLightGeometry, Caches::getInstance());
588    FunctorTestRenderer renderer;
589    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
590    EXPECT_EQ(1, renderer.getIndex()) << "Functor should not be rejected";
591}
592
593RENDERTHREAD_TEST(FrameBuilder, deferColorOp_unbounded) {
594    class ColorTestRenderer : public TestRendererBase {
595    public:
596        void onColorOp(const ColorOp& op, const BakedOpState& state) override {
597            EXPECT_EQ(0, mIndex++);
598            EXPECT_EQ(Rect(200, 200), state.computedState.clippedBounds)
599                    << "Color op should be expanded to bounds of surrounding";
600        }
601    };
602
603    auto unclippedColorView = TestUtils::createNode(0, 0, 10, 10,
604            [](RenderProperties& props, RecordingCanvas& canvas) {
605        props.setClipToBounds(false);
606        canvas.drawColor(SK_ColorWHITE, SkXfermode::Mode::kSrcOver_Mode);
607    });
608
609    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
610            TestUtils::createSyncedNodeList(unclippedColorView),
611            sLightGeometry, Caches::getInstance());
612    ColorTestRenderer renderer;
613    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
614    EXPECT_EQ(1, renderer.getIndex()) << "ColorOp should not be rejected";
615}
616
617TEST(FrameBuilder, renderNode) {
618    class RenderNodeTestRenderer : public TestRendererBase {
619    public:
620        void onRectOp(const RectOp& op, const BakedOpState& state) override {
621            switch(mIndex++) {
622            case 0:
623                EXPECT_EQ(Rect(200, 200), state.computedState.clippedBounds);
624                EXPECT_EQ(SK_ColorDKGRAY, op.paint->getColor());
625                break;
626            case 1:
627                EXPECT_EQ(Rect(50, 50, 150, 150), state.computedState.clippedBounds);
628                EXPECT_EQ(SK_ColorWHITE, op.paint->getColor());
629                break;
630            default:
631                ADD_FAILURE();
632            }
633        }
634    };
635
636    auto child = TestUtils::createNode(10, 10, 110, 110,
637            [](RenderProperties& props, RecordingCanvas& canvas) {
638        SkPaint paint;
639        paint.setColor(SK_ColorWHITE);
640        canvas.drawRect(0, 0, 100, 100, paint);
641    });
642
643    auto parent = TestUtils::createNode(0, 0, 200, 200,
644            [&child](RenderProperties& props, RecordingCanvas& canvas) {
645        SkPaint paint;
646        paint.setColor(SK_ColorDKGRAY);
647        canvas.drawRect(0, 0, 200, 200, paint);
648
649        canvas.save(SaveFlags::MatrixClip);
650        canvas.translate(40, 40);
651        canvas.drawRenderNode(child.get());
652        canvas.restore();
653    });
654
655    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
656            TestUtils::createSyncedNodeList(parent), sLightGeometry, Caches::getInstance());
657    RenderNodeTestRenderer renderer;
658    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
659    EXPECT_EQ(2, renderer.getIndex());
660}
661
662RENDERTHREAD_TEST(FrameBuilder, clipped) {
663    class ClippedTestRenderer : public TestRendererBase {
664    public:
665        void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override {
666            EXPECT_EQ(0, mIndex++);
667            EXPECT_EQ(Rect(10, 20, 30, 40), state.computedState.clippedBounds);
668            EXPECT_EQ(Rect(10, 20, 30, 40), state.computedState.clipRect());
669            EXPECT_TRUE(state.computedState.transform.isIdentity());
670        }
671    };
672
673    auto node = TestUtils::createNode(0, 0, 200, 200,
674            [](RenderProperties& props, RecordingCanvas& canvas) {
675        SkBitmap bitmap = TestUtils::createSkBitmap(200, 200);
676        canvas.drawBitmap(bitmap, 0, 0, nullptr);
677    });
678
679    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue,
680            SkRect::MakeLTRB(10, 20, 30, 40), // clip to small area, should see in receiver
681            200, 200, TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
682    ClippedTestRenderer renderer;
683    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
684}
685
686RENDERTHREAD_TEST(FrameBuilder, saveLayer_simple) {
687    class SaveLayerSimpleTestRenderer : public TestRendererBase {
688    public:
689        OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override {
690            EXPECT_EQ(0, mIndex++);
691            EXPECT_EQ(180u, width);
692            EXPECT_EQ(180u, height);
693            return nullptr;
694        }
695        void endLayer() override {
696            EXPECT_EQ(2, mIndex++);
697        }
698        void onRectOp(const RectOp& op, const BakedOpState& state) override {
699            EXPECT_EQ(1, mIndex++);
700            EXPECT_EQ(Rect(10, 10, 190, 190), op.unmappedBounds);
701            EXPECT_EQ(Rect(180, 180), state.computedState.clippedBounds);
702            EXPECT_EQ(Rect(180, 180), state.computedState.clipRect());
703
704            Matrix4 expectedTransform;
705            expectedTransform.loadTranslate(-10, -10, 0);
706            EXPECT_MATRIX_APPROX_EQ(expectedTransform, state.computedState.transform);
707        }
708        void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
709            EXPECT_EQ(3, mIndex++);
710            EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds);
711            EXPECT_EQ(Rect(200, 200), state.computedState.clipRect());
712            EXPECT_TRUE(state.computedState.transform.isIdentity());
713        }
714    };
715
716    auto node = TestUtils::createNode(0, 0, 200, 200,
717            [](RenderProperties& props, RecordingCanvas& canvas) {
718        canvas.saveLayerAlpha(10, 10, 190, 190, 128, SaveFlags::ClipToLayer);
719        canvas.drawRect(10, 10, 190, 190, SkPaint());
720        canvas.restore();
721    });
722    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
723            TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
724    SaveLayerSimpleTestRenderer renderer;
725    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
726    EXPECT_EQ(4, renderer.getIndex());
727}
728
729RENDERTHREAD_TEST(FrameBuilder, saveLayer_nested) {
730    /* saveLayer1 { rect1, saveLayer2 { rect2 } } will play back as:
731     * - startTemporaryLayer2, rect2 endLayer2
732     * - startTemporaryLayer1, rect1, drawLayer2, endLayer1
733     * - startFrame, layerOp1, endFrame
734     */
735    class SaveLayerNestedTestRenderer : public TestRendererBase {
736    public:
737        OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override {
738            const int index = mIndex++;
739            if (index == 0) {
740                EXPECT_EQ(400u, width);
741                EXPECT_EQ(400u, height);
742                return (OffscreenBuffer*) 0x400;
743            } else if (index == 3) {
744                EXPECT_EQ(800u, width);
745                EXPECT_EQ(800u, height);
746                return (OffscreenBuffer*) 0x800;
747            } else { ADD_FAILURE(); }
748            return (OffscreenBuffer*) nullptr;
749        }
750        void endLayer() override {
751            int index = mIndex++;
752            EXPECT_TRUE(index == 2 || index == 6);
753        }
754        void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
755            EXPECT_EQ(7, mIndex++);
756        }
757        void endFrame(const Rect& repaintRect) override {
758            EXPECT_EQ(9, mIndex++);
759        }
760        void onRectOp(const RectOp& op, const BakedOpState& state) override {
761            const int index = mIndex++;
762            if (index == 1) {
763                EXPECT_EQ(Rect(400, 400), op.unmappedBounds); // inner rect
764            } else if (index == 4) {
765                EXPECT_EQ(Rect(800, 800), op.unmappedBounds); // outer rect
766            } else { ADD_FAILURE(); }
767        }
768        void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
769            const int index = mIndex++;
770            if (index == 5) {
771                EXPECT_EQ((OffscreenBuffer*)0x400, *op.layerHandle);
772                EXPECT_EQ(Rect(400, 400), op.unmappedBounds); // inner layer
773            } else if (index == 8) {
774                EXPECT_EQ((OffscreenBuffer*)0x800, *op.layerHandle);
775                EXPECT_EQ(Rect(800, 800), op.unmappedBounds); // outer layer
776            } else { ADD_FAILURE(); }
777        }
778    };
779
780    auto node = TestUtils::createNode(0, 0, 800, 800,
781            [](RenderProperties& props, RecordingCanvas& canvas) {
782        canvas.saveLayerAlpha(0, 0, 800, 800, 128, SaveFlags::ClipToLayer);
783        {
784            canvas.drawRect(0, 0, 800, 800, SkPaint());
785            canvas.saveLayerAlpha(0, 0, 400, 400, 128, SaveFlags::ClipToLayer);
786            {
787                canvas.drawRect(0, 0, 400, 400, SkPaint());
788            }
789            canvas.restore();
790        }
791        canvas.restore();
792    });
793
794    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(800, 800), 800, 800,
795            TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
796    SaveLayerNestedTestRenderer renderer;
797    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
798    EXPECT_EQ(10, renderer.getIndex());
799}
800
801RENDERTHREAD_TEST(FrameBuilder, saveLayer_contentRejection) {
802        auto node = TestUtils::createNode(0, 0, 200, 200,
803                [](RenderProperties& props, RecordingCanvas& canvas) {
804        canvas.save(SaveFlags::MatrixClip);
805        canvas.clipRect(200, 200, 400, 400, SkRegion::kIntersect_Op);
806        canvas.saveLayerAlpha(200, 200, 400, 400, 128, SaveFlags::ClipToLayer);
807
808        // draw within save layer may still be recorded, but shouldn't be drawn
809        canvas.drawRect(200, 200, 400, 400, SkPaint());
810
811        canvas.restore();
812        canvas.restore();
813    });
814    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
815            TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
816
817    FailRenderer renderer;
818    // should see no ops, even within the layer, since the layer should be rejected
819    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
820}
821
822RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_simple) {
823    class SaveLayerUnclippedSimpleTestRenderer : public TestRendererBase {
824    public:
825        void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override {
826            EXPECT_EQ(0, mIndex++);
827            EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds);
828            EXPECT_CLIP_RECT(Rect(200, 200), state.computedState.clipState);
829            EXPECT_TRUE(state.computedState.transform.isIdentity());
830        }
831        void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override {
832            EXPECT_EQ(1, mIndex++);
833            ASSERT_NE(nullptr, op.paint);
834            ASSERT_EQ(SkXfermode::kClear_Mode, PaintUtils::getXfermodeDirect(op.paint));
835        }
836        void onRectOp(const RectOp& op, const BakedOpState& state) override {
837            EXPECT_EQ(2, mIndex++);
838            EXPECT_EQ(Rect(200, 200), op.unmappedBounds);
839            EXPECT_EQ(Rect(200, 200), state.computedState.clippedBounds);
840            EXPECT_EQ(Rect(200, 200), state.computedState.clipRect());
841            EXPECT_TRUE(state.computedState.transform.isIdentity());
842        }
843        void onCopyFromLayerOp(const CopyFromLayerOp& op, const BakedOpState& state) override {
844            EXPECT_EQ(3, mIndex++);
845            EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds);
846            EXPECT_CLIP_RECT(Rect(200, 200), state.computedState.clipState);
847            EXPECT_TRUE(state.computedState.transform.isIdentity());
848        }
849    };
850
851    auto node = TestUtils::createNode(0, 0, 200, 200,
852            [](RenderProperties& props, RecordingCanvas& canvas) {
853        canvas.saveLayerAlpha(10, 10, 190, 190, 128, (SaveFlags::Flags)(0));
854        canvas.drawRect(0, 0, 200, 200, SkPaint());
855        canvas.restore();
856    });
857    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
858            TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
859    SaveLayerUnclippedSimpleTestRenderer renderer;
860    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
861    EXPECT_EQ(4, renderer.getIndex());
862}
863
864RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_mergedClears) {
865    class SaveLayerUnclippedMergedClearsTestRenderer : public TestRendererBase {
866    public:
867        void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override {
868            int index = mIndex++;
869            EXPECT_GT(4, index);
870            EXPECT_EQ(5, op.unmappedBounds.getWidth());
871            EXPECT_EQ(5, op.unmappedBounds.getHeight());
872            if (index == 0) {
873                EXPECT_EQ(Rect(10, 10), state.computedState.clippedBounds);
874            } else if (index == 1) {
875                EXPECT_EQ(Rect(190, 0, 200, 10), state.computedState.clippedBounds);
876            } else if (index == 2) {
877                EXPECT_EQ(Rect(0, 190, 10, 200), state.computedState.clippedBounds);
878            } else if (index == 3) {
879                EXPECT_EQ(Rect(190, 190, 200, 200), state.computedState.clippedBounds);
880            }
881        }
882        void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override {
883            EXPECT_EQ(4, mIndex++);
884            ASSERT_EQ(op.vertexCount, 16u);
885            for (size_t i = 0; i < op.vertexCount; i++) {
886                auto v = op.vertices[i];
887                EXPECT_TRUE(v.x == 0 || v.x == 10 || v.x == 190 || v.x == 200);
888                EXPECT_TRUE(v.y == 0 || v.y == 10 || v.y == 190 || v.y == 200);
889            }
890        }
891        void onRectOp(const RectOp& op, const BakedOpState& state) override {
892            EXPECT_EQ(5, mIndex++);
893        }
894        void onCopyFromLayerOp(const CopyFromLayerOp& op, const BakedOpState& state) override {
895            EXPECT_LT(5, mIndex++);
896        }
897    };
898
899    auto node = TestUtils::createNode(0, 0, 200, 200,
900            [](RenderProperties& props, RecordingCanvas& canvas) {
901
902        int restoreTo = canvas.save(SaveFlags::MatrixClip);
903        canvas.scale(2, 2);
904        canvas.saveLayerAlpha(0, 0, 5, 5, 128, SaveFlags::MatrixClip);
905        canvas.saveLayerAlpha(95, 0, 100, 5, 128, SaveFlags::MatrixClip);
906        canvas.saveLayerAlpha(0, 95, 5, 100, 128, SaveFlags::MatrixClip);
907        canvas.saveLayerAlpha(95, 95, 100, 100, 128, SaveFlags::MatrixClip);
908        canvas.drawRect(0, 0, 100, 100, SkPaint());
909        canvas.restoreToCount(restoreTo);
910    });
911    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
912            TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
913    SaveLayerUnclippedMergedClearsTestRenderer renderer;
914    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
915    EXPECT_EQ(10, renderer.getIndex())
916            << "Expect 4 copyTos, 4 copyFroms, 1 clear SimpleRects, and 1 rect.";
917}
918
919RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_clearClip) {
920    class SaveLayerUnclippedClearClipTestRenderer : public TestRendererBase {
921    public:
922        void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override {
923            EXPECT_EQ(0, mIndex++);
924        }
925        void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override {
926            EXPECT_EQ(1, mIndex++);
927            ASSERT_NE(nullptr, op.paint);
928            EXPECT_EQ(SkXfermode::kClear_Mode, PaintUtils::getXfermodeDirect(op.paint));
929            EXPECT_EQ(Rect(50, 50, 150, 150), state.computedState.clippedBounds)
930                    << "Expect dirty rect as clip";
931            ASSERT_NE(nullptr, state.computedState.clipState);
932            EXPECT_EQ(Rect(50, 50, 150, 150), state.computedState.clipState->rect);
933            EXPECT_EQ(ClipMode::Rectangle, state.computedState.clipState->mode);
934        }
935        void onRectOp(const RectOp& op, const BakedOpState& state) override {
936            EXPECT_EQ(2, mIndex++);
937        }
938        void onCopyFromLayerOp(const CopyFromLayerOp& op, const BakedOpState& state) override {
939            EXPECT_EQ(3, mIndex++);
940        }
941    };
942
943    auto node = TestUtils::createNode(0, 0, 200, 200,
944            [](RenderProperties& props, RecordingCanvas& canvas) {
945        // save smaller than clip, so we get unclipped behavior
946        canvas.saveLayerAlpha(10, 10, 190, 190, 128, (SaveFlags::Flags)(0));
947        canvas.drawRect(0, 0, 200, 200, SkPaint());
948        canvas.restore();
949    });
950
951    // draw with partial screen dirty, and assert we see that rect later
952    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeLTRB(50, 50, 150, 150), 200, 200,
953            TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
954    SaveLayerUnclippedClearClipTestRenderer renderer;
955    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
956    EXPECT_EQ(4, renderer.getIndex());
957}
958
959RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_reject) {
960    auto node = TestUtils::createNode(0, 0, 200, 200,
961            [](RenderProperties& props, RecordingCanvas& canvas) {
962        // unclipped savelayer + rect both in area that won't intersect with dirty
963        canvas.saveLayerAlpha(100, 100, 200, 200, 128, (SaveFlags::Flags)(0));
964        canvas.drawRect(100, 100, 200, 200, SkPaint());
965        canvas.restore();
966    });
967
968    // draw with partial screen dirty that doesn't intersect with savelayer
969    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 200, 200,
970            TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
971    FailRenderer renderer;
972    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
973}
974
975/* saveLayerUnclipped { saveLayer { saveLayerUnclipped { rect } } } will play back as:
976 * - startTemporaryLayer, onCopyToLayer, onSimpleRects, onRect, onCopyFromLayer, endLayer
977 * - startFrame, onCopyToLayer, onSimpleRects, drawLayer, onCopyFromLayer, endframe
978 */
979RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_complex) {
980    class SaveLayerUnclippedComplexTestRenderer : public TestRendererBase {
981    public:
982        OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) {
983            EXPECT_EQ(0, mIndex++); // savelayer first
984            return (OffscreenBuffer*)0xabcd;
985        }
986        void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override {
987            int index = mIndex++;
988            EXPECT_TRUE(index == 1 || index == 7);
989        }
990        void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override {
991            int index = mIndex++;
992            EXPECT_TRUE(index == 2 || index == 8);
993        }
994        void onRectOp(const RectOp& op, const BakedOpState& state) override {
995            EXPECT_EQ(3, mIndex++);
996            Matrix4 expected;
997            expected.loadTranslate(-100, -100, 0);
998            EXPECT_EQ(Rect(100, 100, 200, 200), state.computedState.clippedBounds);
999            EXPECT_MATRIX_APPROX_EQ(expected, state.computedState.transform);
1000        }
1001        void onCopyFromLayerOp(const CopyFromLayerOp& op, const BakedOpState& state) override {
1002            int index = mIndex++;
1003            EXPECT_TRUE(index == 4 || index == 10);
1004        }
1005        void endLayer() override {
1006            EXPECT_EQ(5, mIndex++);
1007        }
1008        void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
1009            EXPECT_EQ(6, mIndex++);
1010        }
1011        void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
1012            EXPECT_EQ(9, mIndex++);
1013        }
1014        void endFrame(const Rect& repaintRect) override {
1015            EXPECT_EQ(11, mIndex++);
1016        }
1017    };
1018
1019    auto node = TestUtils::createNode(0, 0, 600, 600, // 500x500 triggers clipping
1020            [](RenderProperties& props, RecordingCanvas& canvas) {
1021        canvas.saveLayerAlpha(0, 0, 500, 500, 128, (SaveFlags::Flags)0); // unclipped
1022        canvas.saveLayerAlpha(100, 100, 400, 400, 128, SaveFlags::ClipToLayer); // clipped
1023        canvas.saveLayerAlpha(200, 200, 300, 300, 128, (SaveFlags::Flags)0); // unclipped
1024        canvas.drawRect(200, 200, 300, 300, SkPaint());
1025        canvas.restore();
1026        canvas.restore();
1027        canvas.restore();
1028    });
1029    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(600, 600), 600, 600,
1030            TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
1031    SaveLayerUnclippedComplexTestRenderer renderer;
1032    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1033    EXPECT_EQ(12, renderer.getIndex());
1034}
1035
1036RENDERTHREAD_TEST(FrameBuilder, hwLayer_simple) {
1037    class HwLayerSimpleTestRenderer : public TestRendererBase {
1038    public:
1039        void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override {
1040            EXPECT_EQ(0, mIndex++);
1041            EXPECT_EQ(100u, offscreenBuffer->viewportWidth);
1042            EXPECT_EQ(100u, offscreenBuffer->viewportHeight);
1043            EXPECT_EQ(Rect(25, 25, 75, 75), repaintRect);
1044        }
1045        void onRectOp(const RectOp& op, const BakedOpState& state) override {
1046            EXPECT_EQ(1, mIndex++);
1047
1048            EXPECT_TRUE(state.computedState.transform.isIdentity())
1049                    << "Transform should be reset within layer";
1050
1051            EXPECT_EQ(Rect(25, 25, 75, 75), state.computedState.clipRect())
1052                    << "Damage rect should be used to clip layer content";
1053        }
1054        void endLayer() override {
1055            EXPECT_EQ(2, mIndex++);
1056        }
1057        void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
1058            EXPECT_EQ(3, mIndex++);
1059        }
1060        void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
1061            EXPECT_EQ(4, mIndex++);
1062        }
1063        void endFrame(const Rect& repaintRect) override {
1064            EXPECT_EQ(5, mIndex++);
1065        }
1066    };
1067
1068    auto node = TestUtils::createNode(10, 10, 110, 110,
1069            [](RenderProperties& props, RecordingCanvas& canvas) {
1070        props.mutateLayerProperties().setType(LayerType::RenderLayer);
1071        SkPaint paint;
1072        paint.setColor(SK_ColorWHITE);
1073        canvas.drawRect(0, 0, 100, 100, paint);
1074    });
1075    OffscreenBuffer** layerHandle = node->getLayerHandle();
1076
1077    // create RenderNode's layer here in same way prepareTree would
1078    OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 100, 100);
1079    *layerHandle = &layer;
1080
1081    auto syncedNodeList = TestUtils::createSyncedNodeList(node);
1082
1083    // only enqueue partial damage
1084    LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
1085    layerUpdateQueue.enqueueLayerWithDamage(node.get(), Rect(25, 25, 75, 75));
1086
1087    FrameBuilder frameBuilder(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
1088            syncedNodeList, sLightGeometry, Caches::getInstance());
1089    HwLayerSimpleTestRenderer renderer;
1090    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1091    EXPECT_EQ(6, renderer.getIndex());
1092
1093    // clean up layer pointer, so we can safely destruct RenderNode
1094    *layerHandle = nullptr;
1095}
1096
1097RENDERTHREAD_TEST(FrameBuilder, hwLayer_complex) {
1098    /* parentLayer { greyRect, saveLayer { childLayer { whiteRect } } } will play back as:
1099     * - startRepaintLayer(child), rect(grey), endLayer
1100     * - startTemporaryLayer, drawLayer(child), endLayer
1101     * - startRepaintLayer(parent), rect(white), drawLayer(saveLayer), endLayer
1102     * - startFrame, drawLayer(parent), endLayerb
1103     */
1104    class HwLayerComplexTestRenderer : public TestRendererBase {
1105    public:
1106        OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) {
1107            EXPECT_EQ(3, mIndex++); // savelayer first
1108            return (OffscreenBuffer*)0xabcd;
1109        }
1110        void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override {
1111            int index = mIndex++;
1112            if (index == 0) {
1113                // starting inner layer
1114                EXPECT_EQ(100u, offscreenBuffer->viewportWidth);
1115                EXPECT_EQ(100u, offscreenBuffer->viewportHeight);
1116            } else if (index == 6) {
1117                // starting outer layer
1118                EXPECT_EQ(200u, offscreenBuffer->viewportWidth);
1119                EXPECT_EQ(200u, offscreenBuffer->viewportHeight);
1120            } else { ADD_FAILURE(); }
1121        }
1122        void onRectOp(const RectOp& op, const BakedOpState& state) override {
1123            int index = mIndex++;
1124            if (index == 1) {
1125                // inner layer's rect (white)
1126                EXPECT_EQ(SK_ColorWHITE, op.paint->getColor());
1127            } else if (index == 7) {
1128                // outer layer's rect (grey)
1129                EXPECT_EQ(SK_ColorDKGRAY, op.paint->getColor());
1130            } else { ADD_FAILURE(); }
1131        }
1132        void endLayer() override {
1133            int index = mIndex++;
1134            EXPECT_TRUE(index == 2 || index == 5 || index == 9);
1135        }
1136        void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
1137            EXPECT_EQ(10, mIndex++);
1138        }
1139        void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
1140            OffscreenBuffer* layer = *op.layerHandle;
1141            int index = mIndex++;
1142            if (index == 4) {
1143                EXPECT_EQ(100u, layer->viewportWidth);
1144                EXPECT_EQ(100u, layer->viewportHeight);
1145            } else if (index == 8) {
1146                EXPECT_EQ((OffscreenBuffer*)0xabcd, *op.layerHandle);
1147            } else if (index == 11) {
1148                EXPECT_EQ(200u, layer->viewportWidth);
1149                EXPECT_EQ(200u, layer->viewportHeight);
1150            } else { ADD_FAILURE(); }
1151        }
1152        void endFrame(const Rect& repaintRect) override {
1153            EXPECT_EQ(12, mIndex++);
1154        }
1155    };
1156
1157    auto child = TestUtils::createNode(50, 50, 150, 150,
1158            [](RenderProperties& props, RecordingCanvas& canvas) {
1159        props.mutateLayerProperties().setType(LayerType::RenderLayer);
1160        SkPaint paint;
1161        paint.setColor(SK_ColorWHITE);
1162        canvas.drawRect(0, 0, 100, 100, paint);
1163    });
1164    OffscreenBuffer childLayer(renderThread.renderState(), Caches::getInstance(), 100, 100);
1165    *(child->getLayerHandle()) = &childLayer;
1166
1167    RenderNode* childPtr = child.get();
1168    auto parent = TestUtils::createNode(0, 0, 200, 200,
1169            [childPtr](RenderProperties& props, RecordingCanvas& canvas) {
1170        props.mutateLayerProperties().setType(LayerType::RenderLayer);
1171        SkPaint paint;
1172        paint.setColor(SK_ColorDKGRAY);
1173        canvas.drawRect(0, 0, 200, 200, paint);
1174
1175        canvas.saveLayerAlpha(50, 50, 150, 150, 128, SaveFlags::ClipToLayer);
1176        canvas.drawRenderNode(childPtr);
1177        canvas.restore();
1178    });
1179    OffscreenBuffer parentLayer(renderThread.renderState(), Caches::getInstance(), 200, 200);
1180    *(parent->getLayerHandle()) = &parentLayer;
1181
1182    auto syncedList = TestUtils::createSyncedNodeList(parent);
1183
1184    LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
1185    layerUpdateQueue.enqueueLayerWithDamage(child.get(), Rect(100, 100));
1186    layerUpdateQueue.enqueueLayerWithDamage(parent.get(), Rect(200, 200));
1187
1188    FrameBuilder frameBuilder(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
1189            syncedList, sLightGeometry, Caches::getInstance());
1190    HwLayerComplexTestRenderer renderer;
1191    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1192    EXPECT_EQ(13, renderer.getIndex());
1193
1194    // clean up layer pointers, so we can safely destruct RenderNodes
1195    *(child->getLayerHandle()) = nullptr;
1196    *(parent->getLayerHandle()) = nullptr;
1197}
1198
1199
1200RENDERTHREAD_TEST(FrameBuilder, buildLayer) {
1201    class BuildLayerTestRenderer : public TestRendererBase {
1202    public:
1203        void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override {
1204            EXPECT_EQ(0, mIndex++);
1205            EXPECT_EQ(100u, offscreenBuffer->viewportWidth);
1206            EXPECT_EQ(100u, offscreenBuffer->viewportHeight);
1207            EXPECT_EQ(Rect(25, 25, 75, 75), repaintRect);
1208        }
1209        void onColorOp(const ColorOp& op, const BakedOpState& state) override {
1210            EXPECT_EQ(1, mIndex++);
1211
1212            EXPECT_TRUE(state.computedState.transform.isIdentity())
1213                    << "Transform should be reset within layer";
1214
1215            EXPECT_EQ(Rect(25, 25, 75, 75), state.computedState.clipRect())
1216                    << "Damage rect should be used to clip layer content";
1217        }
1218        void endLayer() override {
1219            EXPECT_EQ(2, mIndex++);
1220        }
1221        void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
1222            ADD_FAILURE() << "Primary frame draw not expected in this test";
1223        }
1224        void endFrame(const Rect& repaintRect) override {
1225            ADD_FAILURE() << "Primary frame draw not expected in this test";
1226        }
1227    };
1228
1229    auto node = TestUtils::createNode(10, 10, 110, 110,
1230            [](RenderProperties& props, RecordingCanvas& canvas) {
1231        props.mutateLayerProperties().setType(LayerType::RenderLayer);
1232        canvas.drawColor(SK_ColorWHITE, SkXfermode::Mode::kSrcOver_Mode);
1233    });
1234    OffscreenBuffer** layerHandle = node->getLayerHandle();
1235
1236    // create RenderNode's layer here in same way prepareTree would
1237    OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 100, 100);
1238    *layerHandle = &layer;
1239
1240    auto syncedNodeList = TestUtils::createSyncedNodeList(node);
1241
1242    // only enqueue partial damage
1243    LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
1244    layerUpdateQueue.enqueueLayerWithDamage(node.get(), Rect(25, 25, 75, 75));
1245
1246    // Draw, but pass empty node list, so no work is done for primary frame
1247    FrameBuilder frameBuilder(layerUpdateQueue, SkRect::MakeWH(1, 1), 1, 1,
1248            sEmptyNodeList, sLightGeometry, Caches::getInstance());
1249    BuildLayerTestRenderer renderer;
1250    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1251    EXPECT_EQ(3, renderer.getIndex());
1252
1253    // clean up layer pointer, so we can safely destruct RenderNode
1254    *layerHandle = nullptr;
1255}
1256
1257static void drawOrderedRect(RecordingCanvas* canvas, uint8_t expectedDrawOrder) {
1258    SkPaint paint;
1259    paint.setColor(SkColorSetARGB(256, 0, 0, expectedDrawOrder)); // order put in blue channel
1260    canvas->drawRect(0, 0, 100, 100, paint);
1261}
1262static void drawOrderedNode(RecordingCanvas* canvas, uint8_t expectedDrawOrder, float z) {
1263    auto node = TestUtils::createNode(0, 0, 100, 100,
1264            [expectedDrawOrder](RenderProperties& props, RecordingCanvas& canvas) {
1265        drawOrderedRect(&canvas, expectedDrawOrder);
1266    });
1267    node->mutateStagingProperties().setTranslationZ(z);
1268    node->setPropertyFieldsDirty(RenderNode::TRANSLATION_Z);
1269    canvas->drawRenderNode(node.get()); // canvas takes reference/sole ownership
1270}
1271RENDERTHREAD_TEST(FrameBuilder, zReorder) {
1272    class ZReorderTestRenderer : public TestRendererBase {
1273    public:
1274        void onRectOp(const RectOp& op, const BakedOpState& state) override {
1275            int expectedOrder = SkColorGetB(op.paint->getColor()); // extract order from blue channel
1276            EXPECT_EQ(expectedOrder, mIndex++) << "An op was drawn out of order";
1277        }
1278    };
1279
1280    auto parent = TestUtils::createNode(0, 0, 100, 100,
1281            [](RenderProperties& props, RecordingCanvas& canvas) {
1282        drawOrderedNode(&canvas, 0, 10.0f); // in reorder=false at this point, so played inorder
1283        drawOrderedRect(&canvas, 1);
1284        canvas.insertReorderBarrier(true);
1285        drawOrderedNode(&canvas, 6, 2.0f);
1286        drawOrderedRect(&canvas, 3);
1287        drawOrderedNode(&canvas, 4, 0.0f);
1288        drawOrderedRect(&canvas, 5);
1289        drawOrderedNode(&canvas, 2, -2.0f);
1290        drawOrderedNode(&canvas, 7, 2.0f);
1291        canvas.insertReorderBarrier(false);
1292        drawOrderedRect(&canvas, 8);
1293        drawOrderedNode(&canvas, 9, -10.0f); // in reorder=false at this point, so played inorder
1294    });
1295    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100,
1296            TestUtils::createSyncedNodeList(parent), sLightGeometry, Caches::getInstance());
1297    ZReorderTestRenderer renderer;
1298    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1299    EXPECT_EQ(10, renderer.getIndex());
1300};
1301
1302RENDERTHREAD_TEST(FrameBuilder, projectionReorder) {
1303    static const int scrollX = 5;
1304    static const int scrollY = 10;
1305    class ProjectionReorderTestRenderer : public TestRendererBase {
1306    public:
1307        void onRectOp(const RectOp& op, const BakedOpState& state) override {
1308            const int index = mIndex++;
1309
1310            Matrix4 expectedMatrix;
1311            switch (index) {
1312            case 0:
1313                EXPECT_EQ(Rect(100, 100), op.unmappedBounds);
1314                EXPECT_EQ(SK_ColorWHITE, op.paint->getColor());
1315                expectedMatrix.loadIdentity();
1316                EXPECT_EQ(nullptr, state.computedState.localProjectionPathMask);
1317                break;
1318            case 1:
1319                EXPECT_EQ(Rect(-10, -10, 60, 60), op.unmappedBounds);
1320                EXPECT_EQ(SK_ColorDKGRAY, op.paint->getColor());
1321                expectedMatrix.loadTranslate(50 - scrollX, 50 - scrollY, 0);
1322                ASSERT_NE(nullptr, state.computedState.localProjectionPathMask);
1323                EXPECT_EQ(Rect(-35, -30, 45, 50),
1324                        Rect(state.computedState.localProjectionPathMask->getBounds()));
1325                break;
1326            case 2:
1327                EXPECT_EQ(Rect(100, 50), op.unmappedBounds);
1328                EXPECT_EQ(SK_ColorBLUE, op.paint->getColor());
1329                expectedMatrix.loadTranslate(-scrollX, 50 - scrollY, 0);
1330                EXPECT_EQ(nullptr, state.computedState.localProjectionPathMask);
1331                break;
1332            default:
1333                ADD_FAILURE();
1334            }
1335            EXPECT_EQ(expectedMatrix, state.computedState.transform);
1336        }
1337    };
1338
1339    /**
1340     * Construct a tree of nodes, where the root (A) has a receiver background (B), and a child (C)
1341     * with a projecting child (P) of its own. P would normally draw between B and C's "background"
1342     * draw, but because it is projected backwards, it's drawn in between B and C.
1343     *
1344     * The parent is scrolled by scrollX/scrollY, but this does not affect the background
1345     * (which isn't affected by scroll).
1346     */
1347    auto receiverBackground = TestUtils::createNode(0, 0, 100, 100,
1348            [](RenderProperties& properties, RecordingCanvas& canvas) {
1349        properties.setProjectionReceiver(true);
1350        // scroll doesn't apply to background, so undone via translationX/Y
1351        // NOTE: translationX/Y only! no other transform properties may be set for a proj receiver!
1352        properties.setTranslationX(scrollX);
1353        properties.setTranslationY(scrollY);
1354
1355        SkPaint paint;
1356        paint.setColor(SK_ColorWHITE);
1357        canvas.drawRect(0, 0, 100, 100, paint);
1358    });
1359    auto projectingRipple = TestUtils::createNode(50, 0, 100, 50,
1360            [](RenderProperties& properties, RecordingCanvas& canvas) {
1361        properties.setProjectBackwards(true);
1362        properties.setClipToBounds(false);
1363        SkPaint paint;
1364        paint.setColor(SK_ColorDKGRAY);
1365        canvas.drawRect(-10, -10, 60, 60, paint);
1366    });
1367    auto child = TestUtils::createNode(0, 50, 100, 100,
1368            [&projectingRipple](RenderProperties& properties, RecordingCanvas& canvas) {
1369        SkPaint paint;
1370        paint.setColor(SK_ColorBLUE);
1371        canvas.drawRect(0, 0, 100, 50, paint);
1372        canvas.drawRenderNode(projectingRipple.get());
1373    });
1374    auto parent = TestUtils::createNode(0, 0, 100, 100,
1375            [&receiverBackground, &child](RenderProperties& properties, RecordingCanvas& canvas) {
1376        // Set a rect outline for the projecting ripple to be masked against.
1377        properties.mutableOutline().setRoundRect(10, 10, 90, 90, 5, 1.0f);
1378
1379        canvas.save(SaveFlags::MatrixClip);
1380        canvas.translate(-scrollX, -scrollY); // Apply scroll (note: bg undoes this internally)
1381        canvas.drawRenderNode(receiverBackground.get());
1382        canvas.drawRenderNode(child.get());
1383        canvas.restore();
1384    });
1385
1386    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100,
1387            TestUtils::createSyncedNodeList(parent), sLightGeometry, Caches::getInstance());
1388    ProjectionReorderTestRenderer renderer;
1389    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1390    EXPECT_EQ(3, renderer.getIndex());
1391}
1392
1393RENDERTHREAD_TEST(FrameBuilder, projectionHwLayer) {
1394    static const int scrollX = 5;
1395    static const int scrollY = 10;
1396    class ProjectionHwLayerTestRenderer : public TestRendererBase {
1397    public:
1398        void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override {
1399            EXPECT_EQ(0, mIndex++);
1400        }
1401        void onArcOp(const ArcOp& op, const BakedOpState& state) override {
1402            EXPECT_EQ(1, mIndex++);
1403            ASSERT_EQ(nullptr, state.computedState.localProjectionPathMask);
1404        }
1405        void endLayer() override {
1406            EXPECT_EQ(2, mIndex++);
1407        }
1408        void onRectOp(const RectOp& op, const BakedOpState& state) override {
1409            EXPECT_EQ(3, mIndex++);
1410            ASSERT_EQ(nullptr, state.computedState.localProjectionPathMask);
1411        }
1412        void onOvalOp(const OvalOp& op, const BakedOpState& state) override {
1413            EXPECT_EQ(4, mIndex++);
1414            ASSERT_NE(nullptr, state.computedState.localProjectionPathMask);
1415            Matrix4 expected;
1416            expected.loadTranslate(100 - scrollX, 100 - scrollY, 0);
1417            EXPECT_EQ(expected, state.computedState.transform);
1418            EXPECT_EQ(Rect(-85, -80, 295, 300),
1419                    Rect(state.computedState.localProjectionPathMask->getBounds()));
1420        }
1421        void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
1422            EXPECT_EQ(5, mIndex++);
1423            ASSERT_EQ(nullptr, state.computedState.localProjectionPathMask);
1424        }
1425    };
1426    auto receiverBackground = TestUtils::createNode(0, 0, 400, 400,
1427            [](RenderProperties& properties, RecordingCanvas& canvas) {
1428        properties.setProjectionReceiver(true);
1429        // scroll doesn't apply to background, so undone via translationX/Y
1430        // NOTE: translationX/Y only! no other transform properties may be set for a proj receiver!
1431        properties.setTranslationX(scrollX);
1432        properties.setTranslationY(scrollY);
1433
1434        canvas.drawRect(0, 0, 400, 400, SkPaint());
1435    });
1436    auto projectingRipple = TestUtils::createNode(0, 0, 200, 200,
1437            [](RenderProperties& properties, RecordingCanvas& canvas) {
1438        properties.setProjectBackwards(true);
1439        properties.setClipToBounds(false);
1440        canvas.drawOval(100, 100, 300, 300, SkPaint()); // drawn mostly out of layer bounds
1441    });
1442    auto child = TestUtils::createNode(100, 100, 300, 300,
1443            [&projectingRipple](RenderProperties& properties, RecordingCanvas& canvas) {
1444        properties.mutateLayerProperties().setType(LayerType::RenderLayer);
1445        canvas.drawRenderNode(projectingRipple.get());
1446        canvas.drawArc(0, 0, 200, 200, 0.0f, 280.0f, true, SkPaint());
1447    });
1448    auto parent = TestUtils::createNode(0, 0, 400, 400,
1449            [&receiverBackground, &child](RenderProperties& properties, RecordingCanvas& canvas) {
1450        // Set a rect outline for the projecting ripple to be masked against.
1451        properties.mutableOutline().setRoundRect(10, 10, 390, 390, 0, 1.0f);
1452        canvas.translate(-scrollX, -scrollY); // Apply scroll (note: bg undoes this internally)
1453        canvas.drawRenderNode(receiverBackground.get());
1454        canvas.drawRenderNode(child.get());
1455    });
1456
1457    OffscreenBuffer** layerHandle = child->getLayerHandle();
1458
1459    // create RenderNode's layer here in same way prepareTree would, setting windowTransform
1460    OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 200, 200);
1461    Matrix4 windowTransform;
1462    windowTransform.loadTranslate(100, 100, 0); // total transform of layer's origin
1463    layer.setWindowTransform(windowTransform);
1464    *layerHandle = &layer;
1465
1466    auto syncedList = TestUtils::createSyncedNodeList(parent);
1467    LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
1468    layerUpdateQueue.enqueueLayerWithDamage(child.get(), Rect(200, 200));
1469    FrameBuilder frameBuilder(layerUpdateQueue, SkRect::MakeWH(400, 400), 400, 400,
1470            syncedList, sLightGeometry, Caches::getInstance());
1471    ProjectionHwLayerTestRenderer renderer;
1472    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1473    EXPECT_EQ(6, renderer.getIndex());
1474
1475    // clean up layer pointer, so we can safely destruct RenderNode
1476    *layerHandle = nullptr;
1477}
1478
1479RENDERTHREAD_TEST(FrameBuilder, projectionChildScroll) {
1480    static const int scrollX = 500000;
1481    static const int scrollY = 0;
1482    class ProjectionChildScrollTestRenderer : public TestRendererBase {
1483    public:
1484        void onRectOp(const RectOp& op, const BakedOpState& state) override {
1485            EXPECT_EQ(0, mIndex++);
1486            EXPECT_TRUE(state.computedState.transform.isIdentity());
1487        }
1488        void onOvalOp(const OvalOp& op, const BakedOpState& state) override {
1489            EXPECT_EQ(1, mIndex++);
1490            ASSERT_NE(nullptr, state.computedState.clipState);
1491            ASSERT_EQ(ClipMode::Rectangle, state.computedState.clipState->mode);
1492            ASSERT_EQ(Rect(400, 400), state.computedState.clipState->rect);
1493            EXPECT_TRUE(state.computedState.transform.isIdentity());
1494        }
1495    };
1496    auto receiverBackground = TestUtils::createNode(0, 0, 400, 400,
1497            [](RenderProperties& properties, RecordingCanvas& canvas) {
1498        properties.setProjectionReceiver(true);
1499        canvas.drawRect(0, 0, 400, 400, SkPaint());
1500    });
1501    auto projectingRipple = TestUtils::createNode(0, 0, 200, 200,
1502            [](RenderProperties& properties, RecordingCanvas& canvas) {
1503        // scroll doesn't apply to background, so undone via translationX/Y
1504        // NOTE: translationX/Y only! no other transform properties may be set for a proj receiver!
1505        properties.setTranslationX(scrollX);
1506        properties.setTranslationY(scrollY);
1507        properties.setProjectBackwards(true);
1508        properties.setClipToBounds(false);
1509        canvas.drawOval(0, 0, 200, 200, SkPaint());
1510    });
1511    auto child = TestUtils::createNode(0, 0, 400, 400,
1512            [&projectingRipple](RenderProperties& properties, RecordingCanvas& canvas) {
1513        // Record time clip will be ignored by projectee
1514        canvas.clipRect(100, 100, 300, 300, SkRegion::kIntersect_Op);
1515
1516        canvas.translate(-scrollX, -scrollY); // Apply scroll (note: bg undoes this internally)
1517        canvas.drawRenderNode(projectingRipple.get());
1518    });
1519    auto parent = TestUtils::createNode(0, 0, 400, 400,
1520            [&receiverBackground, &child](RenderProperties& properties, RecordingCanvas& canvas) {
1521        canvas.drawRenderNode(receiverBackground.get());
1522        canvas.drawRenderNode(child.get());
1523    });
1524
1525    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(400, 400), 400, 400,
1526            TestUtils::createSyncedNodeList(parent), sLightGeometry, Caches::getInstance());
1527    ProjectionChildScrollTestRenderer renderer;
1528    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1529    EXPECT_EQ(2, renderer.getIndex());
1530}
1531
1532// creates a 100x100 shadow casting node with provided translationZ
1533static sp<RenderNode> createWhiteRectShadowCaster(float translationZ) {
1534    return TestUtils::createNode(0, 0, 100, 100,
1535            [translationZ](RenderProperties& properties, RecordingCanvas& canvas) {
1536        properties.setTranslationZ(translationZ);
1537        properties.mutableOutline().setRoundRect(0, 0, 100, 100, 0.0f, 1.0f);
1538        SkPaint paint;
1539        paint.setColor(SK_ColorWHITE);
1540        canvas.drawRect(0, 0, 100, 100, paint);
1541    });
1542}
1543
1544RENDERTHREAD_TEST(FrameBuilder, shadow) {
1545    class ShadowTestRenderer : public TestRendererBase {
1546    public:
1547        void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
1548            EXPECT_EQ(0, mIndex++);
1549            EXPECT_FLOAT_EQ(1.0f, op.casterAlpha);
1550            EXPECT_TRUE(op.shadowTask->casterPerimeter.isRect(nullptr));
1551            EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), op.shadowTask->transformXY);
1552
1553            Matrix4 expectedZ;
1554            expectedZ.loadTranslate(0, 0, 5);
1555            EXPECT_MATRIX_APPROX_EQ(expectedZ, op.shadowTask->transformZ);
1556        }
1557        void onRectOp(const RectOp& op, const BakedOpState& state) override {
1558            EXPECT_EQ(1, mIndex++);
1559        }
1560    };
1561
1562    auto parent = TestUtils::createNode(0, 0, 200, 200,
1563            [](RenderProperties& props, RecordingCanvas& canvas) {
1564        canvas.insertReorderBarrier(true);
1565        canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
1566    });
1567
1568    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
1569            TestUtils::createSyncedNodeList(parent), sLightGeometry, Caches::getInstance());
1570    ShadowTestRenderer renderer;
1571    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1572    EXPECT_EQ(2, renderer.getIndex());
1573}
1574
1575RENDERTHREAD_TEST(FrameBuilder, shadowSaveLayer) {
1576    class ShadowSaveLayerTestRenderer : public TestRendererBase {
1577    public:
1578        OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override {
1579            EXPECT_EQ(0, mIndex++);
1580            return nullptr;
1581        }
1582        void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
1583            EXPECT_EQ(1, mIndex++);
1584            EXPECT_FLOAT_EQ(50, op.shadowTask->lightCenter.x);
1585            EXPECT_FLOAT_EQ(40, op.shadowTask->lightCenter.y);
1586        }
1587        void onRectOp(const RectOp& op, const BakedOpState& state) override {
1588            EXPECT_EQ(2, mIndex++);
1589        }
1590        void endLayer() override {
1591            EXPECT_EQ(3, mIndex++);
1592        }
1593        void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
1594            EXPECT_EQ(4, mIndex++);
1595        }
1596    };
1597
1598    auto parent = TestUtils::createNode(0, 0, 200, 200,
1599            [](RenderProperties& props, RecordingCanvas& canvas) {
1600        // save/restore outside of reorderBarrier, so they don't get moved out of place
1601        canvas.translate(20, 10);
1602        int count = canvas.saveLayerAlpha(30, 50, 130, 150, 128, SaveFlags::ClipToLayer);
1603        canvas.insertReorderBarrier(true);
1604        canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
1605        canvas.insertReorderBarrier(false);
1606        canvas.restoreToCount(count);
1607    });
1608
1609    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
1610            TestUtils::createSyncedNodeList(parent),
1611            (FrameBuilder::LightGeometry) {{ 100, 100, 100 }, 50}, Caches::getInstance());
1612    ShadowSaveLayerTestRenderer renderer;
1613    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1614    EXPECT_EQ(5, renderer.getIndex());
1615}
1616
1617RENDERTHREAD_TEST(FrameBuilder, shadowHwLayer) {
1618    class ShadowHwLayerTestRenderer : public TestRendererBase {
1619    public:
1620        void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override {
1621            EXPECT_EQ(0, mIndex++);
1622        }
1623        void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
1624            EXPECT_EQ(1, mIndex++);
1625            EXPECT_FLOAT_EQ(50, op.shadowTask->lightCenter.x);
1626            EXPECT_FLOAT_EQ(40, op.shadowTask->lightCenter.y);
1627            EXPECT_FLOAT_EQ(30, op.shadowTask->lightRadius);
1628        }
1629        void onRectOp(const RectOp& op, const BakedOpState& state) override {
1630            EXPECT_EQ(2, mIndex++);
1631        }
1632        void endLayer() override {
1633            EXPECT_EQ(3, mIndex++);
1634        }
1635        void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
1636            EXPECT_EQ(4, mIndex++);
1637        }
1638    };
1639
1640    auto parent = TestUtils::createNode(50, 60, 150, 160,
1641            [](RenderProperties& props, RecordingCanvas& canvas) {
1642        props.mutateLayerProperties().setType(LayerType::RenderLayer);
1643        canvas.insertReorderBarrier(true);
1644        canvas.save(SaveFlags::MatrixClip);
1645        canvas.translate(20, 10);
1646        canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
1647        canvas.restore();
1648    });
1649    OffscreenBuffer** layerHandle = parent->getLayerHandle();
1650
1651    // create RenderNode's layer here in same way prepareTree would, setting windowTransform
1652    OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 100, 100);
1653    Matrix4 windowTransform;
1654    windowTransform.loadTranslate(50, 60, 0); // total transform of layer's origin
1655    layer.setWindowTransform(windowTransform);
1656    *layerHandle = &layer;
1657
1658    auto syncedList = TestUtils::createSyncedNodeList(parent);
1659    LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
1660    layerUpdateQueue.enqueueLayerWithDamage(parent.get(), Rect(100, 100));
1661    FrameBuilder frameBuilder(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
1662            syncedList,
1663            (FrameBuilder::LightGeometry) {{ 100, 100, 100 }, 30}, Caches::getInstance());
1664    ShadowHwLayerTestRenderer renderer;
1665    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1666    EXPECT_EQ(5, renderer.getIndex());
1667
1668    // clean up layer pointer, so we can safely destruct RenderNode
1669    *layerHandle = nullptr;
1670}
1671
1672RENDERTHREAD_TEST(FrameBuilder, shadowLayering) {
1673    class ShadowLayeringTestRenderer : public TestRendererBase {
1674    public:
1675        void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
1676            int index = mIndex++;
1677            EXPECT_TRUE(index == 0 || index == 1);
1678        }
1679        void onRectOp(const RectOp& op, const BakedOpState& state) override {
1680            int index = mIndex++;
1681            EXPECT_TRUE(index == 2 || index == 3);
1682        }
1683    };
1684    auto parent = TestUtils::createNode(0, 0, 200, 200,
1685            [](RenderProperties& props, RecordingCanvas& canvas) {
1686        canvas.insertReorderBarrier(true);
1687        canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
1688        canvas.drawRenderNode(createWhiteRectShadowCaster(5.0001f).get());
1689    });
1690
1691    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
1692            TestUtils::createSyncedNodeList(parent),
1693            (FrameBuilder::LightGeometry) {{ 100, 100, 100 }, 50}, Caches::getInstance());
1694    ShadowLayeringTestRenderer renderer;
1695    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1696    EXPECT_EQ(4, renderer.getIndex());
1697}
1698
1699static void testProperty(std::function<void(RenderProperties&)> propSetupCallback,
1700        std::function<void(const RectOp&, const BakedOpState&)> opValidateCallback) {
1701    class PropertyTestRenderer : public TestRendererBase {
1702    public:
1703        PropertyTestRenderer(std::function<void(const RectOp&, const BakedOpState&)> callback)
1704                : mCallback(callback) {}
1705        void onRectOp(const RectOp& op, const BakedOpState& state) override {
1706            EXPECT_EQ(mIndex++, 0);
1707            mCallback(op, state);
1708        }
1709        std::function<void(const RectOp&, const BakedOpState&)> mCallback;
1710    };
1711
1712    auto node = TestUtils::createNode(0, 0, 100, 100,
1713            [propSetupCallback](RenderProperties& props, RecordingCanvas& canvas) {
1714        propSetupCallback(props);
1715        SkPaint paint;
1716        paint.setColor(SK_ColorWHITE);
1717        canvas.drawRect(0, 0, 100, 100, paint);
1718    });
1719
1720    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 200, 200,
1721            TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
1722    PropertyTestRenderer renderer(opValidateCallback);
1723    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1724    EXPECT_EQ(1, renderer.getIndex()) << "Should have seen one op";
1725}
1726
1727RENDERTHREAD_TEST(FrameBuilder, renderPropOverlappingRenderingAlpha) {
1728    testProperty([](RenderProperties& properties) {
1729        properties.setAlpha(0.5f);
1730        properties.setHasOverlappingRendering(false);
1731    }, [](const RectOp& op, const BakedOpState& state) {
1732        EXPECT_EQ(0.5f, state.alpha) << "Alpha should be applied directly to op";
1733    });
1734}
1735
1736RENDERTHREAD_TEST(FrameBuilder, renderPropClipping) {
1737    testProperty([](RenderProperties& properties) {
1738        properties.setClipToBounds(true);
1739        properties.setClipBounds(Rect(10, 20, 300, 400));
1740    }, [](const RectOp& op, const BakedOpState& state) {
1741        EXPECT_EQ(Rect(10, 20, 100, 100), state.computedState.clippedBounds)
1742                << "Clip rect should be intersection of node bounds and clip bounds";
1743    });
1744}
1745
1746RENDERTHREAD_TEST(FrameBuilder, renderPropRevealClip) {
1747    testProperty([](RenderProperties& properties) {
1748        properties.mutableRevealClip().set(true, 50, 50, 25);
1749    }, [](const RectOp& op, const BakedOpState& state) {
1750        ASSERT_NE(nullptr, state.roundRectClipState);
1751        EXPECT_TRUE(state.roundRectClipState->highPriority);
1752        EXPECT_EQ(25, state.roundRectClipState->radius);
1753        EXPECT_EQ(Rect(50, 50, 50, 50), state.roundRectClipState->innerRect);
1754    });
1755}
1756
1757RENDERTHREAD_TEST(FrameBuilder, renderPropOutlineClip) {
1758    testProperty([](RenderProperties& properties) {
1759        properties.mutableOutline().setShouldClip(true);
1760        properties.mutableOutline().setRoundRect(10, 20, 30, 40, 5.0f, 0.5f);
1761    }, [](const RectOp& op, const BakedOpState& state) {
1762        ASSERT_NE(nullptr, state.roundRectClipState);
1763        EXPECT_FALSE(state.roundRectClipState->highPriority);
1764        EXPECT_EQ(5, state.roundRectClipState->radius);
1765        EXPECT_EQ(Rect(15, 25, 25, 35), state.roundRectClipState->innerRect);
1766    });
1767}
1768
1769RENDERTHREAD_TEST(FrameBuilder, renderPropTransform) {
1770    testProperty([](RenderProperties& properties) {
1771        properties.setLeftTopRightBottom(10, 10, 110, 110);
1772
1773        SkMatrix staticMatrix = SkMatrix::MakeScale(1.2f, 1.2f);
1774        properties.setStaticMatrix(&staticMatrix);
1775
1776        // ignored, since static overrides animation
1777        SkMatrix animationMatrix = SkMatrix::MakeTrans(15, 15);
1778        properties.setAnimationMatrix(&animationMatrix);
1779
1780        properties.setTranslationX(10);
1781        properties.setTranslationY(20);
1782        properties.setScaleX(0.5f);
1783        properties.setScaleY(0.7f);
1784    }, [](const RectOp& op, const BakedOpState& state) {
1785        Matrix4 matrix;
1786        matrix.loadTranslate(10, 10, 0); // left, top
1787        matrix.scale(1.2f, 1.2f, 1); // static matrix
1788        // ignore animation matrix, since static overrides it
1789
1790        // translation xy
1791        matrix.translate(10, 20);
1792
1793        // scale xy (from default pivot - center)
1794        matrix.translate(50, 50);
1795        matrix.scale(0.5f, 0.7f, 1);
1796        matrix.translate(-50, -50);
1797        EXPECT_MATRIX_APPROX_EQ(matrix, state.computedState.transform)
1798                << "Op draw matrix must match expected combination of transformation properties";
1799    });
1800}
1801
1802struct SaveLayerAlphaData {
1803    uint32_t layerWidth = 0;
1804    uint32_t layerHeight = 0;
1805    Rect rectClippedBounds;
1806    Matrix4 rectMatrix;
1807};
1808/**
1809 * Constructs a view to hit the temporary layer alpha property implementation:
1810 *     a) 0 < alpha < 1
1811 *     b) too big for layer (larger than maxTextureSize)
1812 *     c) overlapping rendering content
1813 * returning observed data about layer size and content clip/transform.
1814 *
1815 * Used to validate clipping behavior of temporary layer, where requested layer size is reduced
1816 * (for efficiency, and to fit in layer size constraints) based on parent clip.
1817 */
1818void testSaveLayerAlphaClip(SaveLayerAlphaData* outObservedData,
1819        std::function<void(RenderProperties&)> propSetupCallback) {
1820    class SaveLayerAlphaClipTestRenderer : public TestRendererBase {
1821    public:
1822        SaveLayerAlphaClipTestRenderer(SaveLayerAlphaData* outData)
1823                : mOutData(outData) {}
1824
1825        OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override {
1826            EXPECT_EQ(0, mIndex++);
1827            mOutData->layerWidth = width;
1828            mOutData->layerHeight = height;
1829            return nullptr;
1830        }
1831        void onRectOp(const RectOp& op, const BakedOpState& state) override {
1832            EXPECT_EQ(1, mIndex++);
1833
1834            mOutData->rectClippedBounds = state.computedState.clippedBounds;
1835            mOutData->rectMatrix = state.computedState.transform;
1836        }
1837        void endLayer() override {
1838            EXPECT_EQ(2, mIndex++);
1839        }
1840        void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
1841            EXPECT_EQ(3, mIndex++);
1842        }
1843    private:
1844        SaveLayerAlphaData* mOutData;
1845    };
1846
1847    ASSERT_GT(10000, DeviceInfo::get()->maxTextureSize())
1848            << "Node must be bigger than max texture size to exercise saveLayer codepath";
1849    auto node = TestUtils::createNode(0, 0, 10000, 10000,
1850            [&propSetupCallback](RenderProperties& properties, RecordingCanvas& canvas) {
1851        properties.setHasOverlappingRendering(true);
1852        properties.setAlpha(0.5f); // force saveLayer, since too big for HW layer
1853        // apply other properties
1854        propSetupCallback(properties);
1855
1856        SkPaint paint;
1857        paint.setColor(SK_ColorWHITE);
1858        canvas.drawRect(0, 0, 10000, 10000, paint);
1859    });
1860    auto nodes = TestUtils::createSyncedNodeList(node); // sync before querying height
1861
1862    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
1863            nodes, sLightGeometry, Caches::getInstance());
1864    SaveLayerAlphaClipTestRenderer renderer(outObservedData);
1865    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1866
1867    // assert, since output won't be valid if we haven't seen a save layer triggered
1868    ASSERT_EQ(4, renderer.getIndex()) << "Test must trigger saveLayer alpha behavior.";
1869}
1870
1871RENDERTHREAD_TEST(FrameBuilder, renderPropSaveLayerAlphaClipBig) {
1872    SaveLayerAlphaData observedData;
1873    testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) {
1874        properties.setTranslationX(10); // offset rendering content
1875        properties.setTranslationY(-2000); // offset rendering content
1876    });
1877    EXPECT_EQ(190u, observedData.layerWidth);
1878    EXPECT_EQ(200u, observedData.layerHeight);
1879    EXPECT_EQ(Rect(190, 200), observedData.rectClippedBounds)
1880            << "expect content to be clipped to screen area";
1881    Matrix4 expected;
1882    expected.loadTranslate(0, -2000, 0);
1883    EXPECT_MATRIX_APPROX_EQ(expected, observedData.rectMatrix)
1884            << "expect content to be translated as part of being clipped";
1885}
1886
1887RENDERTHREAD_TEST(FrameBuilder, renderPropSaveLayerAlphaRotate) {
1888    SaveLayerAlphaData observedData;
1889    testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) {
1890        // Translate and rotate the view so that the only visible part is the top left corner of
1891        // the view. It will form an isosceles right triangle with a long side length of 200 at the
1892        // bottom of the viewport.
1893        properties.setTranslationX(100);
1894        properties.setTranslationY(100);
1895        properties.setPivotX(0);
1896        properties.setPivotY(0);
1897        properties.setRotation(45);
1898    });
1899    // ceil(sqrt(2) / 2 * 200) = 142
1900    EXPECT_EQ(142u, observedData.layerWidth);
1901    EXPECT_EQ(142u, observedData.layerHeight);
1902    EXPECT_EQ(Rect(142, 142), observedData.rectClippedBounds);
1903    EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), observedData.rectMatrix);
1904}
1905
1906RENDERTHREAD_TEST(FrameBuilder, renderPropSaveLayerAlphaScale) {
1907    SaveLayerAlphaData observedData;
1908    testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) {
1909        properties.setPivotX(0);
1910        properties.setPivotY(0);
1911        properties.setScaleX(2);
1912        properties.setScaleY(0.5f);
1913    });
1914    EXPECT_EQ(100u, observedData.layerWidth);
1915    EXPECT_EQ(400u, observedData.layerHeight);
1916    EXPECT_EQ(Rect(100, 400), observedData.rectClippedBounds);
1917    EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), observedData.rectMatrix);
1918}
1919
1920} // namespace uirenderer
1921} // namespace android
1922