FrameBuilderTests.cpp revision 834653bcf80a3879c4d80004469053f205d45b69
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<RecordingCanvas>(0, 0, 100, 200,
131            [](RenderProperties& props, RecordingCanvas& canvas) {
132        sk_sp<Bitmap> bitmap(TestUtils::createBitmap(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<RecordingCanvas>(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<RecordingCanvas>(0, 0, 200, 200,
175            [](RenderProperties& props, RecordingCanvas& canvas) {
176        canvas.save(SaveFlags::MatrixClip);
177        canvas.clipRect(200, 200, 400, 400, SkClipOp::kIntersect); // 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<RecordingCanvas>(0, 0, 200, 200,
202            [](RenderProperties& props, RecordingCanvas& canvas) {
203
204        sk_sp<Bitmap> bitmap(TestUtils::createBitmap(10, 10,
205                kAlpha_8_SkColorType)); // Disable merging by using alpha 8 bitmap
206
207        // Alternate between drawing rects and bitmaps, with bitmaps overlapping rects.
208        // Rects don't overlap bitmaps, so bitmaps should be brought to front as a group.
209        canvas.save(SaveFlags::MatrixClip);
210        for (int i = 0; i < LOOPS; i++) {
211            canvas.translate(0, 10);
212            canvas.drawRect(0, 0, 10, 10, SkPaint());
213            canvas.drawBitmap(*bitmap, 5, 0, nullptr);
214        }
215        canvas.restore();
216    });
217    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
218            sLightGeometry, Caches::getInstance());
219    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
220
221    SimpleBatchingTestRenderer renderer;
222    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
223    EXPECT_EQ(2 * LOOPS, renderer.getIndex())
224            << "Expect number of ops = 2 * loop count";
225}
226
227RENDERTHREAD_TEST(FrameBuilder, deferRenderNode_translateClip) {
228    class DeferRenderNodeTranslateClipTestRenderer : public TestRendererBase {
229    public:
230        void onRectOp(const RectOp& op, const BakedOpState& state) override {
231            EXPECT_EQ(0, mIndex++);
232            EXPECT_EQ(Rect(5, 10, 55, 60), state.computedState.clippedBounds);
233            EXPECT_EQ(OpClipSideFlags::Right | OpClipSideFlags::Bottom,
234                    state.computedState.clipSideFlags);
235        }
236    };
237
238    auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100,
239            [](RenderProperties& props, RecordingCanvas& canvas) {
240        canvas.drawRect(0, 0, 100, 100, SkPaint());
241    });
242
243    FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100,
244            sLightGeometry, Caches::getInstance());
245    frameBuilder.deferRenderNode(5, 10, Rect(50, 50), // translate + clip node
246            *TestUtils::getSyncedNode(node));
247
248    DeferRenderNodeTranslateClipTestRenderer renderer;
249    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
250    EXPECT_EQ(1, renderer.getIndex());
251}
252
253RENDERTHREAD_TEST(FrameBuilder, deferRenderNodeScene) {
254    class DeferRenderNodeSceneTestRenderer : public TestRendererBase {
255    public:
256        void onRectOp(const RectOp& op, const BakedOpState& state) override {
257            const Rect& clippedBounds = state.computedState.clippedBounds;
258            Matrix4 expected;
259            switch (mIndex++) {
260            case 0:
261                // background - left side
262                EXPECT_EQ(Rect(600, 100, 700, 500), clippedBounds);
263                expected.loadTranslate(100, 100, 0);
264                break;
265            case 1:
266                // background - top side
267                EXPECT_EQ(Rect(100, 400, 600, 500), clippedBounds);
268                expected.loadTranslate(100, 100, 0);
269                break;
270            case 2:
271                // content
272                EXPECT_EQ(Rect(100, 100, 700, 500), clippedBounds);
273                expected.loadTranslate(-50, -50, 0);
274                break;
275            case 3:
276                // overlay
277                EXPECT_EQ(Rect(0, 0, 800, 200), clippedBounds);
278                break;
279            default:
280                ADD_FAILURE() << "Too many rects observed";
281            }
282            EXPECT_EQ(expected, state.computedState.transform);
283        }
284    };
285
286    std::vector<sp<RenderNode>> nodes;
287    SkPaint transparentPaint;
288    transparentPaint.setAlpha(128);
289
290    // backdrop
291    nodes.push_back(TestUtils::createNode<RecordingCanvas>(100, 100, 700, 500, // 600x400
292            [&transparentPaint](RenderProperties& props, RecordingCanvas& canvas) {
293        canvas.drawRect(0, 0, 600, 400, transparentPaint);
294    }));
295
296    // content
297    Rect contentDrawBounds(150, 150, 650, 450); // 500x300
298    nodes.push_back(TestUtils::createNode<RecordingCanvas>(0, 0, 800, 600,
299            [&transparentPaint](RenderProperties& props, RecordingCanvas& canvas) {
300        canvas.drawRect(0, 0, 800, 600, transparentPaint);
301    }));
302
303    // overlay
304    nodes.push_back(TestUtils::createNode<RecordingCanvas>(0, 0, 800, 600,
305            [&transparentPaint](RenderProperties& props, RecordingCanvas& canvas) {
306        canvas.drawRect(0, 0, 800, 200, transparentPaint);
307    }));
308
309    for (auto& node : nodes) {
310        TestUtils::syncHierarchyPropertiesAndDisplayList(node);
311    }
312
313    FrameBuilder frameBuilder(SkRect::MakeWH(800, 600), 800, 600,
314            sLightGeometry, Caches::getInstance());
315    frameBuilder.deferRenderNodeScene(nodes, contentDrawBounds);
316
317    DeferRenderNodeSceneTestRenderer renderer;
318    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
319    EXPECT_EQ(4, renderer.getIndex());
320}
321
322RENDERTHREAD_TEST(FrameBuilder, empty_noFbo0) {
323    class EmptyNoFbo0TestRenderer : public TestRendererBase {
324    public:
325        void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
326            ADD_FAILURE() << "Primary frame draw not expected in this test";
327        }
328        void endFrame(const Rect& repaintRect) override {
329            ADD_FAILURE() << "Primary frame draw not expected in this test";
330        }
331    };
332
333    // Use layer update constructor, so no work is enqueued for Fbo0
334    LayerUpdateQueue emptyLayerUpdateQueue;
335    FrameBuilder frameBuilder(emptyLayerUpdateQueue, sLightGeometry, Caches::getInstance());
336    EmptyNoFbo0TestRenderer renderer;
337    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
338}
339
340RENDERTHREAD_TEST(FrameBuilder, empty_withFbo0) {
341    class EmptyWithFbo0TestRenderer : public TestRendererBase {
342    public:
343        void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
344            EXPECT_EQ(0, mIndex++);
345        }
346        void endFrame(const Rect& repaintRect) override {
347            EXPECT_EQ(1, mIndex++);
348        }
349    };
350    auto node = TestUtils::createNode<RecordingCanvas>(10, 10, 110, 110,
351            [](RenderProperties& props, RecordingCanvas& canvas) {
352        // no drawn content
353    });
354
355    // Draw, but pass node without draw content, so no work is done for primary frame
356    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
357            sLightGeometry, Caches::getInstance());
358    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
359
360    EmptyWithFbo0TestRenderer renderer;
361    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
362    EXPECT_EQ(2, renderer.getIndex()) << "No drawing content produced,"
363            " but fbo0 update lifecycle should still be observed";
364}
365
366RENDERTHREAD_TEST(FrameBuilder, avoidOverdraw_rects) {
367    class AvoidOverdrawRectsTestRenderer : public TestRendererBase {
368    public:
369        void onRectOp(const RectOp& op, const BakedOpState& state) override {
370            EXPECT_EQ(mIndex++, 0) << "Should be one rect";
371            EXPECT_EQ(Rect(10, 10, 190, 190), op.unmappedBounds)
372                    << "Last rect should occlude others.";
373        }
374    };
375    auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200,
376            [](RenderProperties& props, RecordingCanvas& canvas) {
377        canvas.drawRect(0, 0, 200, 200, SkPaint());
378        canvas.drawRect(0, 0, 200, 200, SkPaint());
379        canvas.drawRect(10, 10, 190, 190, SkPaint());
380    });
381
382    // Damage (and therefore clip) is same as last draw, subset of renderable area.
383    // This means last op occludes other contents, and they'll be rejected to avoid overdraw.
384    FrameBuilder frameBuilder(SkRect::MakeLTRB(10, 10, 190, 190), 200, 200,
385            sLightGeometry, Caches::getInstance());
386    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
387
388    EXPECT_EQ(3u, node->getDisplayList()->getOps().size())
389            << "Recording must not have rejected ops, in order for this test to be valid";
390
391    AvoidOverdrawRectsTestRenderer renderer;
392    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
393    EXPECT_EQ(1, renderer.getIndex()) << "Expect exactly one op";
394}
395
396RENDERTHREAD_TEST(FrameBuilder, avoidOverdraw_bitmaps) {
397    static sk_sp<Bitmap> opaqueBitmap(TestUtils::createBitmap(50, 50,
398            SkColorType::kRGB_565_SkColorType));
399    static sk_sp<Bitmap> transpBitmap(TestUtils::createBitmap(50, 50,
400            SkColorType::kAlpha_8_SkColorType));
401    class AvoidOverdrawBitmapsTestRenderer : public TestRendererBase {
402    public:
403        void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override {
404            switch(mIndex++) {
405            case 0:
406                EXPECT_EQ(opaqueBitmap.get(), op.bitmap);
407                break;
408            case 1:
409                EXPECT_EQ(transpBitmap.get(), op.bitmap);
410                break;
411            default:
412                ADD_FAILURE() << "Only two ops expected.";
413            }
414        }
415    };
416
417    auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 50, 50,
418            [](RenderProperties& props, RecordingCanvas& canvas) {
419        canvas.drawRect(0, 0, 50, 50, SkPaint());
420        canvas.drawRect(0, 0, 50, 50, SkPaint());
421        canvas.drawBitmap(*transpBitmap, 0, 0, nullptr);
422
423        // only the below draws should remain, since they're
424        canvas.drawBitmap(*opaqueBitmap, 0, 0, nullptr);
425        canvas.drawBitmap(*transpBitmap, 0, 0, nullptr);
426    });
427    FrameBuilder frameBuilder(SkRect::MakeWH(50, 50), 50, 50,
428            sLightGeometry, Caches::getInstance());
429    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
430
431    EXPECT_EQ(5u, node->getDisplayList()->getOps().size())
432            << "Recording must not have rejected ops, in order for this test to be valid";
433
434    AvoidOverdrawBitmapsTestRenderer renderer;
435    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
436    EXPECT_EQ(2, renderer.getIndex()) << "Expect exactly two ops";
437}
438
439RENDERTHREAD_TEST(FrameBuilder, clippedMerging) {
440    class ClippedMergingTestRenderer : public TestRendererBase {
441    public:
442        void onMergedBitmapOps(const MergedBakedOpList& opList) override {
443            EXPECT_EQ(0, mIndex);
444            mIndex += opList.count;
445            EXPECT_EQ(4u, opList.count);
446            EXPECT_EQ(Rect(10, 10, 90, 90), opList.clip);
447            EXPECT_EQ(OpClipSideFlags::Left | OpClipSideFlags::Top | OpClipSideFlags::Right,
448                    opList.clipSideFlags);
449        }
450    };
451    auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100,
452            [](RenderProperties& props, RecordingCanvas& canvas) {
453        sk_sp<Bitmap> bitmap(TestUtils::createBitmap(20, 20));
454
455        // left side clipped (to inset left half)
456        canvas.save(SaveFlags::MatrixClip);
457        canvas.clipRect(10, 0, 50, 100, SkClipOp::kIntersect);
458        canvas.drawBitmap(*bitmap, 0, 40, nullptr);
459        canvas.restore();
460
461        // top side clipped (to inset top half)
462        canvas.save(SaveFlags::MatrixClip);
463        canvas.clipRect(0, 10, 100, 50, SkClipOp::kIntersect);
464        canvas.drawBitmap(*bitmap, 40, 0, nullptr);
465        canvas.restore();
466
467        // right side clipped (to inset right half)
468        canvas.save(SaveFlags::MatrixClip);
469        canvas.clipRect(50, 0, 90, 100, SkClipOp::kIntersect);
470        canvas.drawBitmap(*bitmap, 80, 40, nullptr);
471        canvas.restore();
472
473        // bottom not clipped, just abutting (inset bottom half)
474        canvas.save(SaveFlags::MatrixClip);
475        canvas.clipRect(0, 50, 100, 90, SkClipOp::kIntersect);
476        canvas.drawBitmap(*bitmap, 40, 70, nullptr);
477        canvas.restore();
478    });
479
480    FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100,
481            sLightGeometry, Caches::getInstance());
482    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
483
484    ClippedMergingTestRenderer renderer;
485    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
486    EXPECT_EQ(4, renderer.getIndex());
487}
488
489RENDERTHREAD_TEST(FrameBuilder, regionClipStopsMerge) {
490    class RegionClipStopsMergeTestRenderer : public TestRendererBase {
491    public:
492        void onTextOp(const TextOp& op, const BakedOpState& state) override { mIndex++; }
493    };
494    auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 400, 400,
495            [](RenderProperties& props, RecordingCanvas& canvas) {
496        SkPath path;
497        path.addCircle(200, 200, 200, SkPath::kCW_Direction);
498        canvas.save(SaveFlags::MatrixClip);
499        canvas.clipPath(&path, SkClipOp::kIntersect);
500        SkPaint paint;
501        paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
502        paint.setAntiAlias(true);
503        paint.setTextSize(50);
504        TestUtils::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 100);
505        TestUtils::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 200);
506        canvas.restore();
507    });
508
509    FrameBuilder frameBuilder(SkRect::MakeWH(400, 400), 400, 400,
510            sLightGeometry, Caches::getInstance());
511    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
512
513    RegionClipStopsMergeTestRenderer renderer;
514    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
515    EXPECT_EQ(2, renderer.getIndex());
516}
517
518RENDERTHREAD_TEST(FrameBuilder, textMerging) {
519    class TextMergingTestRenderer : public TestRendererBase {
520    public:
521        void onMergedTextOps(const MergedBakedOpList& opList) override {
522            EXPECT_EQ(0, mIndex);
523            mIndex += opList.count;
524            EXPECT_EQ(2u, opList.count);
525            EXPECT_EQ(OpClipSideFlags::Top, opList.clipSideFlags);
526            EXPECT_EQ(OpClipSideFlags::Top, opList.states[0]->computedState.clipSideFlags);
527            EXPECT_EQ(OpClipSideFlags::None, opList.states[1]->computedState.clipSideFlags);
528        }
529    };
530    auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 400, 400,
531            [](RenderProperties& props, RecordingCanvas& canvas) {
532        SkPaint paint;
533        paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
534        paint.setAntiAlias(true);
535        paint.setTextSize(50);
536        TestUtils::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 0); // will be top clipped
537        TestUtils::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 100); // not clipped
538    });
539    FrameBuilder frameBuilder(SkRect::MakeWH(400, 400), 400, 400,
540            sLightGeometry, Caches::getInstance());
541    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
542
543    TextMergingTestRenderer renderer;
544    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
545    EXPECT_EQ(2, renderer.getIndex()) << "Expect 2 ops";
546}
547
548RENDERTHREAD_TEST(FrameBuilder, textStrikethrough) {
549    const int LOOPS = 5;
550    class TextStrikethroughTestRenderer : public TestRendererBase {
551    public:
552        void onRectOp(const RectOp& op, const BakedOpState& state) override {
553            EXPECT_TRUE(mIndex++ >= LOOPS) << "Strikethrough rects should be above all text";
554        }
555        void onMergedTextOps(const MergedBakedOpList& opList) override {
556            EXPECT_EQ(0, mIndex);
557            mIndex += opList.count;
558            EXPECT_EQ(5u, opList.count);
559        }
560    };
561    auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 2000,
562            [](RenderProperties& props, RecordingCanvas& canvas) {
563        SkPaint textPaint;
564        textPaint.setAntiAlias(true);
565        textPaint.setTextSize(20);
566        textPaint.setStrikeThruText(true);
567        textPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
568        for (int i = 0; i < LOOPS; i++) {
569            TestUtils::drawUtf8ToCanvas(&canvas, "test text", textPaint, 10, 100 * (i + 1));
570        }
571    });
572
573    FrameBuilder frameBuilder(SkRect::MakeWH(200, 2000), 200, 2000,
574            sLightGeometry, Caches::getInstance());
575    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
576
577    TextStrikethroughTestRenderer renderer;
578    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
579    EXPECT_EQ(2 * LOOPS, renderer.getIndex())
580            << "Expect number of ops = 2 * loop count";
581}
582
583static auto styles = {
584        SkPaint::kFill_Style, SkPaint::kStroke_Style, SkPaint::kStrokeAndFill_Style };
585
586RENDERTHREAD_TEST(FrameBuilder, textStyle) {
587    class TextStyleTestRenderer : public TestRendererBase {
588    public:
589        void onMergedTextOps(const MergedBakedOpList& opList) override {
590            ASSERT_EQ(0, mIndex);
591            ASSERT_EQ(3u, opList.count);
592            mIndex += opList.count;
593
594            int index = 0;
595            for (auto style : styles) {
596                auto state = opList.states[index++];
597                ASSERT_EQ(style, state->op->paint->getStyle())
598                        << "Remainder of validation relies upon stable merged order";
599                ASSERT_EQ(0, state->computedState.clipSideFlags)
600                        << "Clipped bounds validation requires unclipped ops";
601            }
602
603            Rect fill = opList.states[0]->computedState.clippedBounds;
604            Rect stroke = opList.states[1]->computedState.clippedBounds;
605            EXPECT_EQ(stroke, opList.states[2]->computedState.clippedBounds)
606                    << "Stroke+Fill should be same as stroke";
607
608            EXPECT_TRUE(stroke.contains(fill));
609            EXPECT_FALSE(fill.contains(stroke));
610
611            // outset by half the stroke width
612            Rect outsetFill(fill);
613            outsetFill.outset(5);
614            EXPECT_EQ(stroke, outsetFill);
615        }
616    };
617    auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 400, 400,
618            [](RenderProperties& props, RecordingCanvas& canvas) {
619        SkPaint paint;
620        paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
621        paint.setAntiAlias(true);
622        paint.setTextSize(50);
623        paint.setStrokeWidth(10);
624
625        // draw 3 copies of the same text overlapping, each with a different style.
626        // They'll get merged, but with
627        for (auto style : styles) {
628            paint.setStyle(style);
629            TestUtils::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 100);
630        }
631    });
632    FrameBuilder frameBuilder(SkRect::MakeWH(400, 400), 400, 400,
633            sLightGeometry, Caches::getInstance());
634    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
635    TextStyleTestRenderer renderer;
636    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
637    EXPECT_EQ(3, renderer.getIndex()) << "Expect 3 ops";
638}
639
640RENDERTHREAD_TEST(FrameBuilder, textureLayer_clipLocalMatrix) {
641    class TextureLayerClipLocalMatrixTestRenderer : public TestRendererBase {
642    public:
643        void onTextureLayerOp(const TextureLayerOp& op, const BakedOpState& state) override {
644            EXPECT_EQ(0, mIndex++);
645            EXPECT_EQ(Rect(50, 50, 150, 150), state.computedState.clipRect());
646            EXPECT_EQ(Rect(50, 50, 105, 105), state.computedState.clippedBounds);
647
648            Matrix4 expected;
649            expected.loadTranslate(5, 5, 0);
650            EXPECT_MATRIX_APPROX_EQ(expected, state.computedState.transform);
651        }
652    };
653
654    auto layerUpdater = TestUtils::createTextureLayerUpdater(renderThread, 100, 100,
655            SkMatrix::MakeTrans(5, 5));
656
657    auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200,
658            [&layerUpdater](RenderProperties& props, RecordingCanvas& canvas) {
659        canvas.save(SaveFlags::MatrixClip);
660        canvas.clipRect(50, 50, 150, 150, SkClipOp::kIntersect);
661        canvas.drawLayer(layerUpdater.get());
662        canvas.restore();
663    });
664
665    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
666            sLightGeometry, Caches::getInstance());
667    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
668
669    TextureLayerClipLocalMatrixTestRenderer renderer;
670    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
671    EXPECT_EQ(1, renderer.getIndex());
672}
673
674RENDERTHREAD_TEST(FrameBuilder, textureLayer_combineMatrices) {
675    class TextureLayerCombineMatricesTestRenderer : public TestRendererBase {
676    public:
677        void onTextureLayerOp(const TextureLayerOp& op, const BakedOpState& state) override {
678            EXPECT_EQ(0, mIndex++);
679
680            Matrix4 expected;
681            expected.loadTranslate(35, 45, 0);
682            EXPECT_MATRIX_APPROX_EQ(expected, state.computedState.transform);
683        }
684    };
685
686    auto layerUpdater = TestUtils::createTextureLayerUpdater(renderThread, 100, 100,
687            SkMatrix::MakeTrans(5, 5));
688
689    auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200,
690            [&layerUpdater](RenderProperties& props, RecordingCanvas& canvas) {
691        canvas.save(SaveFlags::MatrixClip);
692        canvas.translate(30, 40);
693        canvas.drawLayer(layerUpdater.get());
694        canvas.restore();
695    });
696
697    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
698            sLightGeometry, Caches::getInstance());
699    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
700
701    TextureLayerCombineMatricesTestRenderer renderer;
702    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
703    EXPECT_EQ(1, renderer.getIndex());
704}
705
706RENDERTHREAD_TEST(FrameBuilder, textureLayer_reject) {
707    auto layerUpdater = TestUtils::createTextureLayerUpdater(renderThread, 100, 100,
708            SkMatrix::MakeTrans(5, 5));
709    layerUpdater->backingLayer()->setRenderTarget(GL_NONE); // Should be rejected
710
711    auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200,
712            [&layerUpdater](RenderProperties& props, RecordingCanvas& canvas) {
713        canvas.drawLayer(layerUpdater.get());
714    });
715
716    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
717            sLightGeometry, Caches::getInstance());
718    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
719
720    FailRenderer renderer;
721    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
722}
723
724RENDERTHREAD_TEST(FrameBuilder, functor_reject) {
725    class FunctorTestRenderer : public TestRendererBase {
726    public:
727        void onFunctorOp(const FunctorOp& op, const BakedOpState& state) override {
728            EXPECT_EQ(0, mIndex++);
729        }
730    };
731    Functor noopFunctor;
732
733    // 1 million pixel tall view, scrolled down 80%
734    auto scrolledFunctorView = TestUtils::createNode<RecordingCanvas>(0, 0, 400, 1000000,
735            [&noopFunctor](RenderProperties& props, RecordingCanvas& canvas) {
736        canvas.translate(0, -800000);
737        canvas.callDrawGLFunction(&noopFunctor, nullptr);
738    });
739
740    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
741            sLightGeometry, Caches::getInstance());
742    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(scrolledFunctorView));
743
744    FunctorTestRenderer renderer;
745    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
746    EXPECT_EQ(1, renderer.getIndex()) << "Functor should not be rejected";
747}
748
749RENDERTHREAD_TEST(FrameBuilder, deferColorOp_unbounded) {
750    class ColorTestRenderer : public TestRendererBase {
751    public:
752        void onColorOp(const ColorOp& op, const BakedOpState& state) override {
753            EXPECT_EQ(0, mIndex++);
754            EXPECT_EQ(Rect(200, 200), state.computedState.clippedBounds)
755                    << "Color op should be expanded to bounds of surrounding";
756        }
757    };
758
759    auto unclippedColorView = TestUtils::createNode<RecordingCanvas>(0, 0, 10, 10,
760            [](RenderProperties& props, RecordingCanvas& canvas) {
761        props.setClipToBounds(false);
762        canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver);
763    });
764
765    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
766            sLightGeometry, Caches::getInstance());
767    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(unclippedColorView));
768
769    ColorTestRenderer renderer;
770    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
771    EXPECT_EQ(1, renderer.getIndex()) << "ColorOp should not be rejected";
772}
773
774TEST(FrameBuilder, renderNode) {
775    class RenderNodeTestRenderer : public TestRendererBase {
776    public:
777        void onRectOp(const RectOp& op, const BakedOpState& state) override {
778            switch(mIndex++) {
779            case 0:
780                EXPECT_EQ(Rect(200, 200), state.computedState.clippedBounds);
781                EXPECT_EQ(SK_ColorDKGRAY, op.paint->getColor());
782                break;
783            case 1:
784                EXPECT_EQ(Rect(50, 50, 150, 150), state.computedState.clippedBounds);
785                EXPECT_EQ(SK_ColorWHITE, op.paint->getColor());
786                break;
787            default:
788                ADD_FAILURE();
789            }
790        }
791    };
792
793    auto child = TestUtils::createNode<RecordingCanvas>(10, 10, 110, 110,
794            [](RenderProperties& props, RecordingCanvas& canvas) {
795        SkPaint paint;
796        paint.setColor(SK_ColorWHITE);
797        canvas.drawRect(0, 0, 100, 100, paint);
798    });
799
800    auto parent = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200,
801            [&child](RenderProperties& props, RecordingCanvas& canvas) {
802        SkPaint paint;
803        paint.setColor(SK_ColorDKGRAY);
804        canvas.drawRect(0, 0, 200, 200, paint);
805
806        canvas.save(SaveFlags::MatrixClip);
807        canvas.translate(40, 40);
808        canvas.drawRenderNode(child.get());
809        canvas.restore();
810    });
811
812    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
813            sLightGeometry, Caches::getInstance());
814    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent));
815
816    RenderNodeTestRenderer renderer;
817    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
818    EXPECT_EQ(2, renderer.getIndex());
819}
820
821RENDERTHREAD_TEST(FrameBuilder, clipped) {
822    class ClippedTestRenderer : public TestRendererBase {
823    public:
824        void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override {
825            EXPECT_EQ(0, mIndex++);
826            EXPECT_EQ(Rect(10, 20, 30, 40), state.computedState.clippedBounds);
827            EXPECT_EQ(Rect(10, 20, 30, 40), state.computedState.clipRect());
828            EXPECT_TRUE(state.computedState.transform.isIdentity());
829        }
830    };
831
832    auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200,
833            [](RenderProperties& props, RecordingCanvas& canvas) {
834        sk_sp<Bitmap> bitmap(TestUtils::createBitmap(200, 200));
835        canvas.drawBitmap(*bitmap, 0, 0, nullptr);
836    });
837
838    // clip to small area, should see in receiver
839    FrameBuilder frameBuilder(SkRect::MakeLTRB(10, 20, 30, 40), 200, 200,
840            sLightGeometry, Caches::getInstance());
841    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
842
843    ClippedTestRenderer renderer;
844    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
845}
846
847RENDERTHREAD_TEST(FrameBuilder, saveLayer_simple) {
848    class SaveLayerSimpleTestRenderer : public TestRendererBase {
849    public:
850        OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override {
851            EXPECT_EQ(0, mIndex++);
852            EXPECT_EQ(180u, width);
853            EXPECT_EQ(180u, height);
854            return nullptr;
855        }
856        void endLayer() override {
857            EXPECT_EQ(2, mIndex++);
858        }
859        void onRectOp(const RectOp& op, const BakedOpState& state) override {
860            EXPECT_EQ(1, mIndex++);
861            EXPECT_EQ(Rect(10, 10, 190, 190), op.unmappedBounds);
862            EXPECT_EQ(Rect(180, 180), state.computedState.clippedBounds);
863            EXPECT_EQ(Rect(180, 180), state.computedState.clipRect());
864
865            Matrix4 expectedTransform;
866            expectedTransform.loadTranslate(-10, -10, 0);
867            EXPECT_MATRIX_APPROX_EQ(expectedTransform, state.computedState.transform);
868        }
869        void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
870            EXPECT_EQ(3, mIndex++);
871            EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds);
872            EXPECT_EQ(Rect(200, 200), state.computedState.clipRect());
873            EXPECT_TRUE(state.computedState.transform.isIdentity());
874        }
875        void recycleTemporaryLayer(OffscreenBuffer* offscreenBuffer) override {
876            EXPECT_EQ(4, mIndex++);
877            EXPECT_EQ(nullptr, offscreenBuffer);
878        }
879    };
880
881    auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200,
882            [](RenderProperties& props, RecordingCanvas& canvas) {
883        canvas.saveLayerAlpha(10, 10, 190, 190, 128, SaveFlags::ClipToLayer);
884        canvas.drawRect(10, 10, 190, 190, SkPaint());
885        canvas.restore();
886    });
887
888    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
889            sLightGeometry, Caches::getInstance());
890    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
891
892    SaveLayerSimpleTestRenderer renderer;
893    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
894    EXPECT_EQ(5, renderer.getIndex());
895}
896
897RENDERTHREAD_TEST(FrameBuilder, saveLayer_nested) {
898    /* saveLayer1 { rect1, saveLayer2 { rect2 } } will play back as:
899     * - startTemporaryLayer2, rect2 endLayer2
900     * - startTemporaryLayer1, rect1, drawLayer2, endLayer1
901     * - startFrame, layerOp1, endFrame
902     */
903    class SaveLayerNestedTestRenderer : public TestRendererBase {
904    public:
905        OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override {
906            const int index = mIndex++;
907            if (index == 0) {
908                EXPECT_EQ(400u, width);
909                EXPECT_EQ(400u, height);
910                return (OffscreenBuffer*) 0x400;
911            } else if (index == 3) {
912                EXPECT_EQ(800u, width);
913                EXPECT_EQ(800u, height);
914                return (OffscreenBuffer*) 0x800;
915            } else { ADD_FAILURE(); }
916            return (OffscreenBuffer*) nullptr;
917        }
918        void endLayer() override {
919            int index = mIndex++;
920            EXPECT_TRUE(index == 2 || index == 6);
921        }
922        void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
923            EXPECT_EQ(7, mIndex++);
924        }
925        void endFrame(const Rect& repaintRect) override {
926            EXPECT_EQ(9, mIndex++);
927        }
928        void onRectOp(const RectOp& op, const BakedOpState& state) override {
929            const int index = mIndex++;
930            if (index == 1) {
931                EXPECT_EQ(Rect(400, 400), op.unmappedBounds); // inner rect
932            } else if (index == 4) {
933                EXPECT_EQ(Rect(800, 800), op.unmappedBounds); // outer rect
934            } else { ADD_FAILURE(); }
935        }
936        void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
937            const int index = mIndex++;
938            if (index == 5) {
939                EXPECT_EQ((OffscreenBuffer*)0x400, *op.layerHandle);
940                EXPECT_EQ(Rect(400, 400), op.unmappedBounds); // inner layer
941            } else if (index == 8) {
942                EXPECT_EQ((OffscreenBuffer*)0x800, *op.layerHandle);
943                EXPECT_EQ(Rect(800, 800), op.unmappedBounds); // outer layer
944            } else { ADD_FAILURE(); }
945        }
946        void recycleTemporaryLayer(OffscreenBuffer* offscreenBuffer) override {
947            const int index = mIndex++;
948            // order isn't important, but we need to see both
949            if (index == 10) {
950                EXPECT_EQ((OffscreenBuffer*)0x400, offscreenBuffer);
951            } else if (index == 11) {
952                EXPECT_EQ((OffscreenBuffer*)0x800, offscreenBuffer);
953            } else { ADD_FAILURE(); }
954        }
955    };
956
957    auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 800, 800,
958            [](RenderProperties& props, RecordingCanvas& canvas) {
959        canvas.saveLayerAlpha(0, 0, 800, 800, 128, SaveFlags::ClipToLayer);
960        {
961            canvas.drawRect(0, 0, 800, 800, SkPaint());
962            canvas.saveLayerAlpha(0, 0, 400, 400, 128, SaveFlags::ClipToLayer);
963            {
964                canvas.drawRect(0, 0, 400, 400, SkPaint());
965            }
966            canvas.restore();
967        }
968        canvas.restore();
969    });
970
971    FrameBuilder frameBuilder(SkRect::MakeWH(800, 800), 800, 800,
972            sLightGeometry, Caches::getInstance());
973    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
974
975    SaveLayerNestedTestRenderer renderer;
976    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
977    EXPECT_EQ(12, renderer.getIndex());
978}
979
980RENDERTHREAD_TEST(FrameBuilder, saveLayer_contentRejection) {
981        auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200,
982                [](RenderProperties& props, RecordingCanvas& canvas) {
983        canvas.save(SaveFlags::MatrixClip);
984        canvas.clipRect(200, 200, 400, 400, SkClipOp::kIntersect);
985        canvas.saveLayerAlpha(200, 200, 400, 400, 128, SaveFlags::ClipToLayer);
986
987        // draw within save layer may still be recorded, but shouldn't be drawn
988        canvas.drawRect(200, 200, 400, 400, SkPaint());
989
990        canvas.restore();
991        canvas.restore();
992    });
993
994    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
995            sLightGeometry, Caches::getInstance());
996    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
997
998    FailRenderer renderer;
999    // should see no ops, even within the layer, since the layer should be rejected
1000    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1001}
1002
1003RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_simple) {
1004    class SaveLayerUnclippedSimpleTestRenderer : public TestRendererBase {
1005    public:
1006        void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override {
1007            EXPECT_EQ(0, mIndex++);
1008            EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds);
1009            EXPECT_CLIP_RECT(Rect(200, 200), state.computedState.clipState);
1010            EXPECT_TRUE(state.computedState.transform.isIdentity());
1011        }
1012        void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override {
1013            EXPECT_EQ(1, mIndex++);
1014            ASSERT_NE(nullptr, op.paint);
1015            ASSERT_EQ(SkBlendMode::kClear, PaintUtils::getBlendModeDirect(op.paint));
1016        }
1017        void onRectOp(const RectOp& op, const BakedOpState& state) override {
1018            EXPECT_EQ(2, mIndex++);
1019            EXPECT_EQ(Rect(200, 200), op.unmappedBounds);
1020            EXPECT_EQ(Rect(200, 200), state.computedState.clippedBounds);
1021            EXPECT_EQ(Rect(200, 200), state.computedState.clipRect());
1022            EXPECT_TRUE(state.computedState.transform.isIdentity());
1023        }
1024        void onCopyFromLayerOp(const CopyFromLayerOp& op, const BakedOpState& state) override {
1025            EXPECT_EQ(3, mIndex++);
1026            EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds);
1027            EXPECT_CLIP_RECT(Rect(200, 200), state.computedState.clipState);
1028            EXPECT_TRUE(state.computedState.transform.isIdentity());
1029        }
1030    };
1031
1032    auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200,
1033            [](RenderProperties& props, RecordingCanvas& canvas) {
1034        canvas.saveLayerAlpha(10, 10, 190, 190, 128, (SaveFlags::Flags)(0));
1035        canvas.drawRect(0, 0, 200, 200, SkPaint());
1036        canvas.restore();
1037    });
1038
1039    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
1040            sLightGeometry, Caches::getInstance());
1041    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
1042
1043    SaveLayerUnclippedSimpleTestRenderer renderer;
1044    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1045    EXPECT_EQ(4, renderer.getIndex());
1046}
1047
1048RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_round) {
1049    class SaveLayerUnclippedRoundTestRenderer : public TestRendererBase {
1050    public:
1051        void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override {
1052            EXPECT_EQ(0, mIndex++);
1053            EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds)
1054                    << "Bounds rect should round out";
1055        }
1056        void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override {}
1057        void onRectOp(const RectOp& op, const BakedOpState& state) override {}
1058        void onCopyFromLayerOp(const CopyFromLayerOp& op, const BakedOpState& state) override {
1059            EXPECT_EQ(1, mIndex++);
1060            EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds)
1061                    << "Bounds rect should round out";
1062        }
1063    };
1064
1065    auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200,
1066            [](RenderProperties& props, RecordingCanvas& canvas) {
1067        canvas.saveLayerAlpha(10.95f, 10.5f, 189.75f, 189.25f, // values should all round out
1068                128, (SaveFlags::Flags)(0));
1069        canvas.drawRect(0, 0, 200, 200, SkPaint());
1070        canvas.restore();
1071    });
1072
1073    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
1074            sLightGeometry, Caches::getInstance());
1075    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
1076
1077    SaveLayerUnclippedRoundTestRenderer renderer;
1078    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1079    EXPECT_EQ(2, renderer.getIndex());
1080}
1081
1082RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_mergedClears) {
1083    class SaveLayerUnclippedMergedClearsTestRenderer : public TestRendererBase {
1084    public:
1085        void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override {
1086            int index = mIndex++;
1087            EXPECT_GT(4, index);
1088            EXPECT_EQ(5, op.unmappedBounds.getWidth());
1089            EXPECT_EQ(5, op.unmappedBounds.getHeight());
1090            if (index == 0) {
1091                EXPECT_EQ(Rect(10, 10), state.computedState.clippedBounds);
1092            } else if (index == 1) {
1093                EXPECT_EQ(Rect(190, 0, 200, 10), state.computedState.clippedBounds);
1094            } else if (index == 2) {
1095                EXPECT_EQ(Rect(0, 190, 10, 200), state.computedState.clippedBounds);
1096            } else if (index == 3) {
1097                EXPECT_EQ(Rect(190, 190, 200, 200), state.computedState.clippedBounds);
1098            }
1099        }
1100        void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override {
1101            EXPECT_EQ(4, mIndex++);
1102            ASSERT_EQ(op.vertexCount, 16u);
1103            for (size_t i = 0; i < op.vertexCount; i++) {
1104                auto v = op.vertices[i];
1105                EXPECT_TRUE(v.x == 0 || v.x == 10 || v.x == 190 || v.x == 200);
1106                EXPECT_TRUE(v.y == 0 || v.y == 10 || v.y == 190 || v.y == 200);
1107            }
1108        }
1109        void onRectOp(const RectOp& op, const BakedOpState& state) override {
1110            EXPECT_EQ(5, mIndex++);
1111        }
1112        void onCopyFromLayerOp(const CopyFromLayerOp& op, const BakedOpState& state) override {
1113            EXPECT_LT(5, mIndex++);
1114        }
1115    };
1116
1117    auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200,
1118            [](RenderProperties& props, RecordingCanvas& canvas) {
1119
1120        int restoreTo = canvas.save(SaveFlags::MatrixClip);
1121        canvas.scale(2, 2);
1122        canvas.saveLayerAlpha(0, 0, 5, 5, 128, SaveFlags::MatrixClip);
1123        canvas.saveLayerAlpha(95, 0, 100, 5, 128, SaveFlags::MatrixClip);
1124        canvas.saveLayerAlpha(0, 95, 5, 100, 128, SaveFlags::MatrixClip);
1125        canvas.saveLayerAlpha(95, 95, 100, 100, 128, SaveFlags::MatrixClip);
1126        canvas.drawRect(0, 0, 100, 100, SkPaint());
1127        canvas.restoreToCount(restoreTo);
1128    });
1129
1130    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
1131            sLightGeometry, Caches::getInstance());
1132    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
1133
1134    SaveLayerUnclippedMergedClearsTestRenderer renderer;
1135    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1136    EXPECT_EQ(10, renderer.getIndex())
1137            << "Expect 4 copyTos, 4 copyFroms, 1 clear SimpleRects, and 1 rect.";
1138}
1139
1140RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_clearClip) {
1141    class SaveLayerUnclippedClearClipTestRenderer : public TestRendererBase {
1142    public:
1143        void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override {
1144            EXPECT_EQ(0, mIndex++);
1145        }
1146        void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override {
1147            EXPECT_EQ(1, mIndex++);
1148            ASSERT_NE(nullptr, op.paint);
1149            EXPECT_EQ(SkBlendMode::kClear, PaintUtils::getBlendModeDirect(op.paint));
1150            EXPECT_EQ(Rect(50, 50, 150, 150), state.computedState.clippedBounds)
1151                    << "Expect dirty rect as clip";
1152            ASSERT_NE(nullptr, state.computedState.clipState);
1153            EXPECT_EQ(Rect(50, 50, 150, 150), state.computedState.clipState->rect);
1154            EXPECT_EQ(ClipMode::Rectangle, state.computedState.clipState->mode);
1155        }
1156        void onRectOp(const RectOp& op, const BakedOpState& state) override {
1157            EXPECT_EQ(2, mIndex++);
1158        }
1159        void onCopyFromLayerOp(const CopyFromLayerOp& op, const BakedOpState& state) override {
1160            EXPECT_EQ(3, mIndex++);
1161        }
1162    };
1163
1164    auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200,
1165            [](RenderProperties& props, RecordingCanvas& canvas) {
1166        // save smaller than clip, so we get unclipped behavior
1167        canvas.saveLayerAlpha(10, 10, 190, 190, 128, (SaveFlags::Flags)(0));
1168        canvas.drawRect(0, 0, 200, 200, SkPaint());
1169        canvas.restore();
1170    });
1171
1172    // draw with partial screen dirty, and assert we see that rect later
1173    FrameBuilder frameBuilder(SkRect::MakeLTRB(50, 50, 150, 150), 200, 200,
1174            sLightGeometry, Caches::getInstance());
1175    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
1176
1177    SaveLayerUnclippedClearClipTestRenderer renderer;
1178    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1179    EXPECT_EQ(4, renderer.getIndex());
1180}
1181
1182RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_reject) {
1183    auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200,
1184            [](RenderProperties& props, RecordingCanvas& canvas) {
1185        // unclipped savelayer + rect both in area that won't intersect with dirty
1186        canvas.saveLayerAlpha(100, 100, 200, 200, 128, (SaveFlags::Flags)(0));
1187        canvas.drawRect(100, 100, 200, 200, SkPaint());
1188        canvas.restore();
1189    });
1190
1191    // draw with partial screen dirty that doesn't intersect with savelayer
1192    FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 200, 200,
1193            sLightGeometry, Caches::getInstance());
1194    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
1195
1196    FailRenderer renderer;
1197    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1198}
1199
1200/* saveLayerUnclipped { saveLayer { saveLayerUnclipped { rect } } } will play back as:
1201 * - startTemporaryLayer, onCopyToLayer, onSimpleRects, onRect, onCopyFromLayer, endLayer
1202 * - startFrame, onCopyToLayer, onSimpleRects, drawLayer, onCopyFromLayer, endframe
1203 */
1204RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_complex) {
1205    class SaveLayerUnclippedComplexTestRenderer : public TestRendererBase {
1206    public:
1207        OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) {
1208            EXPECT_EQ(0, mIndex++); // savelayer first
1209            return (OffscreenBuffer*)0xabcd;
1210        }
1211        void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override {
1212            int index = mIndex++;
1213            EXPECT_TRUE(index == 1 || index == 7);
1214        }
1215        void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override {
1216            int index = mIndex++;
1217            EXPECT_TRUE(index == 2 || index == 8);
1218        }
1219        void onRectOp(const RectOp& op, const BakedOpState& state) override {
1220            EXPECT_EQ(3, mIndex++);
1221            Matrix4 expected;
1222            expected.loadTranslate(-100, -100, 0);
1223            EXPECT_EQ(Rect(100, 100, 200, 200), state.computedState.clippedBounds);
1224            EXPECT_MATRIX_APPROX_EQ(expected, state.computedState.transform);
1225        }
1226        void onCopyFromLayerOp(const CopyFromLayerOp& op, const BakedOpState& state) override {
1227            int index = mIndex++;
1228            EXPECT_TRUE(index == 4 || index == 10);
1229        }
1230        void endLayer() override {
1231            EXPECT_EQ(5, mIndex++);
1232        }
1233        void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
1234            EXPECT_EQ(6, mIndex++);
1235        }
1236        void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
1237            EXPECT_EQ(9, mIndex++);
1238            EXPECT_EQ((OffscreenBuffer*)0xabcd, *op.layerHandle);
1239        }
1240        void endFrame(const Rect& repaintRect) override {
1241            EXPECT_EQ(11, mIndex++);
1242        }
1243        void recycleTemporaryLayer(OffscreenBuffer* offscreenBuffer) override {
1244            EXPECT_EQ(12, mIndex++);
1245            EXPECT_EQ((OffscreenBuffer*)0xabcd, offscreenBuffer);
1246        }
1247    };
1248
1249    auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 600, 600, // 500x500 triggers clipping
1250            [](RenderProperties& props, RecordingCanvas& canvas) {
1251        canvas.saveLayerAlpha(0, 0, 500, 500, 128, (SaveFlags::Flags)0); // unclipped
1252        canvas.saveLayerAlpha(100, 100, 400, 400, 128, SaveFlags::ClipToLayer); // clipped
1253        canvas.saveLayerAlpha(200, 200, 300, 300, 128, (SaveFlags::Flags)0); // unclipped
1254        canvas.drawRect(200, 200, 300, 300, SkPaint());
1255        canvas.restore();
1256        canvas.restore();
1257        canvas.restore();
1258    });
1259
1260    FrameBuilder frameBuilder(SkRect::MakeWH(600, 600), 600, 600,
1261            sLightGeometry, Caches::getInstance());
1262    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
1263
1264    SaveLayerUnclippedComplexTestRenderer renderer;
1265    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1266    EXPECT_EQ(13, renderer.getIndex());
1267}
1268
1269RENDERTHREAD_TEST(FrameBuilder, hwLayer_simple) {
1270    class HwLayerSimpleTestRenderer : public TestRendererBase {
1271    public:
1272        void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override {
1273            EXPECT_EQ(0, mIndex++);
1274            EXPECT_EQ(100u, offscreenBuffer->viewportWidth);
1275            EXPECT_EQ(100u, offscreenBuffer->viewportHeight);
1276            EXPECT_EQ(Rect(25, 25, 75, 75), repaintRect);
1277        }
1278        void onRectOp(const RectOp& op, const BakedOpState& state) override {
1279            EXPECT_EQ(1, mIndex++);
1280
1281            EXPECT_TRUE(state.computedState.transform.isIdentity())
1282                    << "Transform should be reset within layer";
1283
1284            EXPECT_EQ(Rect(25, 25, 75, 75), state.computedState.clipRect())
1285                    << "Damage rect should be used to clip layer content";
1286        }
1287        void endLayer() override {
1288            EXPECT_EQ(2, mIndex++);
1289        }
1290        void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
1291            EXPECT_EQ(3, mIndex++);
1292        }
1293        void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
1294            EXPECT_EQ(4, mIndex++);
1295        }
1296        void endFrame(const Rect& repaintRect) override {
1297            EXPECT_EQ(5, mIndex++);
1298        }
1299    };
1300
1301    auto node = TestUtils::createNode<RecordingCanvas>(10, 10, 110, 110,
1302            [](RenderProperties& props, RecordingCanvas& canvas) {
1303        props.mutateLayerProperties().setType(LayerType::RenderLayer);
1304        SkPaint paint;
1305        paint.setColor(SK_ColorWHITE);
1306        canvas.drawRect(0, 0, 100, 100, paint);
1307    });
1308    OffscreenBuffer** layerHandle = node->getLayerHandle();
1309
1310    // create RenderNode's layer here in same way prepareTree would
1311    OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 100, 100);
1312    *layerHandle = &layer;
1313
1314    auto syncedNode = TestUtils::getSyncedNode(node);
1315
1316    // only enqueue partial damage
1317    LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
1318    layerUpdateQueue.enqueueLayerWithDamage(node.get(), Rect(25, 25, 75, 75));
1319
1320    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
1321            sLightGeometry, Caches::getInstance());
1322    frameBuilder.deferLayers(layerUpdateQueue);
1323    frameBuilder.deferRenderNode(*syncedNode);
1324
1325    HwLayerSimpleTestRenderer renderer;
1326    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1327    EXPECT_EQ(6, renderer.getIndex());
1328
1329    // clean up layer pointer, so we can safely destruct RenderNode
1330    *layerHandle = nullptr;
1331}
1332
1333RENDERTHREAD_TEST(FrameBuilder, hwLayer_complex) {
1334    /* parentLayer { greyRect, saveLayer { childLayer { whiteRect } } } will play back as:
1335     * - startRepaintLayer(child), rect(grey), endLayer
1336     * - startTemporaryLayer, drawLayer(child), endLayer
1337     * - startRepaintLayer(parent), rect(white), drawLayer(saveLayer), endLayer
1338     * - startFrame, drawLayer(parent), endLayerb
1339     */
1340    class HwLayerComplexTestRenderer : public TestRendererBase {
1341    public:
1342        OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) {
1343            EXPECT_EQ(3, mIndex++); // savelayer first
1344            return (OffscreenBuffer*)0xabcd;
1345        }
1346        void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override {
1347            int index = mIndex++;
1348            if (index == 0) {
1349                // starting inner layer
1350                EXPECT_EQ(100u, offscreenBuffer->viewportWidth);
1351                EXPECT_EQ(100u, offscreenBuffer->viewportHeight);
1352            } else if (index == 6) {
1353                // starting outer layer
1354                EXPECT_EQ(200u, offscreenBuffer->viewportWidth);
1355                EXPECT_EQ(200u, offscreenBuffer->viewportHeight);
1356            } else { ADD_FAILURE(); }
1357        }
1358        void onRectOp(const RectOp& op, const BakedOpState& state) override {
1359            int index = mIndex++;
1360            if (index == 1) {
1361                // inner layer's rect (white)
1362                EXPECT_EQ(SK_ColorWHITE, op.paint->getColor());
1363            } else if (index == 7) {
1364                // outer layer's rect (grey)
1365                EXPECT_EQ(SK_ColorDKGRAY, op.paint->getColor());
1366            } else { ADD_FAILURE(); }
1367        }
1368        void endLayer() override {
1369            int index = mIndex++;
1370            EXPECT_TRUE(index == 2 || index == 5 || index == 9);
1371        }
1372        void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
1373            EXPECT_EQ(10, mIndex++);
1374        }
1375        void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
1376            OffscreenBuffer* layer = *op.layerHandle;
1377            int index = mIndex++;
1378            if (index == 4) {
1379                EXPECT_EQ(100u, layer->viewportWidth);
1380                EXPECT_EQ(100u, layer->viewportHeight);
1381            } else if (index == 8) {
1382                EXPECT_EQ((OffscreenBuffer*)0xabcd, *op.layerHandle);
1383            } else if (index == 11) {
1384                EXPECT_EQ(200u, layer->viewportWidth);
1385                EXPECT_EQ(200u, layer->viewportHeight);
1386            } else { ADD_FAILURE(); }
1387        }
1388        void endFrame(const Rect& repaintRect) override {
1389            EXPECT_EQ(12, mIndex++);
1390        }
1391        void recycleTemporaryLayer(OffscreenBuffer* offscreenBuffer) override {
1392            EXPECT_EQ(13, mIndex++);
1393        }
1394    };
1395
1396    auto child = TestUtils::createNode<RecordingCanvas>(50, 50, 150, 150,
1397            [](RenderProperties& props, RecordingCanvas& canvas) {
1398        props.mutateLayerProperties().setType(LayerType::RenderLayer);
1399        SkPaint paint;
1400        paint.setColor(SK_ColorWHITE);
1401        canvas.drawRect(0, 0, 100, 100, paint);
1402    });
1403    OffscreenBuffer childLayer(renderThread.renderState(), Caches::getInstance(), 100, 100);
1404    *(child->getLayerHandle()) = &childLayer;
1405
1406    RenderNode* childPtr = child.get();
1407    auto parent = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200,
1408            [childPtr](RenderProperties& props, RecordingCanvas& canvas) {
1409        props.mutateLayerProperties().setType(LayerType::RenderLayer);
1410        SkPaint paint;
1411        paint.setColor(SK_ColorDKGRAY);
1412        canvas.drawRect(0, 0, 200, 200, paint);
1413
1414        canvas.saveLayerAlpha(50, 50, 150, 150, 128, SaveFlags::ClipToLayer);
1415        canvas.drawRenderNode(childPtr);
1416        canvas.restore();
1417    });
1418    OffscreenBuffer parentLayer(renderThread.renderState(), Caches::getInstance(), 200, 200);
1419    *(parent->getLayerHandle()) = &parentLayer;
1420
1421    auto syncedNode = TestUtils::getSyncedNode(parent);
1422
1423    LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
1424    layerUpdateQueue.enqueueLayerWithDamage(child.get(), Rect(100, 100));
1425    layerUpdateQueue.enqueueLayerWithDamage(parent.get(), Rect(200, 200));
1426
1427    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
1428            sLightGeometry, Caches::getInstance());
1429    frameBuilder.deferLayers(layerUpdateQueue);
1430    frameBuilder.deferRenderNode(*syncedNode);
1431
1432    HwLayerComplexTestRenderer renderer;
1433    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1434    EXPECT_EQ(14, renderer.getIndex());
1435
1436    // clean up layer pointers, so we can safely destruct RenderNodes
1437    *(child->getLayerHandle()) = nullptr;
1438    *(parent->getLayerHandle()) = nullptr;
1439}
1440
1441
1442RENDERTHREAD_TEST(FrameBuilder, buildLayer) {
1443    class BuildLayerTestRenderer : public TestRendererBase {
1444    public:
1445        void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override {
1446            EXPECT_EQ(0, mIndex++);
1447            EXPECT_EQ(100u, offscreenBuffer->viewportWidth);
1448            EXPECT_EQ(100u, offscreenBuffer->viewportHeight);
1449            EXPECT_EQ(Rect(25, 25, 75, 75), repaintRect);
1450        }
1451        void onColorOp(const ColorOp& op, const BakedOpState& state) override {
1452            EXPECT_EQ(1, mIndex++);
1453
1454            EXPECT_TRUE(state.computedState.transform.isIdentity())
1455                    << "Transform should be reset within layer";
1456
1457            EXPECT_EQ(Rect(25, 25, 75, 75), state.computedState.clipRect())
1458                    << "Damage rect should be used to clip layer content";
1459        }
1460        void endLayer() override {
1461            EXPECT_EQ(2, mIndex++);
1462        }
1463        void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
1464            ADD_FAILURE() << "Primary frame draw not expected in this test";
1465        }
1466        void endFrame(const Rect& repaintRect) override {
1467            ADD_FAILURE() << "Primary frame draw not expected in this test";
1468        }
1469    };
1470
1471    auto node = TestUtils::createNode<RecordingCanvas>(10, 10, 110, 110,
1472            [](RenderProperties& props, RecordingCanvas& canvas) {
1473        props.mutateLayerProperties().setType(LayerType::RenderLayer);
1474        canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver);
1475    });
1476    OffscreenBuffer** layerHandle = node->getLayerHandle();
1477
1478    // create RenderNode's layer here in same way prepareTree would
1479    OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 100, 100);
1480    *layerHandle = &layer;
1481
1482    TestUtils::syncHierarchyPropertiesAndDisplayList(node);
1483
1484    // only enqueue partial damage
1485    LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
1486    layerUpdateQueue.enqueueLayerWithDamage(node.get(), Rect(25, 25, 75, 75));
1487
1488    // Draw, but pass empty node list, so no work is done for primary frame
1489    FrameBuilder frameBuilder(layerUpdateQueue, sLightGeometry, Caches::getInstance());
1490    BuildLayerTestRenderer renderer;
1491    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1492    EXPECT_EQ(3, renderer.getIndex());
1493
1494    // clean up layer pointer, so we can safely destruct RenderNode
1495    *layerHandle = nullptr;
1496}
1497
1498namespace {
1499
1500static void drawOrderedRect(Canvas* canvas, uint8_t expectedDrawOrder) {
1501    SkPaint paint;
1502    // order put in blue channel, transparent so overlapped content doesn't get rejected
1503    paint.setColor(SkColorSetARGB(1, 0, 0, expectedDrawOrder));
1504    canvas->drawRect(0, 0, 100, 100, paint);
1505}
1506static void drawOrderedNode(Canvas* canvas, uint8_t expectedDrawOrder, float z) {
1507    auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100,
1508            [expectedDrawOrder](RenderProperties& props, RecordingCanvas& canvas) {
1509        drawOrderedRect(&canvas, expectedDrawOrder);
1510    });
1511    node->mutateStagingProperties().setTranslationZ(z);
1512    node->setPropertyFieldsDirty(RenderNode::TRANSLATION_Z);
1513    canvas->drawRenderNode(node.get()); // canvas takes reference/sole ownership
1514}
1515
1516static void drawOrderedNode(Canvas* canvas, uint8_t expectedDrawOrder,
1517        std::function<void(RenderProperties& props, RecordingCanvas& canvas)> setup) {
1518    auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100,
1519            [expectedDrawOrder, setup](RenderProperties& props, RecordingCanvas& canvas) {
1520        drawOrderedRect(&canvas, expectedDrawOrder);
1521        if (setup) {
1522             setup(props, canvas);
1523        }
1524    });
1525    canvas->drawRenderNode(node.get()); // canvas takes reference/sole ownership
1526}
1527
1528class ZReorderTestRenderer : public TestRendererBase {
1529public:
1530    void onRectOp(const RectOp& op, const BakedOpState& state) override {
1531        int expectedOrder = SkColorGetB(op.paint->getColor()); // extract order from blue channel
1532        EXPECT_EQ(expectedOrder, mIndex++) << "An op was drawn out of order";
1533    }
1534};
1535
1536} // end anonymous namespace
1537
1538RENDERTHREAD_TEST(FrameBuilder, zReorder) {
1539    auto parent = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100,
1540            [](RenderProperties& props, RecordingCanvas& canvas) {
1541        canvas.insertReorderBarrier(true);
1542        canvas.insertReorderBarrier(false);
1543        drawOrderedNode(&canvas, 0, 10.0f); // in reorder=false at this point, so played inorder
1544        drawOrderedRect(&canvas, 1);
1545        canvas.insertReorderBarrier(true);
1546        drawOrderedNode(&canvas, 6, 2.0f);
1547        drawOrderedRect(&canvas, 3);
1548        drawOrderedNode(&canvas, 4, 0.0f);
1549        drawOrderedRect(&canvas, 5);
1550        drawOrderedNode(&canvas, 2, -2.0f);
1551        drawOrderedNode(&canvas, 7, 2.0f);
1552        canvas.insertReorderBarrier(false);
1553        drawOrderedRect(&canvas, 8);
1554        drawOrderedNode(&canvas, 9, -10.0f); // in reorder=false at this point, so played inorder
1555        canvas.insertReorderBarrier(true); //reorder a node ahead of drawrect op
1556        drawOrderedRect(&canvas, 11);
1557        drawOrderedNode(&canvas, 10, -1.0f);
1558        canvas.insertReorderBarrier(false);
1559        canvas.insertReorderBarrier(true); //test with two empty reorder sections
1560        canvas.insertReorderBarrier(true);
1561        canvas.insertReorderBarrier(false);
1562        drawOrderedRect(&canvas, 12);
1563    });
1564    FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100,
1565            sLightGeometry, Caches::getInstance());
1566    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent));
1567
1568    ZReorderTestRenderer renderer;
1569    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1570    EXPECT_EQ(13, renderer.getIndex());
1571};
1572
1573RENDERTHREAD_TEST(FrameBuilder, projectionReorder) {
1574    static const int scrollX = 5;
1575    static const int scrollY = 10;
1576    class ProjectionReorderTestRenderer : public TestRendererBase {
1577    public:
1578        void onRectOp(const RectOp& op, const BakedOpState& state) override {
1579            const int index = mIndex++;
1580
1581            Matrix4 expectedMatrix;
1582            switch (index) {
1583            case 0:
1584                EXPECT_EQ(Rect(100, 100), op.unmappedBounds);
1585                EXPECT_EQ(SK_ColorWHITE, op.paint->getColor());
1586                expectedMatrix.loadIdentity();
1587                EXPECT_EQ(nullptr, state.computedState.localProjectionPathMask);
1588                break;
1589            case 1:
1590                EXPECT_EQ(Rect(-10, -10, 60, 60), op.unmappedBounds);
1591                EXPECT_EQ(SK_ColorDKGRAY, op.paint->getColor());
1592                expectedMatrix.loadTranslate(50 - scrollX, 50 - scrollY, 0);
1593                ASSERT_NE(nullptr, state.computedState.localProjectionPathMask);
1594                EXPECT_EQ(Rect(-35, -30, 45, 50),
1595                        Rect(state.computedState.localProjectionPathMask->getBounds()));
1596                break;
1597            case 2:
1598                EXPECT_EQ(Rect(100, 50), op.unmappedBounds);
1599                EXPECT_EQ(SK_ColorBLUE, op.paint->getColor());
1600                expectedMatrix.loadTranslate(-scrollX, 50 - scrollY, 0);
1601                EXPECT_EQ(nullptr, state.computedState.localProjectionPathMask);
1602                break;
1603            default:
1604                ADD_FAILURE();
1605            }
1606            EXPECT_EQ(expectedMatrix, state.computedState.transform);
1607        }
1608    };
1609
1610    /**
1611     * Construct a tree of nodes, where the root (A) has a receiver background (B), and a child (C)
1612     * with a projecting child (P) of its own. P would normally draw between B and C's "background"
1613     * draw, but because it is projected backwards, it's drawn in between B and C.
1614     *
1615     * The parent is scrolled by scrollX/scrollY, but this does not affect the background
1616     * (which isn't affected by scroll).
1617     */
1618    auto receiverBackground = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100,
1619            [](RenderProperties& properties, RecordingCanvas& canvas) {
1620        properties.setProjectionReceiver(true);
1621        // scroll doesn't apply to background, so undone via translationX/Y
1622        // NOTE: translationX/Y only! no other transform properties may be set for a proj receiver!
1623        properties.setTranslationX(scrollX);
1624        properties.setTranslationY(scrollY);
1625
1626        SkPaint paint;
1627        paint.setColor(SK_ColorWHITE);
1628        canvas.drawRect(0, 0, 100, 100, paint);
1629    });
1630    auto projectingRipple = TestUtils::createNode<RecordingCanvas>(50, 0, 100, 50,
1631            [](RenderProperties& properties, RecordingCanvas& canvas) {
1632        properties.setProjectBackwards(true);
1633        properties.setClipToBounds(false);
1634        SkPaint paint;
1635        paint.setColor(SK_ColorDKGRAY);
1636        canvas.drawRect(-10, -10, 60, 60, paint);
1637    });
1638    auto child = TestUtils::createNode<RecordingCanvas>(0, 50, 100, 100,
1639            [&projectingRipple](RenderProperties& properties, RecordingCanvas& canvas) {
1640        SkPaint paint;
1641        paint.setColor(SK_ColorBLUE);
1642        canvas.drawRect(0, 0, 100, 50, paint);
1643        canvas.drawRenderNode(projectingRipple.get());
1644    });
1645    auto parent = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100,
1646            [&receiverBackground, &child](RenderProperties& properties, RecordingCanvas& canvas) {
1647        // Set a rect outline for the projecting ripple to be masked against.
1648        properties.mutableOutline().setRoundRect(10, 10, 90, 90, 5, 1.0f);
1649
1650        canvas.save(SaveFlags::MatrixClip);
1651        canvas.translate(-scrollX, -scrollY); // Apply scroll (note: bg undoes this internally)
1652        canvas.drawRenderNode(receiverBackground.get());
1653        canvas.drawRenderNode(child.get());
1654        canvas.restore();
1655    });
1656
1657    FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100,
1658            sLightGeometry, Caches::getInstance());
1659    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent));
1660
1661    ProjectionReorderTestRenderer renderer;
1662    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1663    EXPECT_EQ(3, renderer.getIndex());
1664}
1665
1666RENDERTHREAD_TEST(FrameBuilder, projectionHwLayer) {
1667    static const int scrollX = 5;
1668    static const int scrollY = 10;
1669    class ProjectionHwLayerTestRenderer : public TestRendererBase {
1670    public:
1671        void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override {
1672            EXPECT_EQ(0, mIndex++);
1673        }
1674        void onArcOp(const ArcOp& op, const BakedOpState& state) override {
1675            EXPECT_EQ(1, mIndex++);
1676            ASSERT_EQ(nullptr, state.computedState.localProjectionPathMask);
1677        }
1678        void endLayer() override {
1679            EXPECT_EQ(2, mIndex++);
1680        }
1681        void onRectOp(const RectOp& op, const BakedOpState& state) override {
1682            EXPECT_EQ(3, mIndex++);
1683            ASSERT_EQ(nullptr, state.computedState.localProjectionPathMask);
1684        }
1685        void onOvalOp(const OvalOp& op, const BakedOpState& state) override {
1686            EXPECT_EQ(4, mIndex++);
1687            ASSERT_NE(nullptr, state.computedState.localProjectionPathMask);
1688            Matrix4 expected;
1689            expected.loadTranslate(100 - scrollX, 100 - scrollY, 0);
1690            EXPECT_EQ(expected, state.computedState.transform);
1691            EXPECT_EQ(Rect(-85, -80, 295, 300),
1692                    Rect(state.computedState.localProjectionPathMask->getBounds()));
1693        }
1694        void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
1695            EXPECT_EQ(5, mIndex++);
1696            ASSERT_EQ(nullptr, state.computedState.localProjectionPathMask);
1697        }
1698    };
1699    auto receiverBackground = TestUtils::createNode<RecordingCanvas>(0, 0, 400, 400,
1700            [](RenderProperties& properties, RecordingCanvas& canvas) {
1701        properties.setProjectionReceiver(true);
1702        // scroll doesn't apply to background, so undone via translationX/Y
1703        // NOTE: translationX/Y only! no other transform properties may be set for a proj receiver!
1704        properties.setTranslationX(scrollX);
1705        properties.setTranslationY(scrollY);
1706
1707        canvas.drawRect(0, 0, 400, 400, SkPaint());
1708    });
1709    auto projectingRipple = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200,
1710            [](RenderProperties& properties, RecordingCanvas& canvas) {
1711        properties.setProjectBackwards(true);
1712        properties.setClipToBounds(false);
1713        canvas.drawOval(100, 100, 300, 300, SkPaint()); // drawn mostly out of layer bounds
1714    });
1715    auto child = TestUtils::createNode<RecordingCanvas>(100, 100, 300, 300,
1716            [&projectingRipple](RenderProperties& properties, RecordingCanvas& canvas) {
1717        properties.mutateLayerProperties().setType(LayerType::RenderLayer);
1718        canvas.drawRenderNode(projectingRipple.get());
1719        canvas.drawArc(0, 0, 200, 200, 0.0f, 280.0f, true, SkPaint());
1720    });
1721    auto parent = TestUtils::createNode<RecordingCanvas>(0, 0, 400, 400,
1722            [&receiverBackground, &child](RenderProperties& properties, RecordingCanvas& canvas) {
1723        // Set a rect outline for the projecting ripple to be masked against.
1724        properties.mutableOutline().setRoundRect(10, 10, 390, 390, 0, 1.0f);
1725        canvas.translate(-scrollX, -scrollY); // Apply scroll (note: bg undoes this internally)
1726        canvas.drawRenderNode(receiverBackground.get());
1727        canvas.drawRenderNode(child.get());
1728    });
1729
1730    OffscreenBuffer** layerHandle = child->getLayerHandle();
1731
1732    // create RenderNode's layer here in same way prepareTree would, setting windowTransform
1733    OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 200, 200);
1734    Matrix4 windowTransform;
1735    windowTransform.loadTranslate(100, 100, 0); // total transform of layer's origin
1736    layer.setWindowTransform(windowTransform);
1737    *layerHandle = &layer;
1738
1739    auto syncedNode = TestUtils::getSyncedNode(parent);
1740
1741    LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
1742    layerUpdateQueue.enqueueLayerWithDamage(child.get(), Rect(200, 200));
1743
1744    FrameBuilder frameBuilder(SkRect::MakeWH(400, 400), 400, 400,
1745            sLightGeometry, Caches::getInstance());
1746    frameBuilder.deferLayers(layerUpdateQueue);
1747    frameBuilder.deferRenderNode(*syncedNode);
1748
1749    ProjectionHwLayerTestRenderer renderer;
1750    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1751    EXPECT_EQ(6, renderer.getIndex());
1752
1753    // clean up layer pointer, so we can safely destruct RenderNode
1754    *layerHandle = nullptr;
1755}
1756
1757RENDERTHREAD_TEST(FrameBuilder, projectionChildScroll) {
1758    static const int scrollX = 500000;
1759    static const int scrollY = 0;
1760    class ProjectionChildScrollTestRenderer : public TestRendererBase {
1761    public:
1762        void onRectOp(const RectOp& op, const BakedOpState& state) override {
1763            EXPECT_EQ(0, mIndex++);
1764            EXPECT_TRUE(state.computedState.transform.isIdentity());
1765        }
1766        void onOvalOp(const OvalOp& op, const BakedOpState& state) override {
1767            EXPECT_EQ(1, mIndex++);
1768            ASSERT_NE(nullptr, state.computedState.clipState);
1769            ASSERT_EQ(ClipMode::Rectangle, state.computedState.clipState->mode);
1770            ASSERT_EQ(Rect(400, 400), state.computedState.clipState->rect);
1771            EXPECT_TRUE(state.computedState.transform.isIdentity());
1772        }
1773    };
1774    auto receiverBackground = TestUtils::createNode<RecordingCanvas>(0, 0, 400, 400,
1775            [](RenderProperties& properties, RecordingCanvas& canvas) {
1776        properties.setProjectionReceiver(true);
1777        canvas.drawRect(0, 0, 400, 400, SkPaint());
1778    });
1779    auto projectingRipple = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200,
1780            [](RenderProperties& properties, RecordingCanvas& canvas) {
1781        // scroll doesn't apply to background, so undone via translationX/Y
1782        // NOTE: translationX/Y only! no other transform properties may be set for a proj receiver!
1783        properties.setTranslationX(scrollX);
1784        properties.setTranslationY(scrollY);
1785        properties.setProjectBackwards(true);
1786        properties.setClipToBounds(false);
1787        canvas.drawOval(0, 0, 200, 200, SkPaint());
1788    });
1789    auto child = TestUtils::createNode<RecordingCanvas>(0, 0, 400, 400,
1790            [&projectingRipple](RenderProperties& properties, RecordingCanvas& canvas) {
1791        // Record time clip will be ignored by projectee
1792        canvas.clipRect(100, 100, 300, 300, SkClipOp::kIntersect);
1793
1794        canvas.translate(-scrollX, -scrollY); // Apply scroll (note: bg undoes this internally)
1795        canvas.drawRenderNode(projectingRipple.get());
1796    });
1797    auto parent = TestUtils::createNode<RecordingCanvas>(0, 0, 400, 400,
1798            [&receiverBackground, &child](RenderProperties& properties, RecordingCanvas& canvas) {
1799        canvas.drawRenderNode(receiverBackground.get());
1800        canvas.drawRenderNode(child.get());
1801    });
1802
1803    FrameBuilder frameBuilder(SkRect::MakeWH(400, 400), 400, 400,
1804            sLightGeometry, Caches::getInstance());
1805    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent));
1806
1807    ProjectionChildScrollTestRenderer renderer;
1808    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1809    EXPECT_EQ(2, renderer.getIndex());
1810}
1811
1812// creates a 100x100 shadow casting node with provided translationZ
1813static sp<RenderNode> createWhiteRectShadowCaster(float translationZ) {
1814    return TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100,
1815            [translationZ](RenderProperties& properties, RecordingCanvas& canvas) {
1816        properties.setTranslationZ(translationZ);
1817        properties.mutableOutline().setRoundRect(0, 0, 100, 100, 0.0f, 1.0f);
1818        SkPaint paint;
1819        paint.setColor(SK_ColorWHITE);
1820        canvas.drawRect(0, 0, 100, 100, paint);
1821    });
1822}
1823
1824RENDERTHREAD_TEST(FrameBuilder, shadow) {
1825    class ShadowTestRenderer : public TestRendererBase {
1826    public:
1827        void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
1828            EXPECT_EQ(0, mIndex++);
1829            EXPECT_FLOAT_EQ(1.0f, op.casterAlpha);
1830            EXPECT_TRUE(op.shadowTask->casterPerimeter.isRect(nullptr));
1831            EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), op.shadowTask->transformXY);
1832
1833            Matrix4 expectedZ;
1834            expectedZ.loadTranslate(0, 0, 5);
1835            EXPECT_MATRIX_APPROX_EQ(expectedZ, op.shadowTask->transformZ);
1836        }
1837        void onRectOp(const RectOp& op, const BakedOpState& state) override {
1838            EXPECT_EQ(1, mIndex++);
1839        }
1840    };
1841
1842    auto parent = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200,
1843            [](RenderProperties& props, RecordingCanvas& canvas) {
1844        canvas.insertReorderBarrier(true);
1845        canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
1846    });
1847
1848    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
1849            sLightGeometry, Caches::getInstance());
1850    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent));
1851
1852    ShadowTestRenderer renderer;
1853    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1854    EXPECT_EQ(2, renderer.getIndex());
1855}
1856
1857RENDERTHREAD_TEST(FrameBuilder, shadowSaveLayer) {
1858    class ShadowSaveLayerTestRenderer : public TestRendererBase {
1859    public:
1860        OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override {
1861            EXPECT_EQ(0, mIndex++);
1862            return nullptr;
1863        }
1864        void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
1865            EXPECT_EQ(1, mIndex++);
1866            EXPECT_FLOAT_EQ(50, op.shadowTask->lightCenter.x);
1867            EXPECT_FLOAT_EQ(40, op.shadowTask->lightCenter.y);
1868        }
1869        void onRectOp(const RectOp& op, const BakedOpState& state) override {
1870            EXPECT_EQ(2, mIndex++);
1871        }
1872        void endLayer() override {
1873            EXPECT_EQ(3, mIndex++);
1874        }
1875        void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
1876            EXPECT_EQ(4, mIndex++);
1877        }
1878        void recycleTemporaryLayer(OffscreenBuffer* offscreenBuffer) override {
1879            EXPECT_EQ(5, mIndex++);
1880        }
1881    };
1882
1883    auto parent = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200,
1884            [](RenderProperties& props, RecordingCanvas& canvas) {
1885        // save/restore outside of reorderBarrier, so they don't get moved out of place
1886        canvas.translate(20, 10);
1887        int count = canvas.saveLayerAlpha(30, 50, 130, 150, 128, SaveFlags::ClipToLayer);
1888        canvas.insertReorderBarrier(true);
1889        canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
1890        canvas.insertReorderBarrier(false);
1891        canvas.restoreToCount(count);
1892    });
1893
1894    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
1895            (FrameBuilder::LightGeometry) {{ 100, 100, 100 }, 50}, Caches::getInstance());
1896    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent));
1897
1898    ShadowSaveLayerTestRenderer renderer;
1899    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1900    EXPECT_EQ(6, renderer.getIndex());
1901}
1902
1903RENDERTHREAD_TEST(FrameBuilder, shadowHwLayer) {
1904    class ShadowHwLayerTestRenderer : public TestRendererBase {
1905    public:
1906        void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override {
1907            EXPECT_EQ(0, mIndex++);
1908        }
1909        void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
1910            EXPECT_EQ(1, mIndex++);
1911            EXPECT_FLOAT_EQ(50, op.shadowTask->lightCenter.x);
1912            EXPECT_FLOAT_EQ(40, op.shadowTask->lightCenter.y);
1913            EXPECT_FLOAT_EQ(30, op.shadowTask->lightRadius);
1914        }
1915        void onRectOp(const RectOp& op, const BakedOpState& state) override {
1916            EXPECT_EQ(2, mIndex++);
1917        }
1918        void endLayer() override {
1919            EXPECT_EQ(3, mIndex++);
1920        }
1921        void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
1922            EXPECT_EQ(4, mIndex++);
1923        }
1924    };
1925
1926    auto parent = TestUtils::createNode<RecordingCanvas>(50, 60, 150, 160,
1927            [](RenderProperties& props, RecordingCanvas& canvas) {
1928        props.mutateLayerProperties().setType(LayerType::RenderLayer);
1929        canvas.insertReorderBarrier(true);
1930        canvas.save(SaveFlags::MatrixClip);
1931        canvas.translate(20, 10);
1932        canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
1933        canvas.restore();
1934    });
1935    OffscreenBuffer** layerHandle = parent->getLayerHandle();
1936
1937    // create RenderNode's layer here in same way prepareTree would, setting windowTransform
1938    OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 100, 100);
1939    Matrix4 windowTransform;
1940    windowTransform.loadTranslate(50, 60, 0); // total transform of layer's origin
1941    layer.setWindowTransform(windowTransform);
1942    *layerHandle = &layer;
1943
1944    auto syncedNode = TestUtils::getSyncedNode(parent);
1945    LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
1946    layerUpdateQueue.enqueueLayerWithDamage(parent.get(), Rect(100, 100));
1947
1948    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
1949            (FrameBuilder::LightGeometry) {{ 100, 100, 100 }, 30}, Caches::getInstance());
1950    frameBuilder.deferLayers(layerUpdateQueue);
1951    frameBuilder.deferRenderNode(*syncedNode);
1952
1953    ShadowHwLayerTestRenderer renderer;
1954    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1955    EXPECT_EQ(5, renderer.getIndex());
1956
1957    // clean up layer pointer, so we can safely destruct RenderNode
1958    *layerHandle = nullptr;
1959}
1960
1961RENDERTHREAD_TEST(FrameBuilder, shadowLayering) {
1962    class ShadowLayeringTestRenderer : public TestRendererBase {
1963    public:
1964        void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
1965            int index = mIndex++;
1966            EXPECT_TRUE(index == 0 || index == 1);
1967        }
1968        void onRectOp(const RectOp& op, const BakedOpState& state) override {
1969            int index = mIndex++;
1970            EXPECT_TRUE(index == 2 || index == 3);
1971        }
1972    };
1973    auto parent = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200,
1974            [](RenderProperties& props, RecordingCanvas& canvas) {
1975        canvas.insertReorderBarrier(true);
1976        canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
1977        canvas.drawRenderNode(createWhiteRectShadowCaster(5.0001f).get());
1978    });
1979    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
1980            (FrameBuilder::LightGeometry) {{ 100, 100, 100 }, 50}, Caches::getInstance());
1981    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent));
1982
1983    ShadowLayeringTestRenderer renderer;
1984    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1985    EXPECT_EQ(4, renderer.getIndex());
1986}
1987
1988RENDERTHREAD_TEST(FrameBuilder, shadowClipping) {
1989    class ShadowClippingTestRenderer : public TestRendererBase {
1990    public:
1991        void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
1992            EXPECT_EQ(0, mIndex++);
1993            EXPECT_EQ(Rect(25, 25, 75, 75), state.computedState.clipState->rect)
1994                    << "Shadow must respect pre-barrier canvas clip value.";
1995        }
1996        void onRectOp(const RectOp& op, const BakedOpState& state) override {
1997            EXPECT_EQ(1, mIndex++);
1998        }
1999    };
2000    auto parent = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100,
2001            [](RenderProperties& props, RecordingCanvas& canvas) {
2002        // Apply a clip before the reorder barrier/shadow casting child is drawn.
2003        // This clip must be applied to the shadow cast by the child.
2004        canvas.clipRect(25, 25, 75, 75, SkClipOp::kIntersect);
2005        canvas.insertReorderBarrier(true);
2006        canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
2007    });
2008
2009    FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100,
2010            (FrameBuilder::LightGeometry) {{ 100, 100, 100 }, 50}, Caches::getInstance());
2011    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent));
2012
2013    ShadowClippingTestRenderer renderer;
2014    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
2015    EXPECT_EQ(2, renderer.getIndex());
2016}
2017
2018static void testProperty(std::function<void(RenderProperties&)> propSetupCallback,
2019        std::function<void(const RectOp&, const BakedOpState&)> opValidateCallback) {
2020    class PropertyTestRenderer : public TestRendererBase {
2021    public:
2022        explicit PropertyTestRenderer(std::function<void(const RectOp&, const BakedOpState&)> callback)
2023                : mCallback(callback) {}
2024        void onRectOp(const RectOp& op, const BakedOpState& state) override {
2025            EXPECT_EQ(mIndex++, 0);
2026            mCallback(op, state);
2027        }
2028        std::function<void(const RectOp&, const BakedOpState&)> mCallback;
2029    };
2030
2031    auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100,
2032            [propSetupCallback](RenderProperties& props, RecordingCanvas& canvas) {
2033        propSetupCallback(props);
2034        SkPaint paint;
2035        paint.setColor(SK_ColorWHITE);
2036        canvas.drawRect(0, 0, 100, 100, paint);
2037    });
2038
2039    FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 200, 200,
2040            sLightGeometry, Caches::getInstance());
2041    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
2042
2043    PropertyTestRenderer renderer(opValidateCallback);
2044    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
2045    EXPECT_EQ(1, renderer.getIndex()) << "Should have seen one op";
2046}
2047
2048RENDERTHREAD_TEST(FrameBuilder, renderPropOverlappingRenderingAlpha) {
2049    testProperty([](RenderProperties& properties) {
2050        properties.setAlpha(0.5f);
2051        properties.setHasOverlappingRendering(false);
2052    }, [](const RectOp& op, const BakedOpState& state) {
2053        EXPECT_EQ(0.5f, state.alpha) << "Alpha should be applied directly to op";
2054    });
2055}
2056
2057RENDERTHREAD_TEST(FrameBuilder, renderPropClipping) {
2058    testProperty([](RenderProperties& properties) {
2059        properties.setClipToBounds(true);
2060        properties.setClipBounds(Rect(10, 20, 300, 400));
2061    }, [](const RectOp& op, const BakedOpState& state) {
2062        EXPECT_EQ(Rect(10, 20, 100, 100), state.computedState.clippedBounds)
2063                << "Clip rect should be intersection of node bounds and clip bounds";
2064    });
2065}
2066
2067RENDERTHREAD_TEST(FrameBuilder, renderPropRevealClip) {
2068    testProperty([](RenderProperties& properties) {
2069        properties.mutableRevealClip().set(true, 50, 50, 25);
2070    }, [](const RectOp& op, const BakedOpState& state) {
2071        ASSERT_NE(nullptr, state.roundRectClipState);
2072        EXPECT_TRUE(state.roundRectClipState->highPriority);
2073        EXPECT_EQ(25, state.roundRectClipState->radius);
2074        EXPECT_EQ(Rect(50, 50, 50, 50), state.roundRectClipState->innerRect);
2075    });
2076}
2077
2078RENDERTHREAD_TEST(FrameBuilder, renderPropOutlineClip) {
2079    testProperty([](RenderProperties& properties) {
2080        properties.mutableOutline().setShouldClip(true);
2081        properties.mutableOutline().setRoundRect(10, 20, 30, 40, 5.0f, 0.5f);
2082    }, [](const RectOp& op, const BakedOpState& state) {
2083        ASSERT_NE(nullptr, state.roundRectClipState);
2084        EXPECT_FALSE(state.roundRectClipState->highPriority);
2085        EXPECT_EQ(5, state.roundRectClipState->radius);
2086        EXPECT_EQ(Rect(15, 25, 25, 35), state.roundRectClipState->innerRect);
2087    });
2088}
2089
2090RENDERTHREAD_TEST(FrameBuilder, renderPropTransform) {
2091    testProperty([](RenderProperties& properties) {
2092        properties.setLeftTopRightBottom(10, 10, 110, 110);
2093
2094        SkMatrix staticMatrix = SkMatrix::MakeScale(1.2f, 1.2f);
2095        properties.setStaticMatrix(&staticMatrix);
2096
2097        // ignored, since static overrides animation
2098        SkMatrix animationMatrix = SkMatrix::MakeTrans(15, 15);
2099        properties.setAnimationMatrix(&animationMatrix);
2100
2101        properties.setTranslationX(10);
2102        properties.setTranslationY(20);
2103        properties.setScaleX(0.5f);
2104        properties.setScaleY(0.7f);
2105    }, [](const RectOp& op, const BakedOpState& state) {
2106        Matrix4 matrix;
2107        matrix.loadTranslate(10, 10, 0); // left, top
2108        matrix.scale(1.2f, 1.2f, 1); // static matrix
2109        // ignore animation matrix, since static overrides it
2110
2111        // translation xy
2112        matrix.translate(10, 20);
2113
2114        // scale xy (from default pivot - center)
2115        matrix.translate(50, 50);
2116        matrix.scale(0.5f, 0.7f, 1);
2117        matrix.translate(-50, -50);
2118        EXPECT_MATRIX_APPROX_EQ(matrix, state.computedState.transform)
2119                << "Op draw matrix must match expected combination of transformation properties";
2120    });
2121}
2122
2123struct SaveLayerAlphaData {
2124    uint32_t layerWidth = 0;
2125    uint32_t layerHeight = 0;
2126    Rect rectClippedBounds;
2127    Matrix4 rectMatrix;
2128    Matrix4 drawLayerMatrix;
2129};
2130/**
2131 * Constructs a view to hit the temporary layer alpha property implementation:
2132 *     a) 0 < alpha < 1
2133 *     b) too big for layer (larger than maxTextureSize)
2134 *     c) overlapping rendering content
2135 * returning observed data about layer size and content clip/transform.
2136 *
2137 * Used to validate clipping behavior of temporary layer, where requested layer size is reduced
2138 * (for efficiency, and to fit in layer size constraints) based on parent clip.
2139 */
2140void testSaveLayerAlphaClip(SaveLayerAlphaData* outObservedData,
2141        std::function<void(RenderProperties&)> propSetupCallback) {
2142    class SaveLayerAlphaClipTestRenderer : public TestRendererBase {
2143    public:
2144        explicit SaveLayerAlphaClipTestRenderer(SaveLayerAlphaData* outData)
2145                : mOutData(outData) {}
2146
2147        OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override {
2148            EXPECT_EQ(0, mIndex++);
2149            mOutData->layerWidth = width;
2150            mOutData->layerHeight = height;
2151            return nullptr;
2152        }
2153        void onRectOp(const RectOp& op, const BakedOpState& state) override {
2154            EXPECT_EQ(1, mIndex++);
2155
2156            mOutData->rectClippedBounds = state.computedState.clippedBounds;
2157            mOutData->rectMatrix = state.computedState.transform;
2158        }
2159        void endLayer() override {
2160            EXPECT_EQ(2, mIndex++);
2161        }
2162        void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
2163            EXPECT_EQ(3, mIndex++);
2164            mOutData->drawLayerMatrix = state.computedState.transform;
2165        }
2166        void recycleTemporaryLayer(OffscreenBuffer* offscreenBuffer) override {
2167            EXPECT_EQ(4, mIndex++);
2168        }
2169    private:
2170        SaveLayerAlphaData* mOutData;
2171    };
2172
2173    ASSERT_GT(10000, DeviceInfo::get()->maxTextureSize())
2174            << "Node must be bigger than max texture size to exercise saveLayer codepath";
2175    auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 10000, 10000,
2176            [&propSetupCallback](RenderProperties& properties, RecordingCanvas& canvas) {
2177        properties.setHasOverlappingRendering(true);
2178        properties.setAlpha(0.5f); // force saveLayer, since too big for HW layer
2179        // apply other properties
2180        propSetupCallback(properties);
2181
2182        SkPaint paint;
2183        paint.setColor(SK_ColorWHITE);
2184        canvas.drawRect(0, 0, 10000, 10000, paint);
2185    });
2186    auto syncedNode = TestUtils::getSyncedNode(node); // sync before querying height
2187
2188    FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
2189                sLightGeometry, Caches::getInstance());
2190    frameBuilder.deferRenderNode(*syncedNode);
2191
2192    SaveLayerAlphaClipTestRenderer renderer(outObservedData);
2193    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
2194
2195    // assert, since output won't be valid if we haven't seen a save layer triggered
2196    ASSERT_EQ(5, renderer.getIndex()) << "Test must trigger saveLayer alpha behavior.";
2197}
2198
2199RENDERTHREAD_TEST(FrameBuilder, renderPropSaveLayerAlphaClipBig) {
2200    SaveLayerAlphaData observedData;
2201    testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) {
2202        properties.setTranslationX(10); // offset rendering content
2203        properties.setTranslationY(-2000); // offset rendering content
2204    });
2205    EXPECT_EQ(190u, observedData.layerWidth);
2206    EXPECT_EQ(200u, observedData.layerHeight);
2207    EXPECT_EQ(Rect(190, 200), observedData.rectClippedBounds)
2208            << "expect content to be clipped to screen area";
2209    Matrix4 expected;
2210    expected.loadTranslate(0, -2000, 0);
2211    EXPECT_MATRIX_APPROX_EQ(expected, observedData.rectMatrix)
2212            << "expect content to be translated as part of being clipped";
2213    expected.loadTranslate(10, 0, 0);
2214    EXPECT_MATRIX_APPROX_EQ(expected, observedData.drawLayerMatrix)
2215                << "expect drawLayer to be translated as part of being clipped";
2216}
2217
2218RENDERTHREAD_TEST(FrameBuilder, renderPropSaveLayerAlphaRotate) {
2219    SaveLayerAlphaData observedData;
2220    testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) {
2221        // Translate and rotate the view so that the only visible part is the top left corner of
2222        // the view. It will form an isosceles right triangle with a long side length of 200 at the
2223        // bottom of the viewport.
2224        properties.setTranslationX(100);
2225        properties.setTranslationY(100);
2226        properties.setPivotX(0);
2227        properties.setPivotY(0);
2228        properties.setRotation(45);
2229    });
2230    // ceil(sqrt(2) / 2 * 200) = 142
2231    EXPECT_EQ(142u, observedData.layerWidth);
2232    EXPECT_EQ(142u, observedData.layerHeight);
2233    EXPECT_EQ(Rect(142, 142), observedData.rectClippedBounds);
2234    EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), observedData.rectMatrix);
2235}
2236
2237RENDERTHREAD_TEST(FrameBuilder, renderPropSaveLayerAlphaScale) {
2238    SaveLayerAlphaData observedData;
2239    testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) {
2240        properties.setPivotX(0);
2241        properties.setPivotY(0);
2242        properties.setScaleX(2);
2243        properties.setScaleY(0.5f);
2244    });
2245    EXPECT_EQ(100u, observedData.layerWidth);
2246    EXPECT_EQ(400u, observedData.layerHeight);
2247    EXPECT_EQ(Rect(100, 400), observedData.rectClippedBounds);
2248    EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), observedData.rectMatrix);
2249}
2250
2251RENDERTHREAD_TEST(FrameBuilder, clip_replace) {
2252    class ClipReplaceTestRenderer : public TestRendererBase {
2253    public:
2254        void onColorOp(const ColorOp& op, const BakedOpState& state) override {
2255            EXPECT_EQ(0, mIndex++);
2256            EXPECT_TRUE(op.localClip->intersectWithRoot);
2257            EXPECT_EQ(Rect(20, 10, 30, 40), state.computedState.clipState->rect)
2258                    << "Expect resolved clip to be intersection of viewport clip and clip op";
2259        }
2260    };
2261    auto node = TestUtils::createNode<RecordingCanvas>(20, 20, 30, 30,
2262            [](RenderProperties& props, RecordingCanvas& canvas) {
2263        canvas.clipRect(0, -20, 10, 30, SkClipOp::kReplace_private_internal_do_not_use);
2264        canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver);
2265    });
2266
2267    FrameBuilder frameBuilder(SkRect::MakeLTRB(10, 10, 40, 40), 50, 50,
2268            sLightGeometry, Caches::getInstance());
2269    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
2270
2271    ClipReplaceTestRenderer renderer;
2272    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
2273    EXPECT_EQ(1, renderer.getIndex());
2274}
2275
2276TEST(FrameBuilder, projectionReorderProjectedInMiddle) {
2277    /* R is backward projected on B
2278                A
2279               / \
2280              B   C
2281                  |
2282                  R
2283    */
2284    auto nodeA = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100,
2285            [](RenderProperties& props, RecordingCanvas& canvas) {
2286        drawOrderedNode(&canvas, 0, [](RenderProperties& props, RecordingCanvas& canvas) {
2287            props.setProjectionReceiver(true);
2288        } ); //nodeB
2289        drawOrderedNode(&canvas, 2, [](RenderProperties& props, RecordingCanvas& canvas) {
2290            drawOrderedNode(&canvas, 1, [](RenderProperties& props, RecordingCanvas& canvas) {
2291                props.setProjectBackwards(true);
2292                props.setClipToBounds(false);
2293            } ); //nodeR
2294        } ); //nodeC
2295    }); //nodeA
2296
2297    FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100,
2298            sLightGeometry, Caches::getInstance());
2299    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA));
2300
2301    ZReorderTestRenderer renderer;
2302    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
2303    EXPECT_EQ(3, renderer.getIndex());
2304}
2305
2306TEST(FrameBuilder, projectionReorderProjectLast) {
2307    /* R is backward projected on E
2308                  A
2309                / | \
2310               /  |  \
2311              B   C   E
2312                  |
2313                  R
2314    */
2315    auto nodeA = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100,
2316            [](RenderProperties& props, RecordingCanvas& canvas) {
2317        drawOrderedNode(&canvas, 0,  nullptr); //nodeB
2318        drawOrderedNode(&canvas, 1, [](RenderProperties& props, RecordingCanvas& canvas) {
2319            drawOrderedNode(&canvas, 3, [](RenderProperties& props, RecordingCanvas& canvas) { //drawn as 2
2320                props.setProjectBackwards(true);
2321                props.setClipToBounds(false);
2322            } ); //nodeR
2323        } ); //nodeC
2324        drawOrderedNode(&canvas, 2, [](RenderProperties& props, RecordingCanvas& canvas) { //drawn as 3
2325            props.setProjectionReceiver(true);
2326        } ); //nodeE
2327    }); //nodeA
2328
2329    FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100,
2330            sLightGeometry, Caches::getInstance());
2331    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA));
2332
2333    ZReorderTestRenderer renderer;
2334    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
2335    EXPECT_EQ(4, renderer.getIndex());
2336}
2337
2338TEST(FrameBuilder, projectionReorderNoReceivable) {
2339    /* R is backward projected without receiver
2340                A
2341               / \
2342              B   C
2343                  |
2344                  R
2345    */
2346     auto nodeA = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100,
2347            [](RenderProperties& props, RecordingCanvas& canvas) {
2348        drawOrderedNode(&canvas, 0, nullptr); //nodeB
2349        drawOrderedNode(&canvas, 1, [](RenderProperties& props, RecordingCanvas& canvas) {
2350            drawOrderedNode(&canvas, 255,  [](RenderProperties& props, RecordingCanvas& canvas) {
2351                //not having a projection receiver is an undefined behavior
2352                props.setProjectBackwards(true);
2353                props.setClipToBounds(false);
2354            } ); //nodeR
2355        } ); //nodeC
2356    }); //nodeA
2357
2358    FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100,
2359            sLightGeometry, Caches::getInstance());
2360    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA));
2361
2362    ZReorderTestRenderer renderer;
2363    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
2364    EXPECT_EQ(2, renderer.getIndex());
2365}
2366
2367TEST(FrameBuilder, projectionReorderParentReceivable) {
2368    /* R is backward projected on C
2369                A
2370               / \
2371              B   C
2372                  |
2373                  R
2374    */
2375     auto nodeA = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100,
2376            [](RenderProperties& props, RecordingCanvas& canvas) {
2377        drawOrderedNode(&canvas, 0, nullptr); //nodeB
2378        drawOrderedNode(&canvas, 1, [](RenderProperties& props, RecordingCanvas& canvas) {
2379            props.setProjectionReceiver(true);
2380            drawOrderedNode(&canvas, 2, [](RenderProperties& props, RecordingCanvas& canvas) {
2381                props.setProjectBackwards(true);
2382                props.setClipToBounds(false);
2383            } ); //nodeR
2384        } ); //nodeC
2385    }); //nodeA
2386
2387    FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100,
2388            sLightGeometry, Caches::getInstance());
2389    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA));
2390
2391    ZReorderTestRenderer renderer;
2392    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
2393    EXPECT_EQ(3, renderer.getIndex());
2394}
2395
2396TEST(FrameBuilder, projectionReorderSameNodeReceivable) {
2397     auto nodeA = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100,
2398            [](RenderProperties& props, RecordingCanvas& canvas) {
2399        drawOrderedNode(&canvas, 0, nullptr); //nodeB
2400        drawOrderedNode(&canvas, 1, [](RenderProperties& props, RecordingCanvas& canvas) {
2401            drawOrderedNode(&canvas, 255, [](RenderProperties& props, RecordingCanvas& canvas) {
2402                //having a node that is projected on itself is an undefined/unexpected behavior
2403                props.setProjectionReceiver(true);
2404                props.setProjectBackwards(true);
2405                props.setClipToBounds(false);
2406            } ); //nodeR
2407        } ); //nodeC
2408    }); //nodeA
2409
2410    FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100,
2411            sLightGeometry, Caches::getInstance());
2412    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA));
2413
2414    ZReorderTestRenderer renderer;
2415    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
2416    EXPECT_EQ(2, renderer.getIndex());
2417}
2418
2419TEST(FrameBuilder, projectionReorderProjectedSibling) {
2420    //TODO: this test together with the next "projectionReorderProjectedSibling2" likely expose a
2421    //bug in HWUI. First test draws R, while the second test does not draw R for a nearly identical
2422    //tree setup. The correct behaviour is to not draw R, because the receiver cannot be a sibling
2423    /* R is backward projected on B. R is not expected to be drawn (see Sibling2 outcome below),
2424       but for some reason it is drawn.
2425                A
2426               /|\
2427              / | \
2428             B  C  R
2429    */
2430    auto nodeA = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100,
2431            [](RenderProperties& props, RecordingCanvas& canvas) {
2432        drawOrderedNode(&canvas, 0, [](RenderProperties& props, RecordingCanvas& canvas) {
2433            props.setProjectionReceiver(true);
2434        } ); //nodeB
2435        drawOrderedNode(&canvas, 2, [](RenderProperties& props, RecordingCanvas& canvas) {
2436        } ); //nodeC
2437        drawOrderedNode(&canvas, 1, [](RenderProperties& props, RecordingCanvas& canvas) {
2438            props.setProjectBackwards(true);
2439            props.setClipToBounds(false);
2440        } ); //nodeR
2441    }); //nodeA
2442
2443    FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100,
2444            sLightGeometry, Caches::getInstance());
2445    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA));
2446
2447    ZReorderTestRenderer renderer;
2448    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
2449    EXPECT_EQ(3, renderer.getIndex());
2450}
2451
2452TEST(FrameBuilder, projectionReorderProjectedSibling2) {
2453    /* R is set to project on B, but R is not drawn because projecting on a sibling is not allowed.
2454                A
2455                |
2456                G
2457               /|\
2458              / | \
2459             B  C  R
2460    */
2461    auto nodeA = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100,
2462            [](RenderProperties& props, RecordingCanvas& canvas) {
2463        drawOrderedNode(&canvas, 0, [](RenderProperties& props, RecordingCanvas& canvas) { //G
2464            drawOrderedNode(&canvas, 1, [](RenderProperties& props, RecordingCanvas& canvas) { //B
2465                props.setProjectionReceiver(true);
2466            } ); //nodeB
2467            drawOrderedNode(&canvas, 2, [](RenderProperties& props, RecordingCanvas& canvas) { //C
2468            } ); //nodeC
2469            drawOrderedNode(&canvas, 255, [](RenderProperties& props, RecordingCanvas& canvas) { //R
2470                props.setProjectBackwards(true);
2471                props.setClipToBounds(false);
2472            } ); //nodeR
2473        } ); //nodeG
2474    }); //nodeA
2475
2476    FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100,
2477            sLightGeometry, Caches::getInstance());
2478    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA));
2479
2480    ZReorderTestRenderer renderer;
2481    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
2482    EXPECT_EQ(3, renderer.getIndex());
2483}
2484
2485TEST(FrameBuilder, projectionReorderGrandparentReceivable) {
2486    /* R is backward projected on B
2487                A
2488                |
2489                B
2490                |
2491                C
2492                |
2493                R
2494    */
2495    auto nodeA = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100,
2496            [](RenderProperties& props, RecordingCanvas& canvas) {
2497        drawOrderedNode(&canvas, 0, [](RenderProperties& props, RecordingCanvas& canvas) {
2498            props.setProjectionReceiver(true);
2499            drawOrderedNode(&canvas, 1, [](RenderProperties& props, RecordingCanvas& canvas) {
2500                drawOrderedNode(&canvas, 2, [](RenderProperties& props, RecordingCanvas& canvas) {
2501                    props.setProjectBackwards(true);
2502                    props.setClipToBounds(false);
2503                } ); //nodeR
2504            } ); //nodeC
2505        } ); //nodeB
2506    }); //nodeA
2507
2508    FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100,
2509            sLightGeometry, Caches::getInstance());
2510    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA));
2511
2512    ZReorderTestRenderer renderer;
2513    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
2514    EXPECT_EQ(3, renderer.getIndex());
2515}
2516
2517TEST(FrameBuilder, projectionReorderTwoReceivables) {
2518    /* B and G are receivables, R is backward projected
2519                A
2520               / \
2521              B   C
2522                 / \
2523                G   R
2524    */
2525    auto nodeA = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100,
2526            [](RenderProperties& props, RecordingCanvas& canvas) {
2527        drawOrderedNode(&canvas, 0, [](RenderProperties& props, RecordingCanvas& canvas) { //B
2528            props.setProjectionReceiver(true);
2529        } ); //nodeB
2530        drawOrderedNode(&canvas, 2, [](RenderProperties& props, RecordingCanvas& canvas) { //C
2531            drawOrderedNode(&canvas, 3, [](RenderProperties& props, RecordingCanvas& canvas) { //G
2532                props.setProjectionReceiver(true);
2533            } ); //nodeG
2534            drawOrderedNode(&canvas, 1, [](RenderProperties& props, RecordingCanvas& canvas) { //R
2535                props.setProjectBackwards(true);
2536                props.setClipToBounds(false);
2537            } ); //nodeR
2538        } ); //nodeC
2539    }); //nodeA
2540
2541    FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100,
2542            sLightGeometry, Caches::getInstance());
2543    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA));
2544
2545    ZReorderTestRenderer renderer;
2546    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
2547    EXPECT_EQ(4, renderer.getIndex());
2548}
2549
2550TEST(FrameBuilder, projectionReorderTwoReceivablesLikelyScenario) {
2551    /* B and G are receivables, G is backward projected
2552                A
2553               / \
2554              B   C
2555                 / \
2556                G   R
2557    */
2558    auto nodeA = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100,
2559            [](RenderProperties& props, RecordingCanvas& canvas) {
2560        drawOrderedNode(&canvas, 0, [](RenderProperties& props, RecordingCanvas& canvas) { //B
2561            props.setProjectionReceiver(true);
2562        } ); //nodeB
2563        drawOrderedNode(&canvas, 2, [](RenderProperties& props, RecordingCanvas& canvas) { //C
2564            drawOrderedNode(&canvas, 1, [](RenderProperties& props, RecordingCanvas& canvas) { //G
2565                props.setProjectionReceiver(true);
2566                props.setProjectBackwards(true);
2567                props.setClipToBounds(false);
2568            } ); //nodeG
2569            drawOrderedNode(&canvas, 3, [](RenderProperties& props, RecordingCanvas& canvas) { //R
2570            } ); //nodeR
2571        } ); //nodeC
2572    }); //nodeA
2573
2574    FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100,
2575            sLightGeometry, Caches::getInstance());
2576    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA));
2577
2578    ZReorderTestRenderer renderer;
2579    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
2580    EXPECT_EQ(4, renderer.getIndex());
2581}
2582
2583TEST(FrameBuilder, projectionReorderTwoReceivablesDeeper) {
2584    /* B and G are receivables, R is backward projected
2585                A
2586               / \
2587              B   C
2588                 / \
2589                G   D
2590                    |
2591                    R
2592    */
2593    auto nodeA = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100,
2594            [](RenderProperties& props, RecordingCanvas& canvas) {
2595        drawOrderedNode(&canvas, 0, [](RenderProperties& props, RecordingCanvas& canvas) { //B
2596            props.setProjectionReceiver(true);
2597        } ); //nodeB
2598        drawOrderedNode(&canvas, 1, [](RenderProperties& props, RecordingCanvas& canvas) { //C
2599            drawOrderedNode(&canvas, 2, [](RenderProperties& props, RecordingCanvas& canvas) { //G
2600                props.setProjectionReceiver(true);
2601            } ); //nodeG
2602            drawOrderedNode(&canvas, 4, [](RenderProperties& props, RecordingCanvas& canvas) { //D
2603                drawOrderedNode(&canvas, 3, [](RenderProperties& props, RecordingCanvas& canvas) { //R
2604                    props.setProjectBackwards(true);
2605                    props.setClipToBounds(false);
2606                } ); //nodeR
2607            } ); //nodeD
2608        } ); //nodeC
2609    }); //nodeA
2610
2611    FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100,
2612            sLightGeometry, Caches::getInstance());
2613    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA));
2614
2615    ZReorderTestRenderer renderer;
2616    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
2617    EXPECT_EQ(5, renderer.getIndex());
2618}
2619
2620} // namespace uirenderer
2621} // namespace android
2622