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