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