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