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