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