FrameBuilderTests.cpp revision f158b49c888f722194afe5a80539a2b020c130bc
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 <LayerUpdateQueue.h>
23#include <RecordedOp.h>
24#include <RecordingCanvas.h>
25#include <tests/common/TestUtils.h>
26
27#include <unordered_map>
28
29namespace android {
30namespace uirenderer {
31
32const LayerUpdateQueue sEmptyLayerUpdateQueue;
33const Vector3 sLightCenter = {100, 100, 100};
34
35static std::vector<sp<RenderNode>> createSyncedNodeList(sp<RenderNode>& node) {
36    TestUtils::syncHierarchyPropertiesAndDisplayList(node);
37    std::vector<sp<RenderNode>> vec;
38    vec.emplace_back(node);
39    return vec;
40}
41
42/**
43 * Virtual class implemented by each test to redirect static operation / state transitions to
44 * virtual methods.
45 *
46 * Virtual dispatch allows for default behaviors to be specified (very common case in below tests),
47 * and allows Renderer vs Dispatching behavior to be merged.
48 *
49 * onXXXOp methods fail by default - tests should override ops they expect
50 * startRepaintLayer fails by default - tests should override if expected
51 * startFrame/endFrame do nothing by default - tests should override to intercept
52 */
53class TestRendererBase {
54public:
55    virtual ~TestRendererBase() {}
56    virtual OffscreenBuffer* startTemporaryLayer(uint32_t, uint32_t) {
57        ADD_FAILURE() << "Layer creation not expected in this test";
58        return nullptr;
59    }
60    virtual void startRepaintLayer(OffscreenBuffer*, const Rect& repaintRect) {
61        ADD_FAILURE() << "Layer repaint not expected in this test";
62    }
63    virtual void endLayer() {
64        ADD_FAILURE() << "Layer updates not expected in this test";
65    }
66    virtual void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) {}
67    virtual void endFrame(const Rect& repaintRect) {}
68
69    // define virtual defaults for single draw methods
70#define X(Type) \
71    virtual void on##Type(const Type&, const BakedOpState&) { \
72        ADD_FAILURE() << #Type " not expected in this test"; \
73    }
74    MAP_RENDERABLE_OPS(X)
75#undef X
76
77    // define virtual defaults for merged draw methods
78#define X(Type) \
79    virtual void onMerged##Type##s(const MergedBakedOpList& opList) { \
80        ADD_FAILURE() << "Merged " #Type "s not expected in this test"; \
81    }
82    MAP_MERGEABLE_OPS(X)
83#undef X
84
85    int getIndex() { return mIndex; }
86
87protected:
88    int mIndex = 0;
89};
90
91/**
92 * Dispatches all static methods to similar formed methods on renderer, which fail by default but
93 * are overridden by subclasses per test.
94 */
95class TestDispatcher {
96public:
97    // define single op methods, which redirect to TestRendererBase
98#define X(Type) \
99    static void on##Type(TestRendererBase& renderer, const Type& op, const BakedOpState& state) { \
100        renderer.on##Type(op, state); \
101    }
102    MAP_RENDERABLE_OPS(X);
103#undef X
104
105    // define merged op methods, which redirect to TestRendererBase
106#define X(Type) \
107    static void onMerged##Type##s(TestRendererBase& renderer, const MergedBakedOpList& opList) { \
108        renderer.onMerged##Type##s(opList); \
109    }
110    MAP_MERGEABLE_OPS(X);
111#undef X
112};
113
114class FailRenderer : public TestRendererBase {};
115
116TEST(FrameBuilder, simple) {
117    class SimpleTestRenderer : public TestRendererBase {
118    public:
119        void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
120            EXPECT_EQ(0, mIndex++);
121            EXPECT_EQ(100u, width);
122            EXPECT_EQ(200u, height);
123        }
124        void onRectOp(const RectOp& op, const BakedOpState& state) override {
125            EXPECT_EQ(1, mIndex++);
126        }
127        void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override {
128            EXPECT_EQ(2, mIndex++);
129        }
130        void endFrame(const Rect& repaintRect) override {
131            EXPECT_EQ(3, mIndex++);
132        }
133    };
134
135    auto node = TestUtils::createNode(0, 0, 100, 200,
136            [](RenderProperties& props, RecordingCanvas& canvas) {
137        SkBitmap bitmap = TestUtils::createSkBitmap(25, 25);
138        canvas.drawRect(0, 0, 100, 200, SkPaint());
139        canvas.drawBitmap(bitmap, 10, 10, nullptr);
140    });
141    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200,
142            createSyncedNodeList(node), sLightCenter);
143    SimpleTestRenderer renderer;
144    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
145    EXPECT_EQ(4, renderer.getIndex()); // 2 ops + start + end
146}
147
148TEST(FrameBuilder, simpleStroke) {
149    class SimpleStrokeTestRenderer : public TestRendererBase {
150    public:
151        void onPointsOp(const PointsOp& op, const BakedOpState& state) override {
152            EXPECT_EQ(0, mIndex++);
153            // even though initial bounds are empty...
154            EXPECT_TRUE(op.unmappedBounds.isEmpty())
155                    << "initial bounds should be empty, since they're unstroked";
156            EXPECT_EQ(Rect(45, 45, 55, 55), state.computedState.clippedBounds)
157                    << "final bounds should account for stroke";
158        }
159    };
160
161    auto node = TestUtils::createNode(0, 0, 100, 200,
162            [](RenderProperties& props, RecordingCanvas& canvas) {
163        SkPaint strokedPaint;
164        strokedPaint.setStrokeWidth(10);
165        canvas.drawPoint(50, 50, strokedPaint);
166    });
167    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200,
168            createSyncedNodeList(node), sLightCenter);
169    SimpleStrokeTestRenderer renderer;
170    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
171    EXPECT_EQ(1, renderer.getIndex());
172}
173
174TEST(FrameBuilder, simpleRejection) {
175    auto node = TestUtils::createNode(0, 0, 200, 200,
176            [](RenderProperties& props, RecordingCanvas& canvas) {
177        canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
178        canvas.clipRect(200, 200, 400, 400, SkRegion::kIntersect_Op); // intersection should be empty
179        canvas.drawRect(0, 0, 400, 400, SkPaint());
180        canvas.restore();
181    });
182    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
183            createSyncedNodeList(node), sLightCenter);
184
185    FailRenderer renderer;
186    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
187}
188
189TEST(FrameBuilder, simpleBatching) {
190    const int LOOPS = 5;
191    class SimpleBatchingTestRenderer : public TestRendererBase {
192    public:
193        void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override {
194            EXPECT_TRUE(mIndex++ >= LOOPS) << "Bitmaps should be above all rects";
195        }
196        void onRectOp(const RectOp& op, const BakedOpState& state) override {
197            EXPECT_TRUE(mIndex++ < LOOPS) << "Rects should be below all bitmaps";
198        }
199    };
200
201    auto node = TestUtils::createNode(0, 0, 200, 200,
202            [](RenderProperties& props, RecordingCanvas& canvas) {
203        SkBitmap bitmap = TestUtils::createSkBitmap(10, 10,
204                kAlpha_8_SkColorType); // Disable merging by using alpha 8 bitmap
205
206        // Alternate between drawing rects and bitmaps, with bitmaps overlapping rects.
207        // Rects don't overlap bitmaps, so bitmaps should be brought to front as a group.
208        canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
209        for (int i = 0; i < LOOPS; i++) {
210            canvas.translate(0, 10);
211            canvas.drawRect(0, 0, 10, 10, SkPaint());
212            canvas.drawBitmap(bitmap, 5, 0, nullptr);
213        }
214        canvas.restore();
215    });
216
217    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
218            createSyncedNodeList(node), sLightCenter);
219    SimpleBatchingTestRenderer renderer;
220    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
221    EXPECT_EQ(2 * LOOPS, renderer.getIndex())
222            << "Expect number of ops = 2 * loop count";
223}
224
225TEST(FrameBuilder, clippedMerging) {
226    class ClippedMergingTestRenderer : public TestRendererBase {
227    public:
228        void onMergedBitmapOps(const MergedBakedOpList& opList) override {
229            EXPECT_EQ(0, mIndex);
230            mIndex += opList.count;
231            EXPECT_EQ(4u, opList.count);
232            EXPECT_EQ(Rect(10, 10, 90, 90), opList.clip);
233            EXPECT_EQ(OpClipSideFlags::Left | OpClipSideFlags::Top | OpClipSideFlags::Right,
234                    opList.clipSideFlags);
235        }
236    };
237    auto node = TestUtils::createNode(0, 0, 100, 100,
238            [](RenderProperties& props, TestCanvas& canvas) {
239        SkBitmap bitmap = TestUtils::createSkBitmap(20, 20);
240
241        // left side clipped (to inset left half)
242        canvas.clipRect(10, 0, 50, 100, SkRegion::kReplace_Op);
243        canvas.drawBitmap(bitmap, 0, 40, nullptr);
244
245        // top side clipped (to inset top half)
246        canvas.clipRect(0, 10, 100, 50, SkRegion::kReplace_Op);
247        canvas.drawBitmap(bitmap, 40, 0, nullptr);
248
249        // right side clipped (to inset right half)
250        canvas.clipRect(50, 0, 90, 100, SkRegion::kReplace_Op);
251        canvas.drawBitmap(bitmap, 80, 40, nullptr);
252
253        // bottom not clipped, just abutting (inset bottom half)
254        canvas.clipRect(0, 50, 100, 90, SkRegion::kReplace_Op);
255        canvas.drawBitmap(bitmap, 40, 70, nullptr);
256    });
257
258    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100,
259            createSyncedNodeList(node), sLightCenter);
260    ClippedMergingTestRenderer renderer;
261    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
262    EXPECT_EQ(4, renderer.getIndex());
263}
264
265TEST(FrameBuilder, textMerging) {
266    class TextMergingTestRenderer : public TestRendererBase {
267    public:
268        void onMergedTextOps(const MergedBakedOpList& opList) override {
269            EXPECT_EQ(0, mIndex);
270            mIndex += opList.count;
271            EXPECT_EQ(2u, opList.count);
272            EXPECT_EQ(OpClipSideFlags::Top, opList.clipSideFlags);
273            EXPECT_EQ(OpClipSideFlags::Top, opList.states[0]->computedState.clipSideFlags);
274            EXPECT_EQ(OpClipSideFlags::None, opList.states[1]->computedState.clipSideFlags);
275        }
276    };
277    auto node = TestUtils::createNode(0, 0, 400, 400,
278            [](RenderProperties& props, TestCanvas& canvas) {
279        SkPaint paint;
280        paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
281        paint.setAntiAlias(true);
282        paint.setTextSize(50);
283        TestUtils::drawTextToCanvas(&canvas, "Test string1", paint, 100, 0); // will be top clipped
284        TestUtils::drawTextToCanvas(&canvas, "Test string1", paint, 100, 100); // not clipped
285    });
286    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(400, 400), 400, 400,
287            createSyncedNodeList(node), sLightCenter);
288    TextMergingTestRenderer renderer;
289    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
290    EXPECT_EQ(2, renderer.getIndex()) << "Expect 2 ops";
291}
292
293TEST(FrameBuilder, textStrikethrough) {
294    const int LOOPS = 5;
295    class TextStrikethroughTestRenderer : public TestRendererBase {
296    public:
297        void onRectOp(const RectOp& op, const BakedOpState& state) override {
298            EXPECT_TRUE(mIndex++ >= LOOPS) << "Strikethrough rects should be above all text";
299        }
300        void onMergedTextOps(const MergedBakedOpList& opList) override {
301            EXPECT_EQ(0, mIndex);
302            mIndex += opList.count;
303            EXPECT_EQ(5u, opList.count);
304        }
305    };
306    auto node = TestUtils::createNode(0, 0, 200, 2000,
307            [](RenderProperties& props, RecordingCanvas& canvas) {
308        SkPaint textPaint;
309        textPaint.setAntiAlias(true);
310        textPaint.setTextSize(20);
311        textPaint.setStrikeThruText(true);
312        textPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
313        for (int i = 0; i < LOOPS; i++) {
314            TestUtils::drawTextToCanvas(&canvas, "test text", textPaint, 10, 100 * (i + 1));
315        }
316    });
317    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 2000), 200, 2000,
318            createSyncedNodeList(node), sLightCenter);
319    TextStrikethroughTestRenderer renderer;
320    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
321    EXPECT_EQ(2 * LOOPS, renderer.getIndex())
322            << "Expect number of ops = 2 * loop count";
323}
324
325RENDERTHREAD_TEST(FrameBuilder, textureLayer) {
326    class TextureLayerTestRenderer : public TestRendererBase {
327    public:
328        void onTextureLayerOp(const TextureLayerOp& op, const BakedOpState& state) override {
329            EXPECT_EQ(0, mIndex++);
330            EXPECT_EQ(Rect(50, 50, 150, 150), state.computedState.clipRect());
331            EXPECT_EQ(Rect(50, 50, 105, 105), state.computedState.clippedBounds);
332
333            Matrix4 expected;
334            expected.loadTranslate(5, 5, 0);
335            EXPECT_MATRIX_APPROX_EQ(expected, state.computedState.transform);
336        }
337    };
338
339    auto layerUpdater = TestUtils::createTextureLayerUpdater(renderThread, 100, 100,
340            [](Matrix4* transform) {
341        transform->loadTranslate(5, 5, 0);
342    });
343
344    auto node = TestUtils::createNode(0, 0, 200, 200,
345            [&layerUpdater](RenderProperties& props, RecordingCanvas& canvas) {
346        canvas.save(SkCanvas::kMatrixClip_SaveFlag);
347        canvas.clipRect(50, 50, 150, 150, SkRegion::kIntersect_Op);
348        canvas.drawLayer(layerUpdater.get());
349        canvas.restore();
350    });
351    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
352            createSyncedNodeList(node), sLightCenter);
353    TextureLayerTestRenderer renderer;
354    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
355    EXPECT_EQ(1, renderer.getIndex());
356}
357
358TEST(FrameBuilder, renderNode) {
359    class RenderNodeTestRenderer : public TestRendererBase {
360    public:
361        void onRectOp(const RectOp& op, const BakedOpState& state) override {
362            switch(mIndex++) {
363            case 0:
364                EXPECT_EQ(Rect(200, 200), state.computedState.clippedBounds);
365                EXPECT_EQ(SK_ColorDKGRAY, op.paint->getColor());
366                break;
367            case 1:
368                EXPECT_EQ(Rect(50, 50, 150, 150), state.computedState.clippedBounds);
369                EXPECT_EQ(SK_ColorWHITE, op.paint->getColor());
370                break;
371            default:
372                ADD_FAILURE();
373            }
374        }
375    };
376
377    auto child = TestUtils::createNode(10, 10, 110, 110,
378            [](RenderProperties& props, RecordingCanvas& canvas) {
379        SkPaint paint;
380        paint.setColor(SK_ColorWHITE);
381        canvas.drawRect(0, 0, 100, 100, paint);
382    });
383
384    auto parent = TestUtils::createNode(0, 0, 200, 200,
385            [&child](RenderProperties& props, RecordingCanvas& canvas) {
386        SkPaint paint;
387        paint.setColor(SK_ColorDKGRAY);
388        canvas.drawRect(0, 0, 200, 200, paint);
389
390        canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
391        canvas.translate(40, 40);
392        canvas.drawRenderNode(child.get());
393        canvas.restore();
394    });
395
396    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
397            createSyncedNodeList(parent), sLightCenter);
398    RenderNodeTestRenderer renderer;
399    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
400}
401
402TEST(FrameBuilder, clipped) {
403    class ClippedTestRenderer : public TestRendererBase {
404    public:
405        void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override {
406            EXPECT_EQ(0, mIndex++);
407            EXPECT_EQ(Rect(10, 20, 30, 40), state.computedState.clippedBounds);
408            EXPECT_EQ(Rect(10, 20, 30, 40), state.computedState.clipRect());
409            EXPECT_TRUE(state.computedState.transform.isIdentity());
410        }
411    };
412
413    auto node = TestUtils::createNode(0, 0, 200, 200,
414            [](RenderProperties& props, RecordingCanvas& canvas) {
415        SkBitmap bitmap = TestUtils::createSkBitmap(200, 200);
416        canvas.drawBitmap(bitmap, 0, 0, nullptr);
417    });
418
419    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue,
420            SkRect::MakeLTRB(10, 20, 30, 40), // clip to small area, should see in receiver
421            200, 200, createSyncedNodeList(node), sLightCenter);
422    ClippedTestRenderer renderer;
423    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
424}
425
426TEST(FrameBuilder, saveLayer_simple) {
427    class SaveLayerSimpleTestRenderer : public TestRendererBase {
428    public:
429        OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override {
430            EXPECT_EQ(0, mIndex++);
431            EXPECT_EQ(180u, width);
432            EXPECT_EQ(180u, height);
433            return nullptr;
434        }
435        void endLayer() override {
436            EXPECT_EQ(2, mIndex++);
437        }
438        void onRectOp(const RectOp& op, const BakedOpState& state) override {
439            EXPECT_EQ(1, mIndex++);
440            EXPECT_EQ(Rect(10, 10, 190, 190), op.unmappedBounds);
441            EXPECT_EQ(Rect(180, 180), state.computedState.clippedBounds);
442            EXPECT_EQ(Rect(180, 180), state.computedState.clipRect());
443
444            Matrix4 expectedTransform;
445            expectedTransform.loadTranslate(-10, -10, 0);
446            EXPECT_MATRIX_APPROX_EQ(expectedTransform, state.computedState.transform);
447        }
448        void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
449            EXPECT_EQ(3, mIndex++);
450            EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds);
451            EXPECT_EQ(Rect(200, 200), state.computedState.clipRect());
452            EXPECT_TRUE(state.computedState.transform.isIdentity());
453        }
454    };
455
456    auto node = TestUtils::createNode(0, 0, 200, 200,
457            [](RenderProperties& props, RecordingCanvas& canvas) {
458        canvas.saveLayerAlpha(10, 10, 190, 190, 128, SkCanvas::kClipToLayer_SaveFlag);
459        canvas.drawRect(10, 10, 190, 190, SkPaint());
460        canvas.restore();
461    });
462    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
463            createSyncedNodeList(node), sLightCenter);
464    SaveLayerSimpleTestRenderer renderer;
465    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
466    EXPECT_EQ(4, renderer.getIndex());
467}
468
469TEST(FrameBuilder, saveLayer_nested) {
470    /* saveLayer1 { rect1, saveLayer2 { rect2 } } will play back as:
471     * - startTemporaryLayer2, rect2 endLayer2
472     * - startTemporaryLayer1, rect1, drawLayer2, endLayer1
473     * - startFrame, layerOp1, endFrame
474     */
475    class SaveLayerNestedTestRenderer : public TestRendererBase {
476    public:
477        OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override {
478            const int index = mIndex++;
479            if (index == 0) {
480                EXPECT_EQ(400u, width);
481                EXPECT_EQ(400u, height);
482                return (OffscreenBuffer*) 0x400;
483            } else if (index == 3) {
484                EXPECT_EQ(800u, width);
485                EXPECT_EQ(800u, height);
486                return (OffscreenBuffer*) 0x800;
487            } else { ADD_FAILURE(); }
488            return (OffscreenBuffer*) nullptr;
489        }
490        void endLayer() override {
491            int index = mIndex++;
492            EXPECT_TRUE(index == 2 || index == 6);
493        }
494        void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
495            EXPECT_EQ(7, mIndex++);
496        }
497        void endFrame(const Rect& repaintRect) override {
498            EXPECT_EQ(9, mIndex++);
499        }
500        void onRectOp(const RectOp& op, const BakedOpState& state) override {
501            const int index = mIndex++;
502            if (index == 1) {
503                EXPECT_EQ(Rect(400, 400), op.unmappedBounds); // inner rect
504            } else if (index == 4) {
505                EXPECT_EQ(Rect(800, 800), op.unmappedBounds); // outer rect
506            } else { ADD_FAILURE(); }
507        }
508        void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
509            const int index = mIndex++;
510            if (index == 5) {
511                EXPECT_EQ((OffscreenBuffer*)0x400, *op.layerHandle);
512                EXPECT_EQ(Rect(400, 400), op.unmappedBounds); // inner layer
513            } else if (index == 8) {
514                EXPECT_EQ((OffscreenBuffer*)0x800, *op.layerHandle);
515                EXPECT_EQ(Rect(800, 800), op.unmappedBounds); // outer layer
516            } else { ADD_FAILURE(); }
517        }
518    };
519
520    auto node = TestUtils::createNode(0, 0, 800, 800,
521            [](RenderProperties& props, RecordingCanvas& canvas) {
522        canvas.saveLayerAlpha(0, 0, 800, 800, 128, SkCanvas::kClipToLayer_SaveFlag);
523        {
524            canvas.drawRect(0, 0, 800, 800, SkPaint());
525            canvas.saveLayerAlpha(0, 0, 400, 400, 128, SkCanvas::kClipToLayer_SaveFlag);
526            {
527                canvas.drawRect(0, 0, 400, 400, SkPaint());
528            }
529            canvas.restore();
530        }
531        canvas.restore();
532    });
533
534    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(800, 800), 800, 800,
535            createSyncedNodeList(node), sLightCenter);
536    SaveLayerNestedTestRenderer renderer;
537    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
538    EXPECT_EQ(10, renderer.getIndex());
539}
540
541TEST(FrameBuilder, saveLayer_contentRejection) {
542        auto node = TestUtils::createNode(0, 0, 200, 200,
543                [](RenderProperties& props, RecordingCanvas& canvas) {
544        canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
545        canvas.clipRect(200, 200, 400, 400, SkRegion::kIntersect_Op);
546        canvas.saveLayerAlpha(200, 200, 400, 400, 128, SkCanvas::kClipToLayer_SaveFlag);
547
548        // draw within save layer may still be recorded, but shouldn't be drawn
549        canvas.drawRect(200, 200, 400, 400, SkPaint());
550
551        canvas.restore();
552        canvas.restore();
553    });
554    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
555            createSyncedNodeList(node), sLightCenter);
556
557    FailRenderer renderer;
558    // should see no ops, even within the layer, since the layer should be rejected
559    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
560}
561
562TEST(FrameBuilder, saveLayerUnclipped_simple) {
563    class SaveLayerUnclippedSimpleTestRenderer : public TestRendererBase {
564    public:
565        void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override {
566            EXPECT_EQ(0, mIndex++);
567            EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds);
568            EXPECT_CLIP_RECT(Rect(200, 200), state.computedState.clipState);
569            EXPECT_TRUE(state.computedState.transform.isIdentity());
570        }
571        void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override {
572            EXPECT_EQ(1, mIndex++);
573            ASSERT_NE(nullptr, op.paint);
574            ASSERT_EQ(SkXfermode::kClear_Mode, PaintUtils::getXfermodeDirect(op.paint));
575        }
576        void onRectOp(const RectOp& op, const BakedOpState& state) override {
577            EXPECT_EQ(2, mIndex++);
578            EXPECT_EQ(Rect(200, 200), op.unmappedBounds);
579            EXPECT_EQ(Rect(200, 200), state.computedState.clippedBounds);
580            EXPECT_EQ(Rect(200, 200), state.computedState.clipRect());
581            EXPECT_TRUE(state.computedState.transform.isIdentity());
582        }
583        void onCopyFromLayerOp(const CopyFromLayerOp& op, const BakedOpState& state) override {
584            EXPECT_EQ(3, mIndex++);
585            EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds);
586            EXPECT_CLIP_RECT(Rect(200, 200), state.computedState.clipState);
587            EXPECT_TRUE(state.computedState.transform.isIdentity());
588        }
589    };
590
591    auto node = TestUtils::createNode(0, 0, 200, 200,
592            [](RenderProperties& props, RecordingCanvas& canvas) {
593        canvas.saveLayerAlpha(10, 10, 190, 190, 128, SkCanvas::kMatrixClip_SaveFlag);
594        canvas.drawRect(0, 0, 200, 200, SkPaint());
595        canvas.restore();
596    });
597    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
598            createSyncedNodeList(node), sLightCenter);
599    SaveLayerUnclippedSimpleTestRenderer renderer;
600    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
601    EXPECT_EQ(4, renderer.getIndex());
602}
603
604TEST(FrameBuilder, saveLayerUnclipped_mergedClears) {
605    class SaveLayerUnclippedMergedClearsTestRenderer : public TestRendererBase {
606    public:
607        void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override {
608            int index = mIndex++;
609            EXPECT_GT(4, index);
610            EXPECT_EQ(5, op.unmappedBounds.getWidth());
611            EXPECT_EQ(5, op.unmappedBounds.getHeight());
612            if (index == 0) {
613                EXPECT_EQ(Rect(10, 10), state.computedState.clippedBounds);
614            } else if (index == 1) {
615                EXPECT_EQ(Rect(190, 0, 200, 10), state.computedState.clippedBounds);
616            } else if (index == 2) {
617                EXPECT_EQ(Rect(0, 190, 10, 200), state.computedState.clippedBounds);
618            } else if (index == 3) {
619                EXPECT_EQ(Rect(190, 190, 200, 200), state.computedState.clippedBounds);
620            }
621        }
622        void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override {
623            EXPECT_EQ(4, mIndex++);
624            ASSERT_EQ(op.vertexCount, 16u);
625            for (size_t i = 0; i < op.vertexCount; i++) {
626                auto v = op.vertices[i];
627                EXPECT_TRUE(v.x == 0 || v.x == 10 || v.x == 190 || v.x == 200);
628                EXPECT_TRUE(v.y == 0 || v.y == 10 || v.y == 190 || v.y == 200);
629            }
630        }
631        void onRectOp(const RectOp& op, const BakedOpState& state) override {
632            EXPECT_EQ(5, mIndex++);
633        }
634        void onCopyFromLayerOp(const CopyFromLayerOp& op, const BakedOpState& state) override {
635            EXPECT_LT(5, mIndex++);
636        }
637    };
638
639    auto node = TestUtils::createNode(0, 0, 200, 200,
640            [](RenderProperties& props, RecordingCanvas& canvas) {
641
642        int restoreTo = canvas.save(SkCanvas::kMatrixClip_SaveFlag);
643        canvas.scale(2, 2);
644        canvas.saveLayerAlpha(0, 0, 5, 5, 128, SkCanvas::kMatrixClip_SaveFlag);
645        canvas.saveLayerAlpha(95, 0, 100, 5, 128, SkCanvas::kMatrixClip_SaveFlag);
646        canvas.saveLayerAlpha(0, 95, 5, 100, 128, SkCanvas::kMatrixClip_SaveFlag);
647        canvas.saveLayerAlpha(95, 95, 100, 100, 128, SkCanvas::kMatrixClip_SaveFlag);
648        canvas.drawRect(0, 0, 100, 100, SkPaint());
649        canvas.restoreToCount(restoreTo);
650    });
651    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
652            createSyncedNodeList(node), sLightCenter);
653    SaveLayerUnclippedMergedClearsTestRenderer renderer;
654    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
655    EXPECT_EQ(10, renderer.getIndex())
656            << "Expect 4 copyTos, 4 copyFroms, 1 clear SimpleRects, and 1 rect.";
657}
658
659/* saveLayerUnclipped { saveLayer { saveLayerUnclipped { rect } } } will play back as:
660 * - startTemporaryLayer, onCopyToLayer, onSimpleRects, onRect, onCopyFromLayer, endLayer
661 * - startFrame, onCopyToLayer, onSimpleRects, drawLayer, onCopyFromLayer, endframe
662 */
663TEST(FrameBuilder, saveLayerUnclipped_complex) {
664    class SaveLayerUnclippedComplexTestRenderer : public TestRendererBase {
665    public:
666        OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) {
667            EXPECT_EQ(0, mIndex++); // savelayer first
668            return (OffscreenBuffer*)0xabcd;
669        }
670        void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override {
671            int index = mIndex++;
672            EXPECT_TRUE(index == 1 || index == 7);
673        }
674        void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override {
675            int index = mIndex++;
676            EXPECT_TRUE(index == 2 || index == 8);
677        }
678        void onRectOp(const RectOp& op, const BakedOpState& state) override {
679            EXPECT_EQ(3, mIndex++);
680            Matrix4 expected;
681            expected.loadTranslate(-100, -100, 0);
682            EXPECT_EQ(Rect(100, 100, 200, 200), state.computedState.clippedBounds);
683            EXPECT_MATRIX_APPROX_EQ(expected, state.computedState.transform);
684        }
685        void onCopyFromLayerOp(const CopyFromLayerOp& op, const BakedOpState& state) override {
686            int index = mIndex++;
687            EXPECT_TRUE(index == 4 || index == 10);
688        }
689        void endLayer() override {
690            EXPECT_EQ(5, mIndex++);
691        }
692        void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
693            EXPECT_EQ(6, mIndex++);
694        }
695        void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
696            EXPECT_EQ(9, mIndex++);
697        }
698        void endFrame(const Rect& repaintRect) override {
699            EXPECT_EQ(11, mIndex++);
700        }
701    };
702
703    auto node = TestUtils::createNode(0, 0, 600, 600, // 500x500 triggers clipping
704            [](RenderProperties& props, RecordingCanvas& canvas) {
705        canvas.saveLayerAlpha(0, 0, 500, 500, 128, (SkCanvas::SaveFlags)0); // unclipped
706        canvas.saveLayerAlpha(100, 100, 400, 400, 128, SkCanvas::kClipToLayer_SaveFlag); // clipped
707        canvas.saveLayerAlpha(200, 200, 300, 300, 128, (SkCanvas::SaveFlags)0); // unclipped
708        canvas.drawRect(200, 200, 300, 300, SkPaint());
709        canvas.restore();
710        canvas.restore();
711        canvas.restore();
712    });
713    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(600, 600), 600, 600,
714            createSyncedNodeList(node), sLightCenter);
715    SaveLayerUnclippedComplexTestRenderer renderer;
716    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
717    EXPECT_EQ(12, renderer.getIndex());
718}
719
720RENDERTHREAD_TEST(FrameBuilder, hwLayer_simple) {
721    class HwLayerSimpleTestRenderer : public TestRendererBase {
722    public:
723        void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override {
724            EXPECT_EQ(0, mIndex++);
725            EXPECT_EQ(100u, offscreenBuffer->viewportWidth);
726            EXPECT_EQ(100u, offscreenBuffer->viewportHeight);
727            EXPECT_EQ(Rect(25, 25, 75, 75), repaintRect);
728        }
729        void onRectOp(const RectOp& op, const BakedOpState& state) override {
730            EXPECT_EQ(1, mIndex++);
731
732            EXPECT_TRUE(state.computedState.transform.isIdentity())
733                    << "Transform should be reset within layer";
734
735            EXPECT_EQ(Rect(25, 25, 75, 75), state.computedState.clipRect())
736                    << "Damage rect should be used to clip layer content";
737        }
738        void endLayer() override {
739            EXPECT_EQ(2, mIndex++);
740        }
741        void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
742            EXPECT_EQ(3, mIndex++);
743        }
744        void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
745            EXPECT_EQ(4, mIndex++);
746        }
747        void endFrame(const Rect& repaintRect) override {
748            EXPECT_EQ(5, mIndex++);
749        }
750    };
751
752    auto node = TestUtils::createNode(10, 10, 110, 110,
753            [](RenderProperties& props, RecordingCanvas& canvas) {
754        props.mutateLayerProperties().setType(LayerType::RenderLayer);
755        SkPaint paint;
756        paint.setColor(SK_ColorWHITE);
757        canvas.drawRect(0, 0, 100, 100, paint);
758    });
759    OffscreenBuffer** layerHandle = node->getLayerHandle();
760
761    // create RenderNode's layer here in same way prepareTree would
762    OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 100, 100);
763    *layerHandle = &layer;
764
765    auto syncedNodeList = createSyncedNodeList(node);
766
767    // only enqueue partial damage
768    LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
769    layerUpdateQueue.enqueueLayerWithDamage(node.get(), Rect(25, 25, 75, 75));
770
771    FrameBuilder frameBuilder(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
772            syncedNodeList, sLightCenter);
773    HwLayerSimpleTestRenderer renderer;
774    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
775    EXPECT_EQ(6, renderer.getIndex());
776
777    // clean up layer pointer, so we can safely destruct RenderNode
778    *layerHandle = nullptr;
779}
780
781RENDERTHREAD_TEST(FrameBuilder, hwLayer_complex) {
782    /* parentLayer { greyRect, saveLayer { childLayer { whiteRect } } } will play back as:
783     * - startRepaintLayer(child), rect(grey), endLayer
784     * - startTemporaryLayer, drawLayer(child), endLayer
785     * - startRepaintLayer(parent), rect(white), drawLayer(saveLayer), endLayer
786     * - startFrame, drawLayer(parent), endLayerb
787     */
788    class HwLayerComplexTestRenderer : public TestRendererBase {
789    public:
790        OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) {
791            EXPECT_EQ(3, mIndex++); // savelayer first
792            return (OffscreenBuffer*)0xabcd;
793        }
794        void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override {
795            int index = mIndex++;
796            if (index == 0) {
797                // starting inner layer
798                EXPECT_EQ(100u, offscreenBuffer->viewportWidth);
799                EXPECT_EQ(100u, offscreenBuffer->viewportHeight);
800            } else if (index == 6) {
801                // starting outer layer
802                EXPECT_EQ(200u, offscreenBuffer->viewportWidth);
803                EXPECT_EQ(200u, offscreenBuffer->viewportHeight);
804            } else { ADD_FAILURE(); }
805        }
806        void onRectOp(const RectOp& op, const BakedOpState& state) override {
807            int index = mIndex++;
808            if (index == 1) {
809                // inner layer's rect (white)
810                EXPECT_EQ(SK_ColorWHITE, op.paint->getColor());
811            } else if (index == 7) {
812                // outer layer's rect (grey)
813                EXPECT_EQ(SK_ColorDKGRAY, op.paint->getColor());
814            } else { ADD_FAILURE(); }
815        }
816        void endLayer() override {
817            int index = mIndex++;
818            EXPECT_TRUE(index == 2 || index == 5 || index == 9);
819        }
820        void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
821            EXPECT_EQ(10, mIndex++);
822        }
823        void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
824            OffscreenBuffer* layer = *op.layerHandle;
825            int index = mIndex++;
826            if (index == 4) {
827                EXPECT_EQ(100u, layer->viewportWidth);
828                EXPECT_EQ(100u, layer->viewportHeight);
829            } else if (index == 8) {
830                EXPECT_EQ((OffscreenBuffer*)0xabcd, *op.layerHandle);
831            } else if (index == 11) {
832                EXPECT_EQ(200u, layer->viewportWidth);
833                EXPECT_EQ(200u, layer->viewportHeight);
834            } else { ADD_FAILURE(); }
835        }
836        void endFrame(const Rect& repaintRect) override {
837            EXPECT_EQ(12, mIndex++);
838        }
839    };
840
841    auto child = TestUtils::createNode(50, 50, 150, 150,
842            [](RenderProperties& props, RecordingCanvas& canvas) {
843        props.mutateLayerProperties().setType(LayerType::RenderLayer);
844        SkPaint paint;
845        paint.setColor(SK_ColorWHITE);
846        canvas.drawRect(0, 0, 100, 100, paint);
847    });
848    OffscreenBuffer childLayer(renderThread.renderState(), Caches::getInstance(), 100, 100);
849    *(child->getLayerHandle()) = &childLayer;
850
851    RenderNode* childPtr = child.get();
852    auto parent = TestUtils::createNode(0, 0, 200, 200,
853            [childPtr](RenderProperties& props, RecordingCanvas& canvas) {
854        props.mutateLayerProperties().setType(LayerType::RenderLayer);
855        SkPaint paint;
856        paint.setColor(SK_ColorDKGRAY);
857        canvas.drawRect(0, 0, 200, 200, paint);
858
859        canvas.saveLayerAlpha(50, 50, 150, 150, 128, SkCanvas::kClipToLayer_SaveFlag);
860        canvas.drawRenderNode(childPtr);
861        canvas.restore();
862    });
863    OffscreenBuffer parentLayer(renderThread.renderState(), Caches::getInstance(), 200, 200);
864    *(parent->getLayerHandle()) = &parentLayer;
865
866    auto syncedList = createSyncedNodeList(parent);
867
868    LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
869    layerUpdateQueue.enqueueLayerWithDamage(child.get(), Rect(100, 100));
870    layerUpdateQueue.enqueueLayerWithDamage(parent.get(), Rect(200, 200));
871
872    FrameBuilder frameBuilder(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
873            syncedList, sLightCenter);
874    HwLayerComplexTestRenderer renderer;
875    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
876    EXPECT_EQ(13, renderer.getIndex());
877
878    // clean up layer pointers, so we can safely destruct RenderNodes
879    *(child->getLayerHandle()) = nullptr;
880    *(parent->getLayerHandle()) = nullptr;
881}
882
883static void drawOrderedRect(RecordingCanvas* canvas, uint8_t expectedDrawOrder) {
884    SkPaint paint;
885    paint.setColor(SkColorSetARGB(256, 0, 0, expectedDrawOrder)); // order put in blue channel
886    canvas->drawRect(0, 0, 100, 100, paint);
887}
888static void drawOrderedNode(RecordingCanvas* canvas, uint8_t expectedDrawOrder, float z) {
889    auto node = TestUtils::createNode(0, 0, 100, 100,
890            [expectedDrawOrder](RenderProperties& props, RecordingCanvas& canvas) {
891        drawOrderedRect(&canvas, expectedDrawOrder);
892    });
893    node->mutateStagingProperties().setTranslationZ(z);
894    node->setPropertyFieldsDirty(RenderNode::TRANSLATION_Z);
895    canvas->drawRenderNode(node.get()); // canvas takes reference/sole ownership
896}
897TEST(FrameBuilder, zReorder) {
898    class ZReorderTestRenderer : public TestRendererBase {
899    public:
900        void onRectOp(const RectOp& op, const BakedOpState& state) override {
901            int expectedOrder = SkColorGetB(op.paint->getColor()); // extract order from blue channel
902            EXPECT_EQ(expectedOrder, mIndex++) << "An op was drawn out of order";
903        }
904    };
905
906    auto parent = TestUtils::createNode(0, 0, 100, 100,
907            [](RenderProperties& props, RecordingCanvas& canvas) {
908        drawOrderedNode(&canvas, 0, 10.0f); // in reorder=false at this point, so played inorder
909        drawOrderedRect(&canvas, 1);
910        canvas.insertReorderBarrier(true);
911        drawOrderedNode(&canvas, 6, 2.0f);
912        drawOrderedRect(&canvas, 3);
913        drawOrderedNode(&canvas, 4, 0.0f);
914        drawOrderedRect(&canvas, 5);
915        drawOrderedNode(&canvas, 2, -2.0f);
916        drawOrderedNode(&canvas, 7, 2.0f);
917        canvas.insertReorderBarrier(false);
918        drawOrderedRect(&canvas, 8);
919        drawOrderedNode(&canvas, 9, -10.0f); // in reorder=false at this point, so played inorder
920    });
921    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100,
922            createSyncedNodeList(parent), sLightCenter);
923    ZReorderTestRenderer renderer;
924    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
925    EXPECT_EQ(10, renderer.getIndex());
926};
927
928TEST(FrameBuilder, projectionReorder) {
929    static const int scrollX = 5;
930    static const int scrollY = 10;
931    class ProjectionReorderTestRenderer : public TestRendererBase {
932    public:
933        void onRectOp(const RectOp& op, const BakedOpState& state) override {
934            const int index = mIndex++;
935
936            Matrix4 expectedMatrix;
937            switch (index) {
938            case 0:
939                EXPECT_EQ(Rect(100, 100), op.unmappedBounds);
940                EXPECT_EQ(SK_ColorWHITE, op.paint->getColor());
941                expectedMatrix.loadIdentity();
942                break;
943            case 1:
944                EXPECT_EQ(Rect(-10, -10, 60, 60), op.unmappedBounds);
945                EXPECT_EQ(SK_ColorDKGRAY, op.paint->getColor());
946                expectedMatrix.loadTranslate(50, 50, 0); // TODO: should scroll be respected here?
947                break;
948            case 2:
949                EXPECT_EQ(Rect(100, 50), op.unmappedBounds);
950                EXPECT_EQ(SK_ColorBLUE, op.paint->getColor());
951                expectedMatrix.loadTranslate(-scrollX, 50 - scrollY, 0);
952                break;
953            default:
954                ADD_FAILURE();
955            }
956            EXPECT_MATRIX_APPROX_EQ(expectedMatrix, state.computedState.transform);
957        }
958    };
959
960    /**
961     * Construct a tree of nodes, where the root (A) has a receiver background (B), and a child (C)
962     * with a projecting child (P) of its own. P would normally draw between B and C's "background"
963     * draw, but because it is projected backwards, it's drawn in between B and C.
964     *
965     * The parent is scrolled by scrollX/scrollY, but this does not affect the background
966     * (which isn't affected by scroll).
967     */
968    auto receiverBackground = TestUtils::createNode(0, 0, 100, 100,
969            [](RenderProperties& properties, RecordingCanvas& canvas) {
970        properties.setProjectionReceiver(true);
971        // scroll doesn't apply to background, so undone via translationX/Y
972        // NOTE: translationX/Y only! no other transform properties may be set for a proj receiver!
973        properties.setTranslationX(scrollX);
974        properties.setTranslationY(scrollY);
975
976        SkPaint paint;
977        paint.setColor(SK_ColorWHITE);
978        canvas.drawRect(0, 0, 100, 100, paint);
979    });
980    auto projectingRipple = TestUtils::createNode(50, 0, 100, 50,
981            [](RenderProperties& properties, RecordingCanvas& canvas) {
982        properties.setProjectBackwards(true);
983        properties.setClipToBounds(false);
984        SkPaint paint;
985        paint.setColor(SK_ColorDKGRAY);
986        canvas.drawRect(-10, -10, 60, 60, paint);
987    });
988    auto child = TestUtils::createNode(0, 50, 100, 100,
989            [&projectingRipple](RenderProperties& properties, RecordingCanvas& canvas) {
990        SkPaint paint;
991        paint.setColor(SK_ColorBLUE);
992        canvas.drawRect(0, 0, 100, 50, paint);
993        canvas.drawRenderNode(projectingRipple.get());
994    });
995    auto parent = TestUtils::createNode(0, 0, 100, 100,
996            [&receiverBackground, &child](RenderProperties& properties, RecordingCanvas& canvas) {
997        canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
998        canvas.translate(-scrollX, -scrollY); // Apply scroll (note: bg undoes this internally)
999        canvas.drawRenderNode(receiverBackground.get());
1000        canvas.drawRenderNode(child.get());
1001        canvas.restore();
1002    });
1003
1004    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100,
1005            createSyncedNodeList(parent), sLightCenter);
1006    ProjectionReorderTestRenderer renderer;
1007    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1008    EXPECT_EQ(3, renderer.getIndex());
1009}
1010
1011// creates a 100x100 shadow casting node with provided translationZ
1012static sp<RenderNode> createWhiteRectShadowCaster(float translationZ) {
1013    return TestUtils::createNode(0, 0, 100, 100,
1014            [translationZ](RenderProperties& properties, RecordingCanvas& canvas) {
1015        properties.setTranslationZ(translationZ);
1016        properties.mutableOutline().setRoundRect(0, 0, 100, 100, 0.0f, 1.0f);
1017        SkPaint paint;
1018        paint.setColor(SK_ColorWHITE);
1019        canvas.drawRect(0, 0, 100, 100, paint);
1020    });
1021}
1022
1023TEST(FrameBuilder, shadow) {
1024    class ShadowTestRenderer : public TestRendererBase {
1025    public:
1026        void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
1027            EXPECT_EQ(0, mIndex++);
1028            EXPECT_FLOAT_EQ(1.0f, op.casterAlpha);
1029            EXPECT_TRUE(op.casterPath->isRect(nullptr));
1030            EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), op.shadowMatrixXY);
1031
1032            Matrix4 expectedZ;
1033            expectedZ.loadTranslate(0, 0, 5);
1034            EXPECT_MATRIX_APPROX_EQ(expectedZ, op.shadowMatrixZ);
1035        }
1036        void onRectOp(const RectOp& op, const BakedOpState& state) override {
1037            EXPECT_EQ(1, mIndex++);
1038        }
1039    };
1040
1041    auto parent = TestUtils::createNode(0, 0, 200, 200,
1042            [](RenderProperties& props, RecordingCanvas& canvas) {
1043        canvas.insertReorderBarrier(true);
1044        canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
1045    });
1046
1047    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
1048            createSyncedNodeList(parent), sLightCenter);
1049    ShadowTestRenderer renderer;
1050    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1051    EXPECT_EQ(2, renderer.getIndex());
1052}
1053
1054TEST(FrameBuilder, shadowSaveLayer) {
1055    class ShadowSaveLayerTestRenderer : public TestRendererBase {
1056    public:
1057        OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override {
1058            EXPECT_EQ(0, mIndex++);
1059            return nullptr;
1060        }
1061        void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
1062            EXPECT_EQ(1, mIndex++);
1063            EXPECT_FLOAT_EQ(50, op.lightCenter.x);
1064            EXPECT_FLOAT_EQ(40, op.lightCenter.y);
1065        }
1066        void onRectOp(const RectOp& op, const BakedOpState& state) override {
1067            EXPECT_EQ(2, mIndex++);
1068        }
1069        void endLayer() override {
1070            EXPECT_EQ(3, mIndex++);
1071        }
1072        void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
1073            EXPECT_EQ(4, mIndex++);
1074        }
1075    };
1076
1077    auto parent = TestUtils::createNode(0, 0, 200, 200,
1078            [](RenderProperties& props, RecordingCanvas& canvas) {
1079        // save/restore outside of reorderBarrier, so they don't get moved out of place
1080        canvas.translate(20, 10);
1081        int count = canvas.saveLayerAlpha(30, 50, 130, 150, 128, SkCanvas::kClipToLayer_SaveFlag);
1082        canvas.insertReorderBarrier(true);
1083        canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
1084        canvas.insertReorderBarrier(false);
1085        canvas.restoreToCount(count);
1086    });
1087
1088    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
1089            createSyncedNodeList(parent), (Vector3) { 100, 100, 100 });
1090    ShadowSaveLayerTestRenderer renderer;
1091    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1092    EXPECT_EQ(5, renderer.getIndex());
1093}
1094
1095RENDERTHREAD_TEST(FrameBuilder, shadowHwLayer) {
1096    class ShadowHwLayerTestRenderer : public TestRendererBase {
1097    public:
1098        void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override {
1099            EXPECT_EQ(0, mIndex++);
1100        }
1101        void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
1102            EXPECT_EQ(1, mIndex++);
1103            EXPECT_FLOAT_EQ(50, op.lightCenter.x);
1104            EXPECT_FLOAT_EQ(40, op.lightCenter.y);
1105        }
1106        void onRectOp(const RectOp& op, const BakedOpState& state) override {
1107            EXPECT_EQ(2, mIndex++);
1108        }
1109        void endLayer() override {
1110            EXPECT_EQ(3, mIndex++);
1111        }
1112        void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
1113            EXPECT_EQ(4, mIndex++);
1114        }
1115    };
1116
1117    auto parent = TestUtils::createNode(50, 60, 150, 160,
1118            [](RenderProperties& props, RecordingCanvas& canvas) {
1119        props.mutateLayerProperties().setType(LayerType::RenderLayer);
1120        canvas.insertReorderBarrier(true);
1121        canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
1122        canvas.translate(20, 10);
1123        canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
1124        canvas.restore();
1125    });
1126    OffscreenBuffer** layerHandle = parent->getLayerHandle();
1127
1128    // create RenderNode's layer here in same way prepareTree would, setting windowTransform
1129    OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 100, 100);
1130    Matrix4 windowTransform;
1131    windowTransform.loadTranslate(50, 60, 0); // total transform of layer's origin
1132    layer.setWindowTransform(windowTransform);
1133    *layerHandle = &layer;
1134
1135    auto syncedList = createSyncedNodeList(parent);
1136    LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
1137    layerUpdateQueue.enqueueLayerWithDamage(parent.get(), Rect(100, 100));
1138    FrameBuilder frameBuilder(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
1139            syncedList, (Vector3) { 100, 100, 100 });
1140    ShadowHwLayerTestRenderer renderer;
1141    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1142    EXPECT_EQ(5, renderer.getIndex());
1143
1144    // clean up layer pointer, so we can safely destruct RenderNode
1145    *layerHandle = nullptr;
1146}
1147
1148TEST(FrameBuilder, shadowLayering) {
1149    class ShadowLayeringTestRenderer : public TestRendererBase {
1150    public:
1151        void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
1152            int index = mIndex++;
1153            EXPECT_TRUE(index == 0 || index == 1);
1154        }
1155        void onRectOp(const RectOp& op, const BakedOpState& state) override {
1156            int index = mIndex++;
1157            EXPECT_TRUE(index == 2 || index == 3);
1158        }
1159    };
1160    auto parent = TestUtils::createNode(0, 0, 200, 200,
1161            [](RenderProperties& props, RecordingCanvas& canvas) {
1162        canvas.insertReorderBarrier(true);
1163        canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
1164        canvas.drawRenderNode(createWhiteRectShadowCaster(5.0001f).get());
1165    });
1166
1167    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
1168            createSyncedNodeList(parent), sLightCenter);
1169    ShadowLayeringTestRenderer renderer;
1170    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1171    EXPECT_EQ(4, renderer.getIndex());
1172}
1173
1174static void testProperty(std::function<void(RenderProperties&)> propSetupCallback,
1175        std::function<void(const RectOp&, const BakedOpState&)> opValidateCallback) {
1176    class PropertyTestRenderer : public TestRendererBase {
1177    public:
1178        PropertyTestRenderer(std::function<void(const RectOp&, const BakedOpState&)> callback)
1179                : mCallback(callback) {}
1180        void onRectOp(const RectOp& op, const BakedOpState& state) override {
1181            EXPECT_EQ(mIndex++, 0);
1182            mCallback(op, state);
1183        }
1184        std::function<void(const RectOp&, const BakedOpState&)> mCallback;
1185    };
1186
1187    auto node = TestUtils::createNode(0, 0, 100, 100,
1188            [propSetupCallback](RenderProperties& props, RecordingCanvas& canvas) {
1189        propSetupCallback(props);
1190        SkPaint paint;
1191        paint.setColor(SK_ColorWHITE);
1192        canvas.drawRect(0, 0, 100, 100, paint);
1193    });
1194
1195    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 200, 200,
1196            createSyncedNodeList(node), sLightCenter);
1197    PropertyTestRenderer renderer(opValidateCallback);
1198    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1199    EXPECT_EQ(1, renderer.getIndex()) << "Should have seen one op";
1200}
1201
1202TEST(FrameBuilder, renderPropOverlappingRenderingAlpha) {
1203    testProperty([](RenderProperties& properties) {
1204        properties.setAlpha(0.5f);
1205        properties.setHasOverlappingRendering(false);
1206    }, [](const RectOp& op, const BakedOpState& state) {
1207        EXPECT_EQ(0.5f, state.alpha) << "Alpha should be applied directly to op";
1208    });
1209}
1210
1211TEST(FrameBuilder, renderPropClipping) {
1212    testProperty([](RenderProperties& properties) {
1213        properties.setClipToBounds(true);
1214        properties.setClipBounds(Rect(10, 20, 300, 400));
1215    }, [](const RectOp& op, const BakedOpState& state) {
1216        EXPECT_EQ(Rect(10, 20, 100, 100), state.computedState.clippedBounds)
1217                << "Clip rect should be intersection of node bounds and clip bounds";
1218    });
1219}
1220
1221TEST(FrameBuilder, renderPropRevealClip) {
1222    testProperty([](RenderProperties& properties) {
1223        properties.mutableRevealClip().set(true, 50, 50, 25);
1224    }, [](const RectOp& op, const BakedOpState& state) {
1225        ASSERT_NE(nullptr, state.roundRectClipState);
1226        EXPECT_TRUE(state.roundRectClipState->highPriority);
1227        EXPECT_EQ(25, state.roundRectClipState->radius);
1228        EXPECT_EQ(Rect(50, 50, 50, 50), state.roundRectClipState->innerRect);
1229    });
1230}
1231
1232TEST(FrameBuilder, renderPropOutlineClip) {
1233    testProperty([](RenderProperties& properties) {
1234        properties.mutableOutline().setShouldClip(true);
1235        properties.mutableOutline().setRoundRect(10, 20, 30, 40, 5.0f, 0.5f);
1236    }, [](const RectOp& op, const BakedOpState& state) {
1237        ASSERT_NE(nullptr, state.roundRectClipState);
1238        EXPECT_FALSE(state.roundRectClipState->highPriority);
1239        EXPECT_EQ(5, state.roundRectClipState->radius);
1240        EXPECT_EQ(Rect(15, 25, 25, 35), state.roundRectClipState->innerRect);
1241    });
1242}
1243
1244TEST(FrameBuilder, renderPropTransform) {
1245    testProperty([](RenderProperties& properties) {
1246        properties.setLeftTopRightBottom(10, 10, 110, 110);
1247
1248        SkMatrix staticMatrix = SkMatrix::MakeScale(1.2f, 1.2f);
1249        properties.setStaticMatrix(&staticMatrix);
1250
1251        // ignored, since static overrides animation
1252        SkMatrix animationMatrix = SkMatrix::MakeTrans(15, 15);
1253        properties.setAnimationMatrix(&animationMatrix);
1254
1255        properties.setTranslationX(10);
1256        properties.setTranslationY(20);
1257        properties.setScaleX(0.5f);
1258        properties.setScaleY(0.7f);
1259    }, [](const RectOp& op, const BakedOpState& state) {
1260        Matrix4 matrix;
1261        matrix.loadTranslate(10, 10, 0); // left, top
1262        matrix.scale(1.2f, 1.2f, 1); // static matrix
1263        // ignore animation matrix, since static overrides it
1264
1265        // translation xy
1266        matrix.translate(10, 20);
1267
1268        // scale xy (from default pivot - center)
1269        matrix.translate(50, 50);
1270        matrix.scale(0.5f, 0.7f, 1);
1271        matrix.translate(-50, -50);
1272        EXPECT_MATRIX_APPROX_EQ(matrix, state.computedState.transform)
1273                << "Op draw matrix must match expected combination of transformation properties";
1274    });
1275}
1276
1277struct SaveLayerAlphaData {
1278    uint32_t layerWidth = 0;
1279    uint32_t layerHeight = 0;
1280    Rect rectClippedBounds;
1281    Matrix4 rectMatrix;
1282};
1283/**
1284 * Constructs a view to hit the temporary layer alpha property implementation:
1285 *     a) 0 < alpha < 1
1286 *     b) too big for layer (larger than maxTextureSize)
1287 *     c) overlapping rendering content
1288 * returning observed data about layer size and content clip/transform.
1289 *
1290 * Used to validate clipping behavior of temporary layer, where requested layer size is reduced
1291 * (for efficiency, and to fit in layer size constraints) based on parent clip.
1292 */
1293void testSaveLayerAlphaClip(SaveLayerAlphaData* outObservedData,
1294        std::function<void(RenderProperties&)> propSetupCallback) {
1295    class SaveLayerAlphaClipTestRenderer : public TestRendererBase {
1296    public:
1297        SaveLayerAlphaClipTestRenderer(SaveLayerAlphaData* outData)
1298                : mOutData(outData) {}
1299
1300        OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override {
1301            EXPECT_EQ(0, mIndex++);
1302            mOutData->layerWidth = width;
1303            mOutData->layerHeight = height;
1304            return nullptr;
1305        }
1306        void onRectOp(const RectOp& op, const BakedOpState& state) override {
1307            EXPECT_EQ(1, mIndex++);
1308
1309            mOutData->rectClippedBounds = state.computedState.clippedBounds;
1310            mOutData->rectMatrix = state.computedState.transform;
1311        }
1312        void endLayer() override {
1313            EXPECT_EQ(2, mIndex++);
1314        }
1315        void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
1316            EXPECT_EQ(3, mIndex++);
1317        }
1318    private:
1319        SaveLayerAlphaData* mOutData;
1320    };
1321
1322    ASSERT_GT(10000, DeviceInfo::get()->maxTextureSize())
1323            << "Node must be bigger than max texture size to exercise saveLayer codepath";
1324    auto node = TestUtils::createNode(0, 0, 10000, 10000,
1325            [&propSetupCallback](RenderProperties& properties, RecordingCanvas& canvas) {
1326        properties.setHasOverlappingRendering(true);
1327        properties.setAlpha(0.5f); // force saveLayer, since too big for HW layer
1328        // apply other properties
1329        propSetupCallback(properties);
1330
1331        SkPaint paint;
1332        paint.setColor(SK_ColorWHITE);
1333        canvas.drawRect(0, 0, 10000, 10000, paint);
1334    });
1335    auto nodes = createSyncedNodeList(node); // sync before querying height
1336
1337    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, nodes, sLightCenter);
1338    SaveLayerAlphaClipTestRenderer renderer(outObservedData);
1339    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1340
1341    // assert, since output won't be valid if we haven't seen a save layer triggered
1342    ASSERT_EQ(4, renderer.getIndex()) << "Test must trigger saveLayer alpha behavior.";
1343}
1344
1345TEST(FrameBuilder, renderPropSaveLayerAlphaClipBig) {
1346    SaveLayerAlphaData observedData;
1347    testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) {
1348        properties.setTranslationX(10); // offset rendering content
1349        properties.setTranslationY(-2000); // offset rendering content
1350    });
1351    EXPECT_EQ(190u, observedData.layerWidth);
1352    EXPECT_EQ(200u, observedData.layerHeight);
1353    EXPECT_EQ(Rect(190, 200), observedData.rectClippedBounds)
1354            << "expect content to be clipped to screen area";
1355    Matrix4 expected;
1356    expected.loadTranslate(0, -2000, 0);
1357    EXPECT_MATRIX_APPROX_EQ(expected, observedData.rectMatrix)
1358            << "expect content to be translated as part of being clipped";
1359}
1360
1361TEST(FrameBuilder, renderPropSaveLayerAlphaRotate) {
1362    SaveLayerAlphaData observedData;
1363    testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) {
1364        // Translate and rotate the view so that the only visible part is the top left corner of
1365        // the view. It will form an isosceles right triangle with a long side length of 200 at the
1366        // bottom of the viewport.
1367        properties.setTranslationX(100);
1368        properties.setTranslationY(100);
1369        properties.setPivotX(0);
1370        properties.setPivotY(0);
1371        properties.setRotation(45);
1372    });
1373    // ceil(sqrt(2) / 2 * 200) = 142
1374    EXPECT_EQ(142u, observedData.layerWidth);
1375    EXPECT_EQ(142u, observedData.layerHeight);
1376    EXPECT_EQ(Rect(142, 142), observedData.rectClippedBounds);
1377    EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), observedData.rectMatrix);
1378}
1379
1380TEST(FrameBuilder, renderPropSaveLayerAlphaScale) {
1381    SaveLayerAlphaData observedData;
1382    testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) {
1383        properties.setPivotX(0);
1384        properties.setPivotY(0);
1385        properties.setScaleX(2);
1386        properties.setScaleY(0.5f);
1387    });
1388    EXPECT_EQ(100u, observedData.layerWidth);
1389    EXPECT_EQ(400u, observedData.layerHeight);
1390    EXPECT_EQ(Rect(100, 400), observedData.rectClippedBounds);
1391    EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), observedData.rectMatrix);
1392}
1393
1394} // namespace uirenderer
1395} // namespace android
1396