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