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