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