FrameBuilderTests.cpp revision d183e0b9886b46656a695a724cee6b9070e433ca
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, regionClipStopsMerge) {
481    class RegionClipStopsMergeTestRenderer : public TestRendererBase {
482    public:
483        void onTextOp(const TextOp& op, const BakedOpState& state) override { mIndex++; }
484    };
485    auto node = TestUtils::createNode(0, 0, 400, 400,
486            [](RenderProperties& props, TestCanvas& canvas) {
487        SkPath path;
488        path.addCircle(200, 200, 200, SkPath::kCW_Direction);
489        canvas.save(SaveFlags::MatrixClip);
490        canvas.clipPath(&path, SkRegion::kIntersect_Op);
491        SkPaint paint;
492        paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
493        paint.setAntiAlias(true);
494        paint.setTextSize(50);
495        TestUtils::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 100);
496        TestUtils::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 200);
497        canvas.restore();
498    });
499
500    FrameBuilder frameBuilder(SkRect::MakeWH(400, 400), 400, 400,
501            sLightGeometry, Caches::getInstance());
502    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
503
504    RegionClipStopsMergeTestRenderer renderer;
505    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
506    EXPECT_EQ(2, renderer.getIndex());
507}
508
509RENDERTHREAD_TEST(FrameBuilder, textMerging) {
510    class TextMergingTestRenderer : public TestRendererBase {
511    public:
512        void onMergedTextOps(const MergedBakedOpList& opList) override {
513            EXPECT_EQ(0, mIndex);
514            mIndex += opList.count;
515            EXPECT_EQ(2u, opList.count);
516            EXPECT_EQ(OpClipSideFlags::Top, opList.clipSideFlags);
517            EXPECT_EQ(OpClipSideFlags::Top, opList.states[0]->computedState.clipSideFlags);
518            EXPECT_EQ(OpClipSideFlags::None, opList.states[1]->computedState.clipSideFlags);
519        }
520    };
521    auto node = TestUtils::createNode(0, 0, 400, 400,
522            [](RenderProperties& props, TestCanvas& canvas) {
523        SkPaint paint;
524        paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
525        paint.setAntiAlias(true);
526        paint.setTextSize(50);
527        TestUtils::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 0); // will be top clipped
528        TestUtils::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 100); // not clipped
529    });
530    FrameBuilder frameBuilder(SkRect::MakeWH(400, 400), 400, 400,
531            sLightGeometry, Caches::getInstance());
532    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
533
534    TextMergingTestRenderer renderer;
535    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
536    EXPECT_EQ(2, renderer.getIndex()) << "Expect 2 ops";
537}
538
539RENDERTHREAD_TEST(FrameBuilder, textStrikethrough) {
540    const int LOOPS = 5;
541    class TextStrikethroughTestRenderer : public TestRendererBase {
542    public:
543        void onRectOp(const RectOp& op, const BakedOpState& state) override {
544            EXPECT_TRUE(mIndex++ >= LOOPS) << "Strikethrough rects should be above all text";
545        }
546        void onMergedTextOps(const MergedBakedOpList& opList) override {
547            EXPECT_EQ(0, mIndex);
548            mIndex += opList.count;
549            EXPECT_EQ(5u, opList.count);
550        }
551    };
552    auto node = TestUtils::createNode(0, 0, 200, 2000,
553            [](RenderProperties& props, RecordingCanvas& canvas) {
554        SkPaint textPaint;
555        textPaint.setAntiAlias(true);
556        textPaint.setTextSize(20);
557        textPaint.setStrikeThruText(true);
558        textPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
559        for (int i = 0; i < LOOPS; i++) {
560            TestUtils::drawUtf8ToCanvas(&canvas, "test text", textPaint, 10, 100 * (i + 1));
561        }
562    });
563
564    FrameBuilder frameBuilder(SkRect::MakeWH(200, 2000), 200, 2000,
565            sLightGeometry, Caches::getInstance());
566    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
567
568    TextStrikethroughTestRenderer renderer;
569    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
570    EXPECT_EQ(2 * LOOPS, renderer.getIndex())
571            << "Expect number of ops = 2 * loop count";
572}
573
574static auto styles = {
575        SkPaint::kFill_Style, SkPaint::kStroke_Style, SkPaint::kStrokeAndFill_Style };
576
577RENDERTHREAD_TEST(FrameBuilder, textStyle) {
578    class TextStyleTestRenderer : public TestRendererBase {
579    public:
580        void onMergedTextOps(const MergedBakedOpList& opList) override {
581            ASSERT_EQ(0, mIndex);
582            ASSERT_EQ(3u, opList.count);
583            mIndex += opList.count;
584
585            int index = 0;
586            for (auto style : styles) {
587                auto state = opList.states[index++];
588                ASSERT_EQ(style, state->op->paint->getStyle())
589                        << "Remainder of validation relies upon stable merged order";
590                ASSERT_EQ(0, state->computedState.clipSideFlags)
591                        << "Clipped bounds validation requires unclipped ops";
592            }
593
594            Rect fill = opList.states[0]->computedState.clippedBounds;
595            Rect stroke = opList.states[1]->computedState.clippedBounds;
596            EXPECT_EQ(stroke, opList.states[2]->computedState.clippedBounds)
597                    << "Stroke+Fill should be same as stroke";
598
599            EXPECT_TRUE(stroke.contains(fill));
600            EXPECT_FALSE(fill.contains(stroke));
601
602            // outset by half the stroke width
603            Rect outsetFill(fill);
604            outsetFill.outset(5);
605            EXPECT_EQ(stroke, outsetFill);
606        }
607    };
608    auto node = TestUtils::createNode(0, 0, 400, 400,
609            [](RenderProperties& props, TestCanvas& canvas) {
610        SkPaint paint;
611        paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
612        paint.setAntiAlias(true);
613        paint.setTextSize(50);
614        paint.setStrokeWidth(10);
615
616        // draw 3 copies of the same text overlapping, each with a different style.
617        // They'll get merged, but with
618        for (auto style : styles) {
619            paint.setStyle(style);
620            TestUtils::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 100);
621        }
622    });
623    FrameBuilder frameBuilder(SkRect::MakeWH(400, 400), 400, 400,
624            sLightGeometry, Caches::getInstance());
625    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
626    TextStyleTestRenderer renderer;
627    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
628    EXPECT_EQ(3, renderer.getIndex()) << "Expect 3 ops";
629}
630
631RENDERTHREAD_TEST(FrameBuilder, textureLayer_clipLocalMatrix) {
632    class TextureLayerClipLocalMatrixTestRenderer : public TestRendererBase {
633    public:
634        void onTextureLayerOp(const TextureLayerOp& op, const BakedOpState& state) override {
635            EXPECT_EQ(0, mIndex++);
636            EXPECT_EQ(Rect(50, 50, 150, 150), state.computedState.clipRect());
637            EXPECT_EQ(Rect(50, 50, 105, 105), state.computedState.clippedBounds);
638
639            Matrix4 expected;
640            expected.loadTranslate(5, 5, 0);
641            EXPECT_MATRIX_APPROX_EQ(expected, state.computedState.transform);
642        }
643    };
644
645    auto layerUpdater = TestUtils::createTextureLayerUpdater(renderThread, 100, 100,
646            SkMatrix::MakeTrans(5, 5));
647
648    auto node = TestUtils::createNode(0, 0, 200, 200,
649            [&layerUpdater](RenderProperties& props, RecordingCanvas& canvas) {
650        canvas.save(SaveFlags::MatrixClip);
651        canvas.clipRect(50, 50, 150, 150, SkRegion::kIntersect_Op);
652        canvas.drawLayer(layerUpdater.get());
653        canvas.restore();
654    });
655
656    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
657            sLightGeometry, Caches::getInstance());
658    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
659
660    TextureLayerClipLocalMatrixTestRenderer renderer;
661    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
662    EXPECT_EQ(1, renderer.getIndex());
663}
664
665RENDERTHREAD_TEST(FrameBuilder, textureLayer_combineMatrices) {
666    class TextureLayerCombineMatricesTestRenderer : public TestRendererBase {
667    public:
668        void onTextureLayerOp(const TextureLayerOp& op, const BakedOpState& state) override {
669            EXPECT_EQ(0, mIndex++);
670
671            Matrix4 expected;
672            expected.loadTranslate(35, 45, 0);
673            EXPECT_MATRIX_APPROX_EQ(expected, state.computedState.transform);
674        }
675    };
676
677    auto layerUpdater = TestUtils::createTextureLayerUpdater(renderThread, 100, 100,
678            SkMatrix::MakeTrans(5, 5));
679
680    auto node = TestUtils::createNode(0, 0, 200, 200,
681            [&layerUpdater](RenderProperties& props, RecordingCanvas& canvas) {
682        canvas.save(SaveFlags::MatrixClip);
683        canvas.translate(30, 40);
684        canvas.drawLayer(layerUpdater.get());
685        canvas.restore();
686    });
687
688    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
689            sLightGeometry, Caches::getInstance());
690    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
691
692    TextureLayerCombineMatricesTestRenderer renderer;
693    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
694    EXPECT_EQ(1, renderer.getIndex());
695}
696
697RENDERTHREAD_TEST(FrameBuilder, textureLayer_reject) {
698    auto layerUpdater = TestUtils::createTextureLayerUpdater(renderThread, 100, 100,
699            SkMatrix::MakeTrans(5, 5));
700    layerUpdater->backingLayer()->setRenderTarget(GL_NONE); // Should be rejected
701
702    auto node = TestUtils::createNode(0, 0, 200, 200,
703            [&layerUpdater](RenderProperties& props, RecordingCanvas& canvas) {
704        canvas.drawLayer(layerUpdater.get());
705    });
706
707    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
708            sLightGeometry, Caches::getInstance());
709    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
710
711    FailRenderer renderer;
712    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
713}
714
715RENDERTHREAD_TEST(FrameBuilder, functor_reject) {
716    class FunctorTestRenderer : public TestRendererBase {
717    public:
718        void onFunctorOp(const FunctorOp& op, const BakedOpState& state) override {
719            EXPECT_EQ(0, mIndex++);
720        }
721    };
722    Functor noopFunctor;
723
724    // 1 million pixel tall view, scrolled down 80%
725    auto scrolledFunctorView = TestUtils::createNode(0, 0, 400, 1000000,
726            [&noopFunctor](RenderProperties& props, RecordingCanvas& canvas) {
727        canvas.translate(0, -800000);
728        canvas.callDrawGLFunction(&noopFunctor, nullptr);
729    });
730
731    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
732            sLightGeometry, Caches::getInstance());
733    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(scrolledFunctorView));
734
735    FunctorTestRenderer renderer;
736    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
737    EXPECT_EQ(1, renderer.getIndex()) << "Functor should not be rejected";
738}
739
740RENDERTHREAD_TEST(FrameBuilder, deferColorOp_unbounded) {
741    class ColorTestRenderer : public TestRendererBase {
742    public:
743        void onColorOp(const ColorOp& op, const BakedOpState& state) override {
744            EXPECT_EQ(0, mIndex++);
745            EXPECT_EQ(Rect(200, 200), state.computedState.clippedBounds)
746                    << "Color op should be expanded to bounds of surrounding";
747        }
748    };
749
750    auto unclippedColorView = TestUtils::createNode(0, 0, 10, 10,
751            [](RenderProperties& props, RecordingCanvas& canvas) {
752        props.setClipToBounds(false);
753        canvas.drawColor(SK_ColorWHITE, SkXfermode::Mode::kSrcOver_Mode);
754    });
755
756    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
757            sLightGeometry, Caches::getInstance());
758    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(unclippedColorView));
759
760    ColorTestRenderer renderer;
761    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
762    EXPECT_EQ(1, renderer.getIndex()) << "ColorOp should not be rejected";
763}
764
765TEST(FrameBuilder, renderNode) {
766    class RenderNodeTestRenderer : public TestRendererBase {
767    public:
768        void onRectOp(const RectOp& op, const BakedOpState& state) override {
769            switch(mIndex++) {
770            case 0:
771                EXPECT_EQ(Rect(200, 200), state.computedState.clippedBounds);
772                EXPECT_EQ(SK_ColorDKGRAY, op.paint->getColor());
773                break;
774            case 1:
775                EXPECT_EQ(Rect(50, 50, 150, 150), state.computedState.clippedBounds);
776                EXPECT_EQ(SK_ColorWHITE, op.paint->getColor());
777                break;
778            default:
779                ADD_FAILURE();
780            }
781        }
782    };
783
784    auto child = TestUtils::createNode(10, 10, 110, 110,
785            [](RenderProperties& props, RecordingCanvas& canvas) {
786        SkPaint paint;
787        paint.setColor(SK_ColorWHITE);
788        canvas.drawRect(0, 0, 100, 100, paint);
789    });
790
791    auto parent = TestUtils::createNode(0, 0, 200, 200,
792            [&child](RenderProperties& props, RecordingCanvas& canvas) {
793        SkPaint paint;
794        paint.setColor(SK_ColorDKGRAY);
795        canvas.drawRect(0, 0, 200, 200, paint);
796
797        canvas.save(SaveFlags::MatrixClip);
798        canvas.translate(40, 40);
799        canvas.drawRenderNode(child.get());
800        canvas.restore();
801    });
802
803    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
804            sLightGeometry, Caches::getInstance());
805    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent));
806
807    RenderNodeTestRenderer renderer;
808    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
809    EXPECT_EQ(2, renderer.getIndex());
810}
811
812RENDERTHREAD_TEST(FrameBuilder, clipped) {
813    class ClippedTestRenderer : public TestRendererBase {
814    public:
815        void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override {
816            EXPECT_EQ(0, mIndex++);
817            EXPECT_EQ(Rect(10, 20, 30, 40), state.computedState.clippedBounds);
818            EXPECT_EQ(Rect(10, 20, 30, 40), state.computedState.clipRect());
819            EXPECT_TRUE(state.computedState.transform.isIdentity());
820        }
821    };
822
823    auto node = TestUtils::createNode(0, 0, 200, 200,
824            [](RenderProperties& props, RecordingCanvas& canvas) {
825        SkBitmap bitmap = TestUtils::createSkBitmap(200, 200);
826        canvas.drawBitmap(bitmap, 0, 0, nullptr);
827    });
828
829    // clip to small area, should see in receiver
830    FrameBuilder frameBuilder(SkRect::MakeLTRB(10, 20, 30, 40), 200, 200,
831            sLightGeometry, Caches::getInstance());
832    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
833
834    ClippedTestRenderer renderer;
835    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
836}
837
838RENDERTHREAD_TEST(FrameBuilder, saveLayer_simple) {
839    class SaveLayerSimpleTestRenderer : public TestRendererBase {
840    public:
841        OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override {
842            EXPECT_EQ(0, mIndex++);
843            EXPECT_EQ(180u, width);
844            EXPECT_EQ(180u, height);
845            return nullptr;
846        }
847        void endLayer() override {
848            EXPECT_EQ(2, mIndex++);
849        }
850        void onRectOp(const RectOp& op, const BakedOpState& state) override {
851            EXPECT_EQ(1, mIndex++);
852            EXPECT_EQ(Rect(10, 10, 190, 190), op.unmappedBounds);
853            EXPECT_EQ(Rect(180, 180), state.computedState.clippedBounds);
854            EXPECT_EQ(Rect(180, 180), state.computedState.clipRect());
855
856            Matrix4 expectedTransform;
857            expectedTransform.loadTranslate(-10, -10, 0);
858            EXPECT_MATRIX_APPROX_EQ(expectedTransform, state.computedState.transform);
859        }
860        void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
861            EXPECT_EQ(3, mIndex++);
862            EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds);
863            EXPECT_EQ(Rect(200, 200), state.computedState.clipRect());
864            EXPECT_TRUE(state.computedState.transform.isIdentity());
865        }
866        void recycleTemporaryLayer(OffscreenBuffer* offscreenBuffer) override {
867            EXPECT_EQ(4, mIndex++);
868            EXPECT_EQ(nullptr, offscreenBuffer);
869        }
870    };
871
872    auto node = TestUtils::createNode(0, 0, 200, 200,
873            [](RenderProperties& props, RecordingCanvas& canvas) {
874        canvas.saveLayerAlpha(10, 10, 190, 190, 128, SaveFlags::ClipToLayer);
875        canvas.drawRect(10, 10, 190, 190, SkPaint());
876        canvas.restore();
877    });
878
879    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
880            sLightGeometry, Caches::getInstance());
881    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
882
883    SaveLayerSimpleTestRenderer renderer;
884    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
885    EXPECT_EQ(5, renderer.getIndex());
886}
887
888RENDERTHREAD_TEST(FrameBuilder, saveLayer_nested) {
889    /* saveLayer1 { rect1, saveLayer2 { rect2 } } will play back as:
890     * - startTemporaryLayer2, rect2 endLayer2
891     * - startTemporaryLayer1, rect1, drawLayer2, endLayer1
892     * - startFrame, layerOp1, endFrame
893     */
894    class SaveLayerNestedTestRenderer : public TestRendererBase {
895    public:
896        OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override {
897            const int index = mIndex++;
898            if (index == 0) {
899                EXPECT_EQ(400u, width);
900                EXPECT_EQ(400u, height);
901                return (OffscreenBuffer*) 0x400;
902            } else if (index == 3) {
903                EXPECT_EQ(800u, width);
904                EXPECT_EQ(800u, height);
905                return (OffscreenBuffer*) 0x800;
906            } else { ADD_FAILURE(); }
907            return (OffscreenBuffer*) nullptr;
908        }
909        void endLayer() override {
910            int index = mIndex++;
911            EXPECT_TRUE(index == 2 || index == 6);
912        }
913        void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
914            EXPECT_EQ(7, mIndex++);
915        }
916        void endFrame(const Rect& repaintRect) override {
917            EXPECT_EQ(9, mIndex++);
918        }
919        void onRectOp(const RectOp& op, const BakedOpState& state) override {
920            const int index = mIndex++;
921            if (index == 1) {
922                EXPECT_EQ(Rect(400, 400), op.unmappedBounds); // inner rect
923            } else if (index == 4) {
924                EXPECT_EQ(Rect(800, 800), op.unmappedBounds); // outer rect
925            } else { ADD_FAILURE(); }
926        }
927        void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
928            const int index = mIndex++;
929            if (index == 5) {
930                EXPECT_EQ((OffscreenBuffer*)0x400, *op.layerHandle);
931                EXPECT_EQ(Rect(400, 400), op.unmappedBounds); // inner layer
932            } else if (index == 8) {
933                EXPECT_EQ((OffscreenBuffer*)0x800, *op.layerHandle);
934                EXPECT_EQ(Rect(800, 800), op.unmappedBounds); // outer layer
935            } else { ADD_FAILURE(); }
936        }
937        void recycleTemporaryLayer(OffscreenBuffer* offscreenBuffer) override {
938            const int index = mIndex++;
939            // order isn't important, but we need to see both
940            if (index == 10) {
941                EXPECT_EQ((OffscreenBuffer*)0x400, offscreenBuffer);
942            } else if (index == 11) {
943                EXPECT_EQ((OffscreenBuffer*)0x800, offscreenBuffer);
944            } else { ADD_FAILURE(); }
945        }
946    };
947
948    auto node = TestUtils::createNode(0, 0, 800, 800,
949            [](RenderProperties& props, RecordingCanvas& canvas) {
950        canvas.saveLayerAlpha(0, 0, 800, 800, 128, SaveFlags::ClipToLayer);
951        {
952            canvas.drawRect(0, 0, 800, 800, SkPaint());
953            canvas.saveLayerAlpha(0, 0, 400, 400, 128, SaveFlags::ClipToLayer);
954            {
955                canvas.drawRect(0, 0, 400, 400, SkPaint());
956            }
957            canvas.restore();
958        }
959        canvas.restore();
960    });
961
962    FrameBuilder frameBuilder(SkRect::MakeWH(800, 800), 800, 800,
963            sLightGeometry, Caches::getInstance());
964    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
965
966    SaveLayerNestedTestRenderer renderer;
967    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
968    EXPECT_EQ(12, renderer.getIndex());
969}
970
971RENDERTHREAD_TEST(FrameBuilder, saveLayer_contentRejection) {
972        auto node = TestUtils::createNode(0, 0, 200, 200,
973                [](RenderProperties& props, RecordingCanvas& canvas) {
974        canvas.save(SaveFlags::MatrixClip);
975        canvas.clipRect(200, 200, 400, 400, SkRegion::kIntersect_Op);
976        canvas.saveLayerAlpha(200, 200, 400, 400, 128, SaveFlags::ClipToLayer);
977
978        // draw within save layer may still be recorded, but shouldn't be drawn
979        canvas.drawRect(200, 200, 400, 400, SkPaint());
980
981        canvas.restore();
982        canvas.restore();
983    });
984
985    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
986            sLightGeometry, Caches::getInstance());
987    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
988
989    FailRenderer renderer;
990    // should see no ops, even within the layer, since the layer should be rejected
991    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
992}
993
994RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_simple) {
995    class SaveLayerUnclippedSimpleTestRenderer : public TestRendererBase {
996    public:
997        void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override {
998            EXPECT_EQ(0, mIndex++);
999            EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds);
1000            EXPECT_CLIP_RECT(Rect(200, 200), state.computedState.clipState);
1001            EXPECT_TRUE(state.computedState.transform.isIdentity());
1002        }
1003        void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override {
1004            EXPECT_EQ(1, mIndex++);
1005            ASSERT_NE(nullptr, op.paint);
1006            ASSERT_EQ(SkXfermode::kClear_Mode, PaintUtils::getXfermodeDirect(op.paint));
1007        }
1008        void onRectOp(const RectOp& op, const BakedOpState& state) override {
1009            EXPECT_EQ(2, mIndex++);
1010            EXPECT_EQ(Rect(200, 200), op.unmappedBounds);
1011            EXPECT_EQ(Rect(200, 200), state.computedState.clippedBounds);
1012            EXPECT_EQ(Rect(200, 200), state.computedState.clipRect());
1013            EXPECT_TRUE(state.computedState.transform.isIdentity());
1014        }
1015        void onCopyFromLayerOp(const CopyFromLayerOp& op, const BakedOpState& state) override {
1016            EXPECT_EQ(3, mIndex++);
1017            EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds);
1018            EXPECT_CLIP_RECT(Rect(200, 200), state.computedState.clipState);
1019            EXPECT_TRUE(state.computedState.transform.isIdentity());
1020        }
1021    };
1022
1023    auto node = TestUtils::createNode(0, 0, 200, 200,
1024            [](RenderProperties& props, RecordingCanvas& canvas) {
1025        canvas.saveLayerAlpha(10, 10, 190, 190, 128, (SaveFlags::Flags)(0));
1026        canvas.drawRect(0, 0, 200, 200, SkPaint());
1027        canvas.restore();
1028    });
1029
1030    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
1031            sLightGeometry, Caches::getInstance());
1032    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
1033
1034    SaveLayerUnclippedSimpleTestRenderer renderer;
1035    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1036    EXPECT_EQ(4, renderer.getIndex());
1037}
1038
1039RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_round) {
1040    class SaveLayerUnclippedRoundTestRenderer : public TestRendererBase {
1041    public:
1042        void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override {
1043            EXPECT_EQ(0, mIndex++);
1044            EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds)
1045                    << "Bounds rect should round out";
1046        }
1047        void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override {}
1048        void onRectOp(const RectOp& op, const BakedOpState& state) override {}
1049        void onCopyFromLayerOp(const CopyFromLayerOp& op, const BakedOpState& state) override {
1050            EXPECT_EQ(1, mIndex++);
1051            EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds)
1052                    << "Bounds rect should round out";
1053        }
1054    };
1055
1056    auto node = TestUtils::createNode(0, 0, 200, 200,
1057            [](RenderProperties& props, RecordingCanvas& canvas) {
1058        canvas.saveLayerAlpha(10.95f, 10.5f, 189.75f, 189.25f, // values should all round out
1059                128, (SaveFlags::Flags)(0));
1060        canvas.drawRect(0, 0, 200, 200, SkPaint());
1061        canvas.restore();
1062    });
1063
1064    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
1065            sLightGeometry, Caches::getInstance());
1066    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
1067
1068    SaveLayerUnclippedRoundTestRenderer renderer;
1069    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1070    EXPECT_EQ(2, renderer.getIndex());
1071}
1072
1073RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_mergedClears) {
1074    class SaveLayerUnclippedMergedClearsTestRenderer : public TestRendererBase {
1075    public:
1076        void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override {
1077            int index = mIndex++;
1078            EXPECT_GT(4, index);
1079            EXPECT_EQ(5, op.unmappedBounds.getWidth());
1080            EXPECT_EQ(5, op.unmappedBounds.getHeight());
1081            if (index == 0) {
1082                EXPECT_EQ(Rect(10, 10), state.computedState.clippedBounds);
1083            } else if (index == 1) {
1084                EXPECT_EQ(Rect(190, 0, 200, 10), state.computedState.clippedBounds);
1085            } else if (index == 2) {
1086                EXPECT_EQ(Rect(0, 190, 10, 200), state.computedState.clippedBounds);
1087            } else if (index == 3) {
1088                EXPECT_EQ(Rect(190, 190, 200, 200), state.computedState.clippedBounds);
1089            }
1090        }
1091        void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override {
1092            EXPECT_EQ(4, mIndex++);
1093            ASSERT_EQ(op.vertexCount, 16u);
1094            for (size_t i = 0; i < op.vertexCount; i++) {
1095                auto v = op.vertices[i];
1096                EXPECT_TRUE(v.x == 0 || v.x == 10 || v.x == 190 || v.x == 200);
1097                EXPECT_TRUE(v.y == 0 || v.y == 10 || v.y == 190 || v.y == 200);
1098            }
1099        }
1100        void onRectOp(const RectOp& op, const BakedOpState& state) override {
1101            EXPECT_EQ(5, mIndex++);
1102        }
1103        void onCopyFromLayerOp(const CopyFromLayerOp& op, const BakedOpState& state) override {
1104            EXPECT_LT(5, mIndex++);
1105        }
1106    };
1107
1108    auto node = TestUtils::createNode(0, 0, 200, 200,
1109            [](RenderProperties& props, RecordingCanvas& canvas) {
1110
1111        int restoreTo = canvas.save(SaveFlags::MatrixClip);
1112        canvas.scale(2, 2);
1113        canvas.saveLayerAlpha(0, 0, 5, 5, 128, SaveFlags::MatrixClip);
1114        canvas.saveLayerAlpha(95, 0, 100, 5, 128, SaveFlags::MatrixClip);
1115        canvas.saveLayerAlpha(0, 95, 5, 100, 128, SaveFlags::MatrixClip);
1116        canvas.saveLayerAlpha(95, 95, 100, 100, 128, SaveFlags::MatrixClip);
1117        canvas.drawRect(0, 0, 100, 100, SkPaint());
1118        canvas.restoreToCount(restoreTo);
1119    });
1120
1121    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
1122            sLightGeometry, Caches::getInstance());
1123    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
1124
1125    SaveLayerUnclippedMergedClearsTestRenderer renderer;
1126    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1127    EXPECT_EQ(10, renderer.getIndex())
1128            << "Expect 4 copyTos, 4 copyFroms, 1 clear SimpleRects, and 1 rect.";
1129}
1130
1131RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_clearClip) {
1132    class SaveLayerUnclippedClearClipTestRenderer : public TestRendererBase {
1133    public:
1134        void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override {
1135            EXPECT_EQ(0, mIndex++);
1136        }
1137        void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override {
1138            EXPECT_EQ(1, mIndex++);
1139            ASSERT_NE(nullptr, op.paint);
1140            EXPECT_EQ(SkXfermode::kClear_Mode, PaintUtils::getXfermodeDirect(op.paint));
1141            EXPECT_EQ(Rect(50, 50, 150, 150), state.computedState.clippedBounds)
1142                    << "Expect dirty rect as clip";
1143            ASSERT_NE(nullptr, state.computedState.clipState);
1144            EXPECT_EQ(Rect(50, 50, 150, 150), state.computedState.clipState->rect);
1145            EXPECT_EQ(ClipMode::Rectangle, state.computedState.clipState->mode);
1146        }
1147        void onRectOp(const RectOp& op, const BakedOpState& state) override {
1148            EXPECT_EQ(2, mIndex++);
1149        }
1150        void onCopyFromLayerOp(const CopyFromLayerOp& op, const BakedOpState& state) override {
1151            EXPECT_EQ(3, mIndex++);
1152        }
1153    };
1154
1155    auto node = TestUtils::createNode(0, 0, 200, 200,
1156            [](RenderProperties& props, RecordingCanvas& canvas) {
1157        // save smaller than clip, so we get unclipped behavior
1158        canvas.saveLayerAlpha(10, 10, 190, 190, 128, (SaveFlags::Flags)(0));
1159        canvas.drawRect(0, 0, 200, 200, SkPaint());
1160        canvas.restore();
1161    });
1162
1163    // draw with partial screen dirty, and assert we see that rect later
1164    FrameBuilder frameBuilder(SkRect::MakeLTRB(50, 50, 150, 150), 200, 200,
1165            sLightGeometry, Caches::getInstance());
1166    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
1167
1168    SaveLayerUnclippedClearClipTestRenderer renderer;
1169    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1170    EXPECT_EQ(4, renderer.getIndex());
1171}
1172
1173RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_reject) {
1174    auto node = TestUtils::createNode(0, 0, 200, 200,
1175            [](RenderProperties& props, RecordingCanvas& canvas) {
1176        // unclipped savelayer + rect both in area that won't intersect with dirty
1177        canvas.saveLayerAlpha(100, 100, 200, 200, 128, (SaveFlags::Flags)(0));
1178        canvas.drawRect(100, 100, 200, 200, SkPaint());
1179        canvas.restore();
1180    });
1181
1182    // draw with partial screen dirty that doesn't intersect with savelayer
1183    FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 200, 200,
1184            sLightGeometry, Caches::getInstance());
1185    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
1186
1187    FailRenderer renderer;
1188    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1189}
1190
1191/* saveLayerUnclipped { saveLayer { saveLayerUnclipped { rect } } } will play back as:
1192 * - startTemporaryLayer, onCopyToLayer, onSimpleRects, onRect, onCopyFromLayer, endLayer
1193 * - startFrame, onCopyToLayer, onSimpleRects, drawLayer, onCopyFromLayer, endframe
1194 */
1195RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_complex) {
1196    class SaveLayerUnclippedComplexTestRenderer : public TestRendererBase {
1197    public:
1198        OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) {
1199            EXPECT_EQ(0, mIndex++); // savelayer first
1200            return (OffscreenBuffer*)0xabcd;
1201        }
1202        void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override {
1203            int index = mIndex++;
1204            EXPECT_TRUE(index == 1 || index == 7);
1205        }
1206        void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override {
1207            int index = mIndex++;
1208            EXPECT_TRUE(index == 2 || index == 8);
1209        }
1210        void onRectOp(const RectOp& op, const BakedOpState& state) override {
1211            EXPECT_EQ(3, mIndex++);
1212            Matrix4 expected;
1213            expected.loadTranslate(-100, -100, 0);
1214            EXPECT_EQ(Rect(100, 100, 200, 200), state.computedState.clippedBounds);
1215            EXPECT_MATRIX_APPROX_EQ(expected, state.computedState.transform);
1216        }
1217        void onCopyFromLayerOp(const CopyFromLayerOp& op, const BakedOpState& state) override {
1218            int index = mIndex++;
1219            EXPECT_TRUE(index == 4 || index == 10);
1220        }
1221        void endLayer() override {
1222            EXPECT_EQ(5, mIndex++);
1223        }
1224        void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
1225            EXPECT_EQ(6, mIndex++);
1226        }
1227        void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
1228            EXPECT_EQ(9, mIndex++);
1229            EXPECT_EQ((OffscreenBuffer*)0xabcd, *op.layerHandle);
1230        }
1231        void endFrame(const Rect& repaintRect) override {
1232            EXPECT_EQ(11, mIndex++);
1233        }
1234        void recycleTemporaryLayer(OffscreenBuffer* offscreenBuffer) override {
1235            EXPECT_EQ(12, mIndex++);
1236            EXPECT_EQ((OffscreenBuffer*)0xabcd, offscreenBuffer);
1237        }
1238    };
1239
1240    auto node = TestUtils::createNode(0, 0, 600, 600, // 500x500 triggers clipping
1241            [](RenderProperties& props, RecordingCanvas& canvas) {
1242        canvas.saveLayerAlpha(0, 0, 500, 500, 128, (SaveFlags::Flags)0); // unclipped
1243        canvas.saveLayerAlpha(100, 100, 400, 400, 128, SaveFlags::ClipToLayer); // clipped
1244        canvas.saveLayerAlpha(200, 200, 300, 300, 128, (SaveFlags::Flags)0); // unclipped
1245        canvas.drawRect(200, 200, 300, 300, SkPaint());
1246        canvas.restore();
1247        canvas.restore();
1248        canvas.restore();
1249    });
1250
1251    FrameBuilder frameBuilder(SkRect::MakeWH(600, 600), 600, 600,
1252            sLightGeometry, Caches::getInstance());
1253    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
1254
1255    SaveLayerUnclippedComplexTestRenderer renderer;
1256    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1257    EXPECT_EQ(13, renderer.getIndex());
1258}
1259
1260RENDERTHREAD_TEST(FrameBuilder, hwLayer_simple) {
1261    class HwLayerSimpleTestRenderer : public TestRendererBase {
1262    public:
1263        void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override {
1264            EXPECT_EQ(0, mIndex++);
1265            EXPECT_EQ(100u, offscreenBuffer->viewportWidth);
1266            EXPECT_EQ(100u, offscreenBuffer->viewportHeight);
1267            EXPECT_EQ(Rect(25, 25, 75, 75), repaintRect);
1268        }
1269        void onRectOp(const RectOp& op, const BakedOpState& state) override {
1270            EXPECT_EQ(1, mIndex++);
1271
1272            EXPECT_TRUE(state.computedState.transform.isIdentity())
1273                    << "Transform should be reset within layer";
1274
1275            EXPECT_EQ(Rect(25, 25, 75, 75), state.computedState.clipRect())
1276                    << "Damage rect should be used to clip layer content";
1277        }
1278        void endLayer() override {
1279            EXPECT_EQ(2, mIndex++);
1280        }
1281        void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
1282            EXPECT_EQ(3, mIndex++);
1283        }
1284        void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
1285            EXPECT_EQ(4, mIndex++);
1286        }
1287        void endFrame(const Rect& repaintRect) override {
1288            EXPECT_EQ(5, mIndex++);
1289        }
1290    };
1291
1292    auto node = TestUtils::createNode(10, 10, 110, 110,
1293            [](RenderProperties& props, RecordingCanvas& canvas) {
1294        props.mutateLayerProperties().setType(LayerType::RenderLayer);
1295        SkPaint paint;
1296        paint.setColor(SK_ColorWHITE);
1297        canvas.drawRect(0, 0, 100, 100, paint);
1298    });
1299    OffscreenBuffer** layerHandle = node->getLayerHandle();
1300
1301    // create RenderNode's layer here in same way prepareTree would
1302    OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 100, 100);
1303    *layerHandle = &layer;
1304
1305    auto syncedNode = TestUtils::getSyncedNode(node);
1306
1307    // only enqueue partial damage
1308    LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
1309    layerUpdateQueue.enqueueLayerWithDamage(node.get(), Rect(25, 25, 75, 75));
1310
1311    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
1312            sLightGeometry, Caches::getInstance());
1313    frameBuilder.deferLayers(layerUpdateQueue);
1314    frameBuilder.deferRenderNode(*syncedNode);
1315
1316    HwLayerSimpleTestRenderer renderer;
1317    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1318    EXPECT_EQ(6, renderer.getIndex());
1319
1320    // clean up layer pointer, so we can safely destruct RenderNode
1321    *layerHandle = nullptr;
1322}
1323
1324RENDERTHREAD_TEST(FrameBuilder, hwLayer_complex) {
1325    /* parentLayer { greyRect, saveLayer { childLayer { whiteRect } } } will play back as:
1326     * - startRepaintLayer(child), rect(grey), endLayer
1327     * - startTemporaryLayer, drawLayer(child), endLayer
1328     * - startRepaintLayer(parent), rect(white), drawLayer(saveLayer), endLayer
1329     * - startFrame, drawLayer(parent), endLayerb
1330     */
1331    class HwLayerComplexTestRenderer : public TestRendererBase {
1332    public:
1333        OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) {
1334            EXPECT_EQ(3, mIndex++); // savelayer first
1335            return (OffscreenBuffer*)0xabcd;
1336        }
1337        void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override {
1338            int index = mIndex++;
1339            if (index == 0) {
1340                // starting inner layer
1341                EXPECT_EQ(100u, offscreenBuffer->viewportWidth);
1342                EXPECT_EQ(100u, offscreenBuffer->viewportHeight);
1343            } else if (index == 6) {
1344                // starting outer layer
1345                EXPECT_EQ(200u, offscreenBuffer->viewportWidth);
1346                EXPECT_EQ(200u, offscreenBuffer->viewportHeight);
1347            } else { ADD_FAILURE(); }
1348        }
1349        void onRectOp(const RectOp& op, const BakedOpState& state) override {
1350            int index = mIndex++;
1351            if (index == 1) {
1352                // inner layer's rect (white)
1353                EXPECT_EQ(SK_ColorWHITE, op.paint->getColor());
1354            } else if (index == 7) {
1355                // outer layer's rect (grey)
1356                EXPECT_EQ(SK_ColorDKGRAY, op.paint->getColor());
1357            } else { ADD_FAILURE(); }
1358        }
1359        void endLayer() override {
1360            int index = mIndex++;
1361            EXPECT_TRUE(index == 2 || index == 5 || index == 9);
1362        }
1363        void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
1364            EXPECT_EQ(10, mIndex++);
1365        }
1366        void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
1367            OffscreenBuffer* layer = *op.layerHandle;
1368            int index = mIndex++;
1369            if (index == 4) {
1370                EXPECT_EQ(100u, layer->viewportWidth);
1371                EXPECT_EQ(100u, layer->viewportHeight);
1372            } else if (index == 8) {
1373                EXPECT_EQ((OffscreenBuffer*)0xabcd, *op.layerHandle);
1374            } else if (index == 11) {
1375                EXPECT_EQ(200u, layer->viewportWidth);
1376                EXPECT_EQ(200u, layer->viewportHeight);
1377            } else { ADD_FAILURE(); }
1378        }
1379        void endFrame(const Rect& repaintRect) override {
1380            EXPECT_EQ(12, mIndex++);
1381        }
1382        void recycleTemporaryLayer(OffscreenBuffer* offscreenBuffer) override {
1383            EXPECT_EQ(13, mIndex++);
1384        }
1385    };
1386
1387    auto child = TestUtils::createNode(50, 50, 150, 150,
1388            [](RenderProperties& props, RecordingCanvas& canvas) {
1389        props.mutateLayerProperties().setType(LayerType::RenderLayer);
1390        SkPaint paint;
1391        paint.setColor(SK_ColorWHITE);
1392        canvas.drawRect(0, 0, 100, 100, paint);
1393    });
1394    OffscreenBuffer childLayer(renderThread.renderState(), Caches::getInstance(), 100, 100);
1395    *(child->getLayerHandle()) = &childLayer;
1396
1397    RenderNode* childPtr = child.get();
1398    auto parent = TestUtils::createNode(0, 0, 200, 200,
1399            [childPtr](RenderProperties& props, RecordingCanvas& canvas) {
1400        props.mutateLayerProperties().setType(LayerType::RenderLayer);
1401        SkPaint paint;
1402        paint.setColor(SK_ColorDKGRAY);
1403        canvas.drawRect(0, 0, 200, 200, paint);
1404
1405        canvas.saveLayerAlpha(50, 50, 150, 150, 128, SaveFlags::ClipToLayer);
1406        canvas.drawRenderNode(childPtr);
1407        canvas.restore();
1408    });
1409    OffscreenBuffer parentLayer(renderThread.renderState(), Caches::getInstance(), 200, 200);
1410    *(parent->getLayerHandle()) = &parentLayer;
1411
1412    auto syncedNode = TestUtils::getSyncedNode(parent);
1413
1414    LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
1415    layerUpdateQueue.enqueueLayerWithDamage(child.get(), Rect(100, 100));
1416    layerUpdateQueue.enqueueLayerWithDamage(parent.get(), Rect(200, 200));
1417
1418    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
1419            sLightGeometry, Caches::getInstance());
1420    frameBuilder.deferLayers(layerUpdateQueue);
1421    frameBuilder.deferRenderNode(*syncedNode);
1422
1423    HwLayerComplexTestRenderer renderer;
1424    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1425    EXPECT_EQ(14, renderer.getIndex());
1426
1427    // clean up layer pointers, so we can safely destruct RenderNodes
1428    *(child->getLayerHandle()) = nullptr;
1429    *(parent->getLayerHandle()) = nullptr;
1430}
1431
1432
1433RENDERTHREAD_TEST(FrameBuilder, buildLayer) {
1434    class BuildLayerTestRenderer : public TestRendererBase {
1435    public:
1436        void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override {
1437            EXPECT_EQ(0, mIndex++);
1438            EXPECT_EQ(100u, offscreenBuffer->viewportWidth);
1439            EXPECT_EQ(100u, offscreenBuffer->viewportHeight);
1440            EXPECT_EQ(Rect(25, 25, 75, 75), repaintRect);
1441        }
1442        void onColorOp(const ColorOp& op, const BakedOpState& state) override {
1443            EXPECT_EQ(1, mIndex++);
1444
1445            EXPECT_TRUE(state.computedState.transform.isIdentity())
1446                    << "Transform should be reset within layer";
1447
1448            EXPECT_EQ(Rect(25, 25, 75, 75), state.computedState.clipRect())
1449                    << "Damage rect should be used to clip layer content";
1450        }
1451        void endLayer() override {
1452            EXPECT_EQ(2, mIndex++);
1453        }
1454        void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
1455            ADD_FAILURE() << "Primary frame draw not expected in this test";
1456        }
1457        void endFrame(const Rect& repaintRect) override {
1458            ADD_FAILURE() << "Primary frame draw not expected in this test";
1459        }
1460    };
1461
1462    auto node = TestUtils::createNode(10, 10, 110, 110,
1463            [](RenderProperties& props, RecordingCanvas& canvas) {
1464        props.mutateLayerProperties().setType(LayerType::RenderLayer);
1465        canvas.drawColor(SK_ColorWHITE, SkXfermode::Mode::kSrcOver_Mode);
1466    });
1467    OffscreenBuffer** layerHandle = node->getLayerHandle();
1468
1469    // create RenderNode's layer here in same way prepareTree would
1470    OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 100, 100);
1471    *layerHandle = &layer;
1472
1473    TestUtils::syncHierarchyPropertiesAndDisplayList(node);
1474
1475    // only enqueue partial damage
1476    LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
1477    layerUpdateQueue.enqueueLayerWithDamage(node.get(), Rect(25, 25, 75, 75));
1478
1479    // Draw, but pass empty node list, so no work is done for primary frame
1480    FrameBuilder frameBuilder(layerUpdateQueue, sLightGeometry, Caches::getInstance());
1481    BuildLayerTestRenderer renderer;
1482    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1483    EXPECT_EQ(3, renderer.getIndex());
1484
1485    // clean up layer pointer, so we can safely destruct RenderNode
1486    *layerHandle = nullptr;
1487}
1488
1489static void drawOrderedRect(RecordingCanvas* canvas, uint8_t expectedDrawOrder) {
1490    SkPaint paint;
1491    // order put in blue channel, transparent so overlapped content doesn't get rejected
1492    paint.setColor(SkColorSetARGB(1, 0, 0, expectedDrawOrder));
1493    canvas->drawRect(0, 0, 100, 100, paint);
1494}
1495static void drawOrderedNode(RecordingCanvas* canvas, uint8_t expectedDrawOrder, float z) {
1496    auto node = TestUtils::createNode(0, 0, 100, 100,
1497            [expectedDrawOrder](RenderProperties& props, RecordingCanvas& canvas) {
1498        drawOrderedRect(&canvas, expectedDrawOrder);
1499    });
1500    node->mutateStagingProperties().setTranslationZ(z);
1501    node->setPropertyFieldsDirty(RenderNode::TRANSLATION_Z);
1502    canvas->drawRenderNode(node.get()); // canvas takes reference/sole ownership
1503}
1504RENDERTHREAD_TEST(FrameBuilder, zReorder) {
1505    class ZReorderTestRenderer : public TestRendererBase {
1506    public:
1507        void onRectOp(const RectOp& op, const BakedOpState& state) override {
1508            int expectedOrder = SkColorGetB(op.paint->getColor()); // extract order from blue channel
1509            EXPECT_EQ(expectedOrder, mIndex++) << "An op was drawn out of order";
1510        }
1511    };
1512
1513    auto parent = TestUtils::createNode(0, 0, 100, 100,
1514            [](RenderProperties& props, RecordingCanvas& canvas) {
1515        drawOrderedNode(&canvas, 0, 10.0f); // in reorder=false at this point, so played inorder
1516        drawOrderedRect(&canvas, 1);
1517        canvas.insertReorderBarrier(true);
1518        drawOrderedNode(&canvas, 6, 2.0f);
1519        drawOrderedRect(&canvas, 3);
1520        drawOrderedNode(&canvas, 4, 0.0f);
1521        drawOrderedRect(&canvas, 5);
1522        drawOrderedNode(&canvas, 2, -2.0f);
1523        drawOrderedNode(&canvas, 7, 2.0f);
1524        canvas.insertReorderBarrier(false);
1525        drawOrderedRect(&canvas, 8);
1526        drawOrderedNode(&canvas, 9, -10.0f); // in reorder=false at this point, so played inorder
1527    });
1528    FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100,
1529            sLightGeometry, Caches::getInstance());
1530    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent));
1531
1532    ZReorderTestRenderer renderer;
1533    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1534    EXPECT_EQ(10, renderer.getIndex());
1535};
1536
1537RENDERTHREAD_TEST(FrameBuilder, projectionReorder) {
1538    static const int scrollX = 5;
1539    static const int scrollY = 10;
1540    class ProjectionReorderTestRenderer : public TestRendererBase {
1541    public:
1542        void onRectOp(const RectOp& op, const BakedOpState& state) override {
1543            const int index = mIndex++;
1544
1545            Matrix4 expectedMatrix;
1546            switch (index) {
1547            case 0:
1548                EXPECT_EQ(Rect(100, 100), op.unmappedBounds);
1549                EXPECT_EQ(SK_ColorWHITE, op.paint->getColor());
1550                expectedMatrix.loadIdentity();
1551                EXPECT_EQ(nullptr, state.computedState.localProjectionPathMask);
1552                break;
1553            case 1:
1554                EXPECT_EQ(Rect(-10, -10, 60, 60), op.unmappedBounds);
1555                EXPECT_EQ(SK_ColorDKGRAY, op.paint->getColor());
1556                expectedMatrix.loadTranslate(50 - scrollX, 50 - scrollY, 0);
1557                ASSERT_NE(nullptr, state.computedState.localProjectionPathMask);
1558                EXPECT_EQ(Rect(-35, -30, 45, 50),
1559                        Rect(state.computedState.localProjectionPathMask->getBounds()));
1560                break;
1561            case 2:
1562                EXPECT_EQ(Rect(100, 50), op.unmappedBounds);
1563                EXPECT_EQ(SK_ColorBLUE, op.paint->getColor());
1564                expectedMatrix.loadTranslate(-scrollX, 50 - scrollY, 0);
1565                EXPECT_EQ(nullptr, state.computedState.localProjectionPathMask);
1566                break;
1567            default:
1568                ADD_FAILURE();
1569            }
1570            EXPECT_EQ(expectedMatrix, state.computedState.transform);
1571        }
1572    };
1573
1574    /**
1575     * Construct a tree of nodes, where the root (A) has a receiver background (B), and a child (C)
1576     * with a projecting child (P) of its own. P would normally draw between B and C's "background"
1577     * draw, but because it is projected backwards, it's drawn in between B and C.
1578     *
1579     * The parent is scrolled by scrollX/scrollY, but this does not affect the background
1580     * (which isn't affected by scroll).
1581     */
1582    auto receiverBackground = TestUtils::createNode(0, 0, 100, 100,
1583            [](RenderProperties& properties, RecordingCanvas& canvas) {
1584        properties.setProjectionReceiver(true);
1585        // scroll doesn't apply to background, so undone via translationX/Y
1586        // NOTE: translationX/Y only! no other transform properties may be set for a proj receiver!
1587        properties.setTranslationX(scrollX);
1588        properties.setTranslationY(scrollY);
1589
1590        SkPaint paint;
1591        paint.setColor(SK_ColorWHITE);
1592        canvas.drawRect(0, 0, 100, 100, paint);
1593    });
1594    auto projectingRipple = TestUtils::createNode(50, 0, 100, 50,
1595            [](RenderProperties& properties, RecordingCanvas& canvas) {
1596        properties.setProjectBackwards(true);
1597        properties.setClipToBounds(false);
1598        SkPaint paint;
1599        paint.setColor(SK_ColorDKGRAY);
1600        canvas.drawRect(-10, -10, 60, 60, paint);
1601    });
1602    auto child = TestUtils::createNode(0, 50, 100, 100,
1603            [&projectingRipple](RenderProperties& properties, RecordingCanvas& canvas) {
1604        SkPaint paint;
1605        paint.setColor(SK_ColorBLUE);
1606        canvas.drawRect(0, 0, 100, 50, paint);
1607        canvas.drawRenderNode(projectingRipple.get());
1608    });
1609    auto parent = TestUtils::createNode(0, 0, 100, 100,
1610            [&receiverBackground, &child](RenderProperties& properties, RecordingCanvas& canvas) {
1611        // Set a rect outline for the projecting ripple to be masked against.
1612        properties.mutableOutline().setRoundRect(10, 10, 90, 90, 5, 1.0f);
1613
1614        canvas.save(SaveFlags::MatrixClip);
1615        canvas.translate(-scrollX, -scrollY); // Apply scroll (note: bg undoes this internally)
1616        canvas.drawRenderNode(receiverBackground.get());
1617        canvas.drawRenderNode(child.get());
1618        canvas.restore();
1619    });
1620
1621    FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100,
1622            sLightGeometry, Caches::getInstance());
1623    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent));
1624
1625    ProjectionReorderTestRenderer renderer;
1626    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1627    EXPECT_EQ(3, renderer.getIndex());
1628}
1629
1630RENDERTHREAD_TEST(FrameBuilder, projectionHwLayer) {
1631    static const int scrollX = 5;
1632    static const int scrollY = 10;
1633    class ProjectionHwLayerTestRenderer : public TestRendererBase {
1634    public:
1635        void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override {
1636            EXPECT_EQ(0, mIndex++);
1637        }
1638        void onArcOp(const ArcOp& op, const BakedOpState& state) override {
1639            EXPECT_EQ(1, mIndex++);
1640            ASSERT_EQ(nullptr, state.computedState.localProjectionPathMask);
1641        }
1642        void endLayer() override {
1643            EXPECT_EQ(2, mIndex++);
1644        }
1645        void onRectOp(const RectOp& op, const BakedOpState& state) override {
1646            EXPECT_EQ(3, mIndex++);
1647            ASSERT_EQ(nullptr, state.computedState.localProjectionPathMask);
1648        }
1649        void onOvalOp(const OvalOp& op, const BakedOpState& state) override {
1650            EXPECT_EQ(4, mIndex++);
1651            ASSERT_NE(nullptr, state.computedState.localProjectionPathMask);
1652            Matrix4 expected;
1653            expected.loadTranslate(100 - scrollX, 100 - scrollY, 0);
1654            EXPECT_EQ(expected, state.computedState.transform);
1655            EXPECT_EQ(Rect(-85, -80, 295, 300),
1656                    Rect(state.computedState.localProjectionPathMask->getBounds()));
1657        }
1658        void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
1659            EXPECT_EQ(5, mIndex++);
1660            ASSERT_EQ(nullptr, state.computedState.localProjectionPathMask);
1661        }
1662    };
1663    auto receiverBackground = TestUtils::createNode(0, 0, 400, 400,
1664            [](RenderProperties& properties, RecordingCanvas& canvas) {
1665        properties.setProjectionReceiver(true);
1666        // scroll doesn't apply to background, so undone via translationX/Y
1667        // NOTE: translationX/Y only! no other transform properties may be set for a proj receiver!
1668        properties.setTranslationX(scrollX);
1669        properties.setTranslationY(scrollY);
1670
1671        canvas.drawRect(0, 0, 400, 400, SkPaint());
1672    });
1673    auto projectingRipple = TestUtils::createNode(0, 0, 200, 200,
1674            [](RenderProperties& properties, RecordingCanvas& canvas) {
1675        properties.setProjectBackwards(true);
1676        properties.setClipToBounds(false);
1677        canvas.drawOval(100, 100, 300, 300, SkPaint()); // drawn mostly out of layer bounds
1678    });
1679    auto child = TestUtils::createNode(100, 100, 300, 300,
1680            [&projectingRipple](RenderProperties& properties, RecordingCanvas& canvas) {
1681        properties.mutateLayerProperties().setType(LayerType::RenderLayer);
1682        canvas.drawRenderNode(projectingRipple.get());
1683        canvas.drawArc(0, 0, 200, 200, 0.0f, 280.0f, true, SkPaint());
1684    });
1685    auto parent = TestUtils::createNode(0, 0, 400, 400,
1686            [&receiverBackground, &child](RenderProperties& properties, RecordingCanvas& canvas) {
1687        // Set a rect outline for the projecting ripple to be masked against.
1688        properties.mutableOutline().setRoundRect(10, 10, 390, 390, 0, 1.0f);
1689        canvas.translate(-scrollX, -scrollY); // Apply scroll (note: bg undoes this internally)
1690        canvas.drawRenderNode(receiverBackground.get());
1691        canvas.drawRenderNode(child.get());
1692    });
1693
1694    OffscreenBuffer** layerHandle = child->getLayerHandle();
1695
1696    // create RenderNode's layer here in same way prepareTree would, setting windowTransform
1697    OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 200, 200);
1698    Matrix4 windowTransform;
1699    windowTransform.loadTranslate(100, 100, 0); // total transform of layer's origin
1700    layer.setWindowTransform(windowTransform);
1701    *layerHandle = &layer;
1702
1703    auto syncedNode = TestUtils::getSyncedNode(parent);
1704
1705    LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
1706    layerUpdateQueue.enqueueLayerWithDamage(child.get(), Rect(200, 200));
1707
1708    FrameBuilder frameBuilder(SkRect::MakeWH(400, 400), 400, 400,
1709            sLightGeometry, Caches::getInstance());
1710    frameBuilder.deferLayers(layerUpdateQueue);
1711    frameBuilder.deferRenderNode(*syncedNode);
1712
1713    ProjectionHwLayerTestRenderer renderer;
1714    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1715    EXPECT_EQ(6, renderer.getIndex());
1716
1717    // clean up layer pointer, so we can safely destruct RenderNode
1718    *layerHandle = nullptr;
1719}
1720
1721RENDERTHREAD_TEST(FrameBuilder, projectionChildScroll) {
1722    static const int scrollX = 500000;
1723    static const int scrollY = 0;
1724    class ProjectionChildScrollTestRenderer : public TestRendererBase {
1725    public:
1726        void onRectOp(const RectOp& op, const BakedOpState& state) override {
1727            EXPECT_EQ(0, mIndex++);
1728            EXPECT_TRUE(state.computedState.transform.isIdentity());
1729        }
1730        void onOvalOp(const OvalOp& op, const BakedOpState& state) override {
1731            EXPECT_EQ(1, mIndex++);
1732            ASSERT_NE(nullptr, state.computedState.clipState);
1733            ASSERT_EQ(ClipMode::Rectangle, state.computedState.clipState->mode);
1734            ASSERT_EQ(Rect(400, 400), state.computedState.clipState->rect);
1735            EXPECT_TRUE(state.computedState.transform.isIdentity());
1736        }
1737    };
1738    auto receiverBackground = TestUtils::createNode(0, 0, 400, 400,
1739            [](RenderProperties& properties, RecordingCanvas& canvas) {
1740        properties.setProjectionReceiver(true);
1741        canvas.drawRect(0, 0, 400, 400, SkPaint());
1742    });
1743    auto projectingRipple = TestUtils::createNode(0, 0, 200, 200,
1744            [](RenderProperties& properties, RecordingCanvas& canvas) {
1745        // scroll doesn't apply to background, so undone via translationX/Y
1746        // NOTE: translationX/Y only! no other transform properties may be set for a proj receiver!
1747        properties.setTranslationX(scrollX);
1748        properties.setTranslationY(scrollY);
1749        properties.setProjectBackwards(true);
1750        properties.setClipToBounds(false);
1751        canvas.drawOval(0, 0, 200, 200, SkPaint());
1752    });
1753    auto child = TestUtils::createNode(0, 0, 400, 400,
1754            [&projectingRipple](RenderProperties& properties, RecordingCanvas& canvas) {
1755        // Record time clip will be ignored by projectee
1756        canvas.clipRect(100, 100, 300, 300, SkRegion::kIntersect_Op);
1757
1758        canvas.translate(-scrollX, -scrollY); // Apply scroll (note: bg undoes this internally)
1759        canvas.drawRenderNode(projectingRipple.get());
1760    });
1761    auto parent = TestUtils::createNode(0, 0, 400, 400,
1762            [&receiverBackground, &child](RenderProperties& properties, RecordingCanvas& canvas) {
1763        canvas.drawRenderNode(receiverBackground.get());
1764        canvas.drawRenderNode(child.get());
1765    });
1766
1767    FrameBuilder frameBuilder(SkRect::MakeWH(400, 400), 400, 400,
1768            sLightGeometry, Caches::getInstance());
1769    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent));
1770
1771    ProjectionChildScrollTestRenderer renderer;
1772    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1773    EXPECT_EQ(2, renderer.getIndex());
1774}
1775
1776// creates a 100x100 shadow casting node with provided translationZ
1777static sp<RenderNode> createWhiteRectShadowCaster(float translationZ) {
1778    return TestUtils::createNode(0, 0, 100, 100,
1779            [translationZ](RenderProperties& properties, RecordingCanvas& canvas) {
1780        properties.setTranslationZ(translationZ);
1781        properties.mutableOutline().setRoundRect(0, 0, 100, 100, 0.0f, 1.0f);
1782        SkPaint paint;
1783        paint.setColor(SK_ColorWHITE);
1784        canvas.drawRect(0, 0, 100, 100, paint);
1785    });
1786}
1787
1788RENDERTHREAD_TEST(FrameBuilder, shadow) {
1789    class ShadowTestRenderer : public TestRendererBase {
1790    public:
1791        void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
1792            EXPECT_EQ(0, mIndex++);
1793            EXPECT_FLOAT_EQ(1.0f, op.casterAlpha);
1794            EXPECT_TRUE(op.shadowTask->casterPerimeter.isRect(nullptr));
1795            EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), op.shadowTask->transformXY);
1796
1797            Matrix4 expectedZ;
1798            expectedZ.loadTranslate(0, 0, 5);
1799            EXPECT_MATRIX_APPROX_EQ(expectedZ, op.shadowTask->transformZ);
1800        }
1801        void onRectOp(const RectOp& op, const BakedOpState& state) override {
1802            EXPECT_EQ(1, mIndex++);
1803        }
1804    };
1805
1806    auto parent = TestUtils::createNode(0, 0, 200, 200,
1807            [](RenderProperties& props, RecordingCanvas& canvas) {
1808        canvas.insertReorderBarrier(true);
1809        canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
1810    });
1811
1812    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
1813            sLightGeometry, Caches::getInstance());
1814    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent));
1815
1816    ShadowTestRenderer renderer;
1817    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1818    EXPECT_EQ(2, renderer.getIndex());
1819}
1820
1821RENDERTHREAD_TEST(FrameBuilder, shadowSaveLayer) {
1822    class ShadowSaveLayerTestRenderer : public TestRendererBase {
1823    public:
1824        OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override {
1825            EXPECT_EQ(0, mIndex++);
1826            return nullptr;
1827        }
1828        void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
1829            EXPECT_EQ(1, mIndex++);
1830            EXPECT_FLOAT_EQ(50, op.shadowTask->lightCenter.x);
1831            EXPECT_FLOAT_EQ(40, op.shadowTask->lightCenter.y);
1832        }
1833        void onRectOp(const RectOp& op, const BakedOpState& state) override {
1834            EXPECT_EQ(2, mIndex++);
1835        }
1836        void endLayer() override {
1837            EXPECT_EQ(3, mIndex++);
1838        }
1839        void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
1840            EXPECT_EQ(4, mIndex++);
1841        }
1842        void recycleTemporaryLayer(OffscreenBuffer* offscreenBuffer) override {
1843            EXPECT_EQ(5, mIndex++);
1844        }
1845    };
1846
1847    auto parent = TestUtils::createNode(0, 0, 200, 200,
1848            [](RenderProperties& props, RecordingCanvas& canvas) {
1849        // save/restore outside of reorderBarrier, so they don't get moved out of place
1850        canvas.translate(20, 10);
1851        int count = canvas.saveLayerAlpha(30, 50, 130, 150, 128, SaveFlags::ClipToLayer);
1852        canvas.insertReorderBarrier(true);
1853        canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
1854        canvas.insertReorderBarrier(false);
1855        canvas.restoreToCount(count);
1856    });
1857
1858    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
1859            (FrameBuilder::LightGeometry) {{ 100, 100, 100 }, 50}, Caches::getInstance());
1860    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent));
1861
1862    ShadowSaveLayerTestRenderer renderer;
1863    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1864    EXPECT_EQ(6, renderer.getIndex());
1865}
1866
1867RENDERTHREAD_TEST(FrameBuilder, shadowHwLayer) {
1868    class ShadowHwLayerTestRenderer : public TestRendererBase {
1869    public:
1870        void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override {
1871            EXPECT_EQ(0, mIndex++);
1872        }
1873        void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
1874            EXPECT_EQ(1, mIndex++);
1875            EXPECT_FLOAT_EQ(50, op.shadowTask->lightCenter.x);
1876            EXPECT_FLOAT_EQ(40, op.shadowTask->lightCenter.y);
1877            EXPECT_FLOAT_EQ(30, op.shadowTask->lightRadius);
1878        }
1879        void onRectOp(const RectOp& op, const BakedOpState& state) override {
1880            EXPECT_EQ(2, mIndex++);
1881        }
1882        void endLayer() override {
1883            EXPECT_EQ(3, mIndex++);
1884        }
1885        void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
1886            EXPECT_EQ(4, mIndex++);
1887        }
1888    };
1889
1890    auto parent = TestUtils::createNode(50, 60, 150, 160,
1891            [](RenderProperties& props, RecordingCanvas& canvas) {
1892        props.mutateLayerProperties().setType(LayerType::RenderLayer);
1893        canvas.insertReorderBarrier(true);
1894        canvas.save(SaveFlags::MatrixClip);
1895        canvas.translate(20, 10);
1896        canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
1897        canvas.restore();
1898    });
1899    OffscreenBuffer** layerHandle = parent->getLayerHandle();
1900
1901    // create RenderNode's layer here in same way prepareTree would, setting windowTransform
1902    OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 100, 100);
1903    Matrix4 windowTransform;
1904    windowTransform.loadTranslate(50, 60, 0); // total transform of layer's origin
1905    layer.setWindowTransform(windowTransform);
1906    *layerHandle = &layer;
1907
1908    auto syncedNode = TestUtils::getSyncedNode(parent);
1909    LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
1910    layerUpdateQueue.enqueueLayerWithDamage(parent.get(), Rect(100, 100));
1911
1912    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
1913            (FrameBuilder::LightGeometry) {{ 100, 100, 100 }, 30}, Caches::getInstance());
1914    frameBuilder.deferLayers(layerUpdateQueue);
1915    frameBuilder.deferRenderNode(*syncedNode);
1916
1917    ShadowHwLayerTestRenderer renderer;
1918    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1919    EXPECT_EQ(5, renderer.getIndex());
1920
1921    // clean up layer pointer, so we can safely destruct RenderNode
1922    *layerHandle = nullptr;
1923}
1924
1925RENDERTHREAD_TEST(FrameBuilder, shadowLayering) {
1926    class ShadowLayeringTestRenderer : public TestRendererBase {
1927    public:
1928        void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
1929            int index = mIndex++;
1930            EXPECT_TRUE(index == 0 || index == 1);
1931        }
1932        void onRectOp(const RectOp& op, const BakedOpState& state) override {
1933            int index = mIndex++;
1934            EXPECT_TRUE(index == 2 || index == 3);
1935        }
1936    };
1937    auto parent = TestUtils::createNode(0, 0, 200, 200,
1938            [](RenderProperties& props, RecordingCanvas& canvas) {
1939        canvas.insertReorderBarrier(true);
1940        canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
1941        canvas.drawRenderNode(createWhiteRectShadowCaster(5.0001f).get());
1942    });
1943    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
1944            (FrameBuilder::LightGeometry) {{ 100, 100, 100 }, 50}, Caches::getInstance());
1945    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent));
1946
1947    ShadowLayeringTestRenderer renderer;
1948    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1949    EXPECT_EQ(4, renderer.getIndex());
1950}
1951
1952RENDERTHREAD_TEST(FrameBuilder, shadowClipping) {
1953    class ShadowClippingTestRenderer : public TestRendererBase {
1954    public:
1955        void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
1956            EXPECT_EQ(0, mIndex++);
1957            EXPECT_EQ(Rect(25, 25, 75, 75), state.computedState.clipState->rect)
1958                    << "Shadow must respect pre-barrier canvas clip value.";
1959        }
1960        void onRectOp(const RectOp& op, const BakedOpState& state) override {
1961            EXPECT_EQ(1, mIndex++);
1962        }
1963    };
1964    auto parent = TestUtils::createNode(0, 0, 100, 100,
1965            [](RenderProperties& props, RecordingCanvas& canvas) {
1966        // Apply a clip before the reorder barrier/shadow casting child is drawn.
1967        // This clip must be applied to the shadow cast by the child.
1968        canvas.clipRect(25, 25, 75, 75, SkRegion::kIntersect_Op);
1969        canvas.insertReorderBarrier(true);
1970        canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
1971    });
1972
1973    FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100,
1974            (FrameBuilder::LightGeometry) {{ 100, 100, 100 }, 50}, Caches::getInstance());
1975    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent));
1976
1977    ShadowClippingTestRenderer renderer;
1978    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1979    EXPECT_EQ(2, renderer.getIndex());
1980}
1981
1982static void testProperty(std::function<void(RenderProperties&)> propSetupCallback,
1983        std::function<void(const RectOp&, const BakedOpState&)> opValidateCallback) {
1984    class PropertyTestRenderer : public TestRendererBase {
1985    public:
1986        PropertyTestRenderer(std::function<void(const RectOp&, const BakedOpState&)> callback)
1987                : mCallback(callback) {}
1988        void onRectOp(const RectOp& op, const BakedOpState& state) override {
1989            EXPECT_EQ(mIndex++, 0);
1990            mCallback(op, state);
1991        }
1992        std::function<void(const RectOp&, const BakedOpState&)> mCallback;
1993    };
1994
1995    auto node = TestUtils::createNode(0, 0, 100, 100,
1996            [propSetupCallback](RenderProperties& props, RecordingCanvas& canvas) {
1997        propSetupCallback(props);
1998        SkPaint paint;
1999        paint.setColor(SK_ColorWHITE);
2000        canvas.drawRect(0, 0, 100, 100, paint);
2001    });
2002
2003    FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 200, 200,
2004            sLightGeometry, Caches::getInstance());
2005    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
2006
2007    PropertyTestRenderer renderer(opValidateCallback);
2008    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
2009    EXPECT_EQ(1, renderer.getIndex()) << "Should have seen one op";
2010}
2011
2012RENDERTHREAD_TEST(FrameBuilder, renderPropOverlappingRenderingAlpha) {
2013    testProperty([](RenderProperties& properties) {
2014        properties.setAlpha(0.5f);
2015        properties.setHasOverlappingRendering(false);
2016    }, [](const RectOp& op, const BakedOpState& state) {
2017        EXPECT_EQ(0.5f, state.alpha) << "Alpha should be applied directly to op";
2018    });
2019}
2020
2021RENDERTHREAD_TEST(FrameBuilder, renderPropClipping) {
2022    testProperty([](RenderProperties& properties) {
2023        properties.setClipToBounds(true);
2024        properties.setClipBounds(Rect(10, 20, 300, 400));
2025    }, [](const RectOp& op, const BakedOpState& state) {
2026        EXPECT_EQ(Rect(10, 20, 100, 100), state.computedState.clippedBounds)
2027                << "Clip rect should be intersection of node bounds and clip bounds";
2028    });
2029}
2030
2031RENDERTHREAD_TEST(FrameBuilder, renderPropRevealClip) {
2032    testProperty([](RenderProperties& properties) {
2033        properties.mutableRevealClip().set(true, 50, 50, 25);
2034    }, [](const RectOp& op, const BakedOpState& state) {
2035        ASSERT_NE(nullptr, state.roundRectClipState);
2036        EXPECT_TRUE(state.roundRectClipState->highPriority);
2037        EXPECT_EQ(25, state.roundRectClipState->radius);
2038        EXPECT_EQ(Rect(50, 50, 50, 50), state.roundRectClipState->innerRect);
2039    });
2040}
2041
2042RENDERTHREAD_TEST(FrameBuilder, renderPropOutlineClip) {
2043    testProperty([](RenderProperties& properties) {
2044        properties.mutableOutline().setShouldClip(true);
2045        properties.mutableOutline().setRoundRect(10, 20, 30, 40, 5.0f, 0.5f);
2046    }, [](const RectOp& op, const BakedOpState& state) {
2047        ASSERT_NE(nullptr, state.roundRectClipState);
2048        EXPECT_FALSE(state.roundRectClipState->highPriority);
2049        EXPECT_EQ(5, state.roundRectClipState->radius);
2050        EXPECT_EQ(Rect(15, 25, 25, 35), state.roundRectClipState->innerRect);
2051    });
2052}
2053
2054RENDERTHREAD_TEST(FrameBuilder, renderPropTransform) {
2055    testProperty([](RenderProperties& properties) {
2056        properties.setLeftTopRightBottom(10, 10, 110, 110);
2057
2058        SkMatrix staticMatrix = SkMatrix::MakeScale(1.2f, 1.2f);
2059        properties.setStaticMatrix(&staticMatrix);
2060
2061        // ignored, since static overrides animation
2062        SkMatrix animationMatrix = SkMatrix::MakeTrans(15, 15);
2063        properties.setAnimationMatrix(&animationMatrix);
2064
2065        properties.setTranslationX(10);
2066        properties.setTranslationY(20);
2067        properties.setScaleX(0.5f);
2068        properties.setScaleY(0.7f);
2069    }, [](const RectOp& op, const BakedOpState& state) {
2070        Matrix4 matrix;
2071        matrix.loadTranslate(10, 10, 0); // left, top
2072        matrix.scale(1.2f, 1.2f, 1); // static matrix
2073        // ignore animation matrix, since static overrides it
2074
2075        // translation xy
2076        matrix.translate(10, 20);
2077
2078        // scale xy (from default pivot - center)
2079        matrix.translate(50, 50);
2080        matrix.scale(0.5f, 0.7f, 1);
2081        matrix.translate(-50, -50);
2082        EXPECT_MATRIX_APPROX_EQ(matrix, state.computedState.transform)
2083                << "Op draw matrix must match expected combination of transformation properties";
2084    });
2085}
2086
2087struct SaveLayerAlphaData {
2088    uint32_t layerWidth = 0;
2089    uint32_t layerHeight = 0;
2090    Rect rectClippedBounds;
2091    Matrix4 rectMatrix;
2092    Matrix4 drawLayerMatrix;
2093};
2094/**
2095 * Constructs a view to hit the temporary layer alpha property implementation:
2096 *     a) 0 < alpha < 1
2097 *     b) too big for layer (larger than maxTextureSize)
2098 *     c) overlapping rendering content
2099 * returning observed data about layer size and content clip/transform.
2100 *
2101 * Used to validate clipping behavior of temporary layer, where requested layer size is reduced
2102 * (for efficiency, and to fit in layer size constraints) based on parent clip.
2103 */
2104void testSaveLayerAlphaClip(SaveLayerAlphaData* outObservedData,
2105        std::function<void(RenderProperties&)> propSetupCallback) {
2106    class SaveLayerAlphaClipTestRenderer : public TestRendererBase {
2107    public:
2108        SaveLayerAlphaClipTestRenderer(SaveLayerAlphaData* outData)
2109                : mOutData(outData) {}
2110
2111        OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override {
2112            EXPECT_EQ(0, mIndex++);
2113            mOutData->layerWidth = width;
2114            mOutData->layerHeight = height;
2115            return nullptr;
2116        }
2117        void onRectOp(const RectOp& op, const BakedOpState& state) override {
2118            EXPECT_EQ(1, mIndex++);
2119
2120            mOutData->rectClippedBounds = state.computedState.clippedBounds;
2121            mOutData->rectMatrix = state.computedState.transform;
2122        }
2123        void endLayer() override {
2124            EXPECT_EQ(2, mIndex++);
2125        }
2126        void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
2127            EXPECT_EQ(3, mIndex++);
2128            mOutData->drawLayerMatrix = state.computedState.transform;
2129        }
2130        void recycleTemporaryLayer(OffscreenBuffer* offscreenBuffer) override {
2131            EXPECT_EQ(4, mIndex++);
2132        }
2133    private:
2134        SaveLayerAlphaData* mOutData;
2135    };
2136
2137    ASSERT_GT(10000, DeviceInfo::get()->maxTextureSize())
2138            << "Node must be bigger than max texture size to exercise saveLayer codepath";
2139    auto node = TestUtils::createNode(0, 0, 10000, 10000,
2140            [&propSetupCallback](RenderProperties& properties, RecordingCanvas& canvas) {
2141        properties.setHasOverlappingRendering(true);
2142        properties.setAlpha(0.5f); // force saveLayer, since too big for HW layer
2143        // apply other properties
2144        propSetupCallback(properties);
2145
2146        SkPaint paint;
2147        paint.setColor(SK_ColorWHITE);
2148        canvas.drawRect(0, 0, 10000, 10000, paint);
2149    });
2150    auto syncedNode = TestUtils::getSyncedNode(node); // sync before querying height
2151
2152    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
2153                sLightGeometry, Caches::getInstance());
2154    frameBuilder.deferRenderNode(*syncedNode);
2155
2156    SaveLayerAlphaClipTestRenderer renderer(outObservedData);
2157    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
2158
2159    // assert, since output won't be valid if we haven't seen a save layer triggered
2160    ASSERT_EQ(5, renderer.getIndex()) << "Test must trigger saveLayer alpha behavior.";
2161}
2162
2163RENDERTHREAD_TEST(FrameBuilder, renderPropSaveLayerAlphaClipBig) {
2164    SaveLayerAlphaData observedData;
2165    testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) {
2166        properties.setTranslationX(10); // offset rendering content
2167        properties.setTranslationY(-2000); // offset rendering content
2168    });
2169    EXPECT_EQ(190u, observedData.layerWidth);
2170    EXPECT_EQ(200u, observedData.layerHeight);
2171    EXPECT_EQ(Rect(190, 200), observedData.rectClippedBounds)
2172            << "expect content to be clipped to screen area";
2173    Matrix4 expected;
2174    expected.loadTranslate(0, -2000, 0);
2175    EXPECT_MATRIX_APPROX_EQ(expected, observedData.rectMatrix)
2176            << "expect content to be translated as part of being clipped";
2177    expected.loadTranslate(10, 0, 0);
2178    EXPECT_MATRIX_APPROX_EQ(expected, observedData.drawLayerMatrix)
2179                << "expect drawLayer to be translated as part of being clipped";
2180}
2181
2182RENDERTHREAD_TEST(FrameBuilder, renderPropSaveLayerAlphaRotate) {
2183    SaveLayerAlphaData observedData;
2184    testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) {
2185        // Translate and rotate the view so that the only visible part is the top left corner of
2186        // the view. It will form an isosceles right triangle with a long side length of 200 at the
2187        // bottom of the viewport.
2188        properties.setTranslationX(100);
2189        properties.setTranslationY(100);
2190        properties.setPivotX(0);
2191        properties.setPivotY(0);
2192        properties.setRotation(45);
2193    });
2194    // ceil(sqrt(2) / 2 * 200) = 142
2195    EXPECT_EQ(142u, observedData.layerWidth);
2196    EXPECT_EQ(142u, observedData.layerHeight);
2197    EXPECT_EQ(Rect(142, 142), observedData.rectClippedBounds);
2198    EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), observedData.rectMatrix);
2199}
2200
2201RENDERTHREAD_TEST(FrameBuilder, renderPropSaveLayerAlphaScale) {
2202    SaveLayerAlphaData observedData;
2203    testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) {
2204        properties.setPivotX(0);
2205        properties.setPivotY(0);
2206        properties.setScaleX(2);
2207        properties.setScaleY(0.5f);
2208    });
2209    EXPECT_EQ(100u, observedData.layerWidth);
2210    EXPECT_EQ(400u, observedData.layerHeight);
2211    EXPECT_EQ(Rect(100, 400), observedData.rectClippedBounds);
2212    EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), observedData.rectMatrix);
2213}
2214
2215RENDERTHREAD_TEST(FrameBuilder, clip_replace) {
2216    class ClipReplaceTestRenderer : public TestRendererBase {
2217    public:
2218        void onColorOp(const ColorOp& op, const BakedOpState& state) override {
2219            EXPECT_EQ(0, mIndex++);
2220            EXPECT_TRUE(op.localClip->intersectWithRoot);
2221            EXPECT_EQ(Rect(20, 10, 30, 40), state.computedState.clipState->rect)
2222                    << "Expect resolved clip to be intersection of viewport clip and clip op";
2223        }
2224    };
2225    auto node = TestUtils::createNode(20, 20, 30, 30,
2226            [](RenderProperties& props, RecordingCanvas& canvas) {
2227        canvas.clipRect(0, -20, 10, 30, SkRegion::kReplace_Op);
2228        canvas.drawColor(SK_ColorWHITE, SkXfermode::Mode::kSrcOver_Mode);
2229    });
2230
2231    FrameBuilder frameBuilder(SkRect::MakeLTRB(10, 10, 40, 40), 50, 50,
2232            sLightGeometry, Caches::getInstance());
2233    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
2234
2235    ClipReplaceTestRenderer renderer;
2236    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
2237    EXPECT_EQ(1, renderer.getIndex());
2238}
2239
2240} // namespace uirenderer
2241} // namespace android
2242