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