FrameBuilderTests.cpp revision 49658d43802dc74c0ad68b7f5ba509832c1dec81
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 FrameBuilder::LightGeometry sLightGeometry = { {100, 100, 100}, 50};
34
35
36/**
37 * Virtual class implemented by each test to redirect static operation / state transitions to
38 * virtual methods.
39 *
40 * Virtual dispatch allows for default behaviors to be specified (very common case in below tests),
41 * and allows Renderer vs Dispatching behavior to be merged.
42 *
43 * onXXXOp methods fail by default - tests should override ops they expect
44 * startRepaintLayer fails by default - tests should override if expected
45 * startFrame/endFrame do nothing by default - tests should override to intercept
46 */
47class TestRendererBase {
48public:
49    virtual ~TestRendererBase() {}
50    virtual OffscreenBuffer* startTemporaryLayer(uint32_t, uint32_t) {
51        ADD_FAILURE() << "Layer creation not expected in this test";
52        return nullptr;
53    }
54    virtual void startRepaintLayer(OffscreenBuffer*, const Rect& repaintRect) {
55        ADD_FAILURE() << "Layer repaint not expected in this test";
56    }
57    virtual void endLayer() {
58        ADD_FAILURE() << "Layer updates not expected in this test";
59    }
60    virtual void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) {}
61    virtual void endFrame(const Rect& repaintRect) {}
62
63    // define virtual defaults for single draw methods
64#define X(Type) \
65    virtual void on##Type(const Type&, const BakedOpState&) { \
66        ADD_FAILURE() << #Type " not expected in this test"; \
67    }
68    MAP_RENDERABLE_OPS(X)
69#undef X
70
71    // define virtual defaults for merged draw methods
72#define X(Type) \
73    virtual void onMerged##Type##s(const MergedBakedOpList& opList) { \
74        ADD_FAILURE() << "Merged " #Type "s not expected in this test"; \
75    }
76    MAP_MERGEABLE_OPS(X)
77#undef X
78
79    int getIndex() { return mIndex; }
80
81protected:
82    int mIndex = 0;
83};
84
85/**
86 * Dispatches all static methods to similar formed methods on renderer, which fail by default but
87 * are overridden by subclasses per test.
88 */
89class TestDispatcher {
90public:
91    // define single op methods, which redirect to TestRendererBase
92#define X(Type) \
93    static void on##Type(TestRendererBase& renderer, const Type& op, const BakedOpState& state) { \
94        renderer.on##Type(op, state); \
95    }
96    MAP_RENDERABLE_OPS(X);
97#undef X
98
99    // define merged op methods, which redirect to TestRendererBase
100#define X(Type) \
101    static void onMerged##Type##s(TestRendererBase& renderer, const MergedBakedOpList& opList) { \
102        renderer.onMerged##Type##s(opList); \
103    }
104    MAP_MERGEABLE_OPS(X);
105#undef X
106};
107
108class FailRenderer : public TestRendererBase {};
109
110TEST(FrameBuilder, simple) {
111    class SimpleTestRenderer : public TestRendererBase {
112    public:
113        void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
114            EXPECT_EQ(0, mIndex++);
115            EXPECT_EQ(100u, width);
116            EXPECT_EQ(200u, height);
117        }
118        void onRectOp(const RectOp& op, const BakedOpState& state) override {
119            EXPECT_EQ(1, mIndex++);
120        }
121        void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override {
122            EXPECT_EQ(2, mIndex++);
123        }
124        void endFrame(const Rect& repaintRect) override {
125            EXPECT_EQ(3, mIndex++);
126        }
127    };
128
129    auto node = TestUtils::createNode(0, 0, 100, 200,
130            [](RenderProperties& props, RecordingCanvas& canvas) {
131        SkBitmap bitmap = TestUtils::createSkBitmap(25, 25);
132        canvas.drawRect(0, 0, 100, 200, SkPaint());
133        canvas.drawBitmap(bitmap, 10, 10, nullptr);
134    });
135    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200,
136            TestUtils::createSyncedNodeList(node), sLightGeometry, nullptr);
137    SimpleTestRenderer renderer;
138    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
139    EXPECT_EQ(4, renderer.getIndex()); // 2 ops + start + end
140}
141
142TEST(FrameBuilder, simpleStroke) {
143    class SimpleStrokeTestRenderer : public TestRendererBase {
144    public:
145        void onPointsOp(const PointsOp& op, const BakedOpState& state) override {
146            EXPECT_EQ(0, mIndex++);
147            // even though initial bounds are empty...
148            EXPECT_TRUE(op.unmappedBounds.isEmpty())
149                    << "initial bounds should be empty, since they're unstroked";
150            EXPECT_EQ(Rect(45, 45, 55, 55), state.computedState.clippedBounds)
151                    << "final bounds should account for stroke";
152        }
153    };
154
155    auto node = TestUtils::createNode(0, 0, 100, 200,
156            [](RenderProperties& props, RecordingCanvas& canvas) {
157        SkPaint strokedPaint;
158        strokedPaint.setStrokeWidth(10);
159        canvas.drawPoint(50, 50, strokedPaint);
160    });
161    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200,
162            TestUtils::createSyncedNodeList(node), sLightGeometry, nullptr);
163    SimpleStrokeTestRenderer renderer;
164    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
165    EXPECT_EQ(1, renderer.getIndex());
166}
167
168TEST(FrameBuilder, simpleRejection) {
169    auto node = TestUtils::createNode(0, 0, 200, 200,
170            [](RenderProperties& props, RecordingCanvas& canvas) {
171        canvas.save(SaveFlags::MatrixClip);
172        canvas.clipRect(200, 200, 400, 400, SkRegion::kIntersect_Op); // intersection should be empty
173        canvas.drawRect(0, 0, 400, 400, SkPaint());
174        canvas.restore();
175    });
176    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
177            TestUtils::createSyncedNodeList(node), sLightGeometry, nullptr);
178
179    FailRenderer renderer;
180    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
181}
182
183TEST(FrameBuilder, simpleBatching) {
184    const int LOOPS = 5;
185    class SimpleBatchingTestRenderer : public TestRendererBase {
186    public:
187        void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override {
188            EXPECT_TRUE(mIndex++ >= LOOPS) << "Bitmaps should be above all rects";
189        }
190        void onRectOp(const RectOp& op, const BakedOpState& state) override {
191            EXPECT_TRUE(mIndex++ < LOOPS) << "Rects should be below all bitmaps";
192        }
193    };
194
195    auto node = TestUtils::createNode(0, 0, 200, 200,
196            [](RenderProperties& props, RecordingCanvas& canvas) {
197        SkBitmap bitmap = TestUtils::createSkBitmap(10, 10,
198                kAlpha_8_SkColorType); // Disable merging by using alpha 8 bitmap
199
200        // Alternate between drawing rects and bitmaps, with bitmaps overlapping rects.
201        // Rects don't overlap bitmaps, so bitmaps should be brought to front as a group.
202        canvas.save(SaveFlags::MatrixClip);
203        for (int i = 0; i < LOOPS; i++) {
204            canvas.translate(0, 10);
205            canvas.drawRect(0, 0, 10, 10, SkPaint());
206            canvas.drawBitmap(bitmap, 5, 0, nullptr);
207        }
208        canvas.restore();
209    });
210
211    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
212            TestUtils::createSyncedNodeList(node), sLightGeometry, nullptr);
213    SimpleBatchingTestRenderer renderer;
214    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
215    EXPECT_EQ(2 * LOOPS, renderer.getIndex())
216            << "Expect number of ops = 2 * loop count";
217}
218
219TEST(FrameBuilder, 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), sLightGeometry, nullptr);
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::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 0); // will be top clipped
278        TestUtils::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 100); // not clipped
279    });
280    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(400, 400), 400, 400,
281            TestUtils::createSyncedNodeList(node), sLightGeometry, nullptr);
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::drawUtf8ToCanvas(&canvas, "test text", textPaint, 10, 100 * (i + 1));
309        }
310    });
311    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 2000), 200, 2000,
312            TestUtils::createSyncedNodeList(node), sLightGeometry, nullptr);
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
319static auto styles = {
320        SkPaint::kFill_Style, SkPaint::kStroke_Style, SkPaint::kStrokeAndFill_Style };
321
322TEST(FrameBuilder, textStyle) {
323    class TextStyleTestRenderer : public TestRendererBase {
324    public:
325        void onMergedTextOps(const MergedBakedOpList& opList) override {
326            ASSERT_EQ(0, mIndex);
327            ASSERT_EQ(3u, opList.count);
328            mIndex += opList.count;
329
330            int index = 0;
331            for (auto style : styles) {
332                auto state = opList.states[index++];
333                ASSERT_EQ(style, state->op->paint->getStyle())
334                        << "Remainder of validation relies upon stable merged order";
335                ASSERT_EQ(0, state->computedState.clipSideFlags)
336                        << "Clipped bounds validation requires unclipped ops";
337            }
338
339            Rect fill = opList.states[0]->computedState.clippedBounds;
340            Rect stroke = opList.states[1]->computedState.clippedBounds;
341            EXPECT_EQ(stroke, opList.states[2]->computedState.clippedBounds)
342                    << "Stroke+Fill should be same as stroke";
343
344            EXPECT_TRUE(stroke.contains(fill));
345            EXPECT_FALSE(fill.contains(stroke));
346
347            Rect outsetFill(fill);
348            outsetFill.outset(10);
349            EXPECT_EQ(stroke, outsetFill);
350        }
351    };
352    auto node = TestUtils::createNode(0, 0, 400, 400,
353            [](RenderProperties& props, TestCanvas& canvas) {
354        SkPaint paint;
355        paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
356        paint.setAntiAlias(true);
357        paint.setTextSize(50);
358        paint.setStrokeWidth(10);
359
360        // draw 3 copies of the same text overlapping, each with a different style.
361        // They'll get merged, but with
362        for (auto style : styles) {
363            paint.setStyle(style);
364            TestUtils::drawTextToCanvas(&canvas, "Test string1", paint, 100, 100);
365        }
366    });
367    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(400, 400), 400, 400,
368            TestUtils::createSyncedNodeList(node), sLightGeometry, nullptr);
369    TextStyleTestRenderer renderer;
370    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
371    EXPECT_EQ(3, renderer.getIndex()) << "Expect 3 ops";
372}
373
374RENDERTHREAD_TEST(FrameBuilder, textureLayer) {
375    class TextureLayerTestRenderer : public TestRendererBase {
376    public:
377        void onTextureLayerOp(const TextureLayerOp& op, const BakedOpState& state) override {
378            EXPECT_EQ(0, mIndex++);
379            EXPECT_EQ(Rect(50, 50, 150, 150), state.computedState.clipRect());
380            EXPECT_EQ(Rect(50, 50, 105, 105), state.computedState.clippedBounds);
381
382            Matrix4 expected;
383            expected.loadTranslate(5, 5, 0);
384            EXPECT_MATRIX_APPROX_EQ(expected, state.computedState.transform);
385        }
386    };
387
388    auto layerUpdater = TestUtils::createTextureLayerUpdater(renderThread, 100, 100,
389            [](Matrix4* transform) {
390        transform->loadTranslate(5, 5, 0);
391    });
392
393    auto node = TestUtils::createNode(0, 0, 200, 200,
394            [&layerUpdater](RenderProperties& props, RecordingCanvas& canvas) {
395        canvas.save(SaveFlags::MatrixClip);
396        canvas.clipRect(50, 50, 150, 150, SkRegion::kIntersect_Op);
397        canvas.drawLayer(layerUpdater.get());
398        canvas.restore();
399    });
400    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
401            TestUtils::createSyncedNodeList(node), sLightGeometry, nullptr);
402    TextureLayerTestRenderer renderer;
403    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
404    EXPECT_EQ(1, renderer.getIndex());
405}
406
407TEST(FrameBuilder, functor_reject) {
408    class FunctorTestRenderer : public TestRendererBase {
409    public:
410        void onFunctorOp(const FunctorOp& op, const BakedOpState& state) override {
411            EXPECT_EQ(0, mIndex++);
412        }
413    };
414    Functor noopFunctor;
415
416    // 1 million pixel tall view, scrolled down 80%
417    auto scrolledFunctorView = TestUtils::createNode(0, 0, 400, 1000000,
418            [&noopFunctor](RenderProperties& props, RecordingCanvas& canvas) {
419        canvas.translate(0, -800000);
420        canvas.callDrawGLFunction(&noopFunctor);
421    });
422
423    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
424            TestUtils::createSyncedNodeList(scrolledFunctorView), sLightGeometry, nullptr);
425    FunctorTestRenderer renderer;
426    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
427    EXPECT_EQ(1, renderer.getIndex()) << "Functor should not be rejected";
428}
429
430TEST(FrameBuilder, renderNode) {
431    class RenderNodeTestRenderer : public TestRendererBase {
432    public:
433        void onRectOp(const RectOp& op, const BakedOpState& state) override {
434            switch(mIndex++) {
435            case 0:
436                EXPECT_EQ(Rect(200, 200), state.computedState.clippedBounds);
437                EXPECT_EQ(SK_ColorDKGRAY, op.paint->getColor());
438                break;
439            case 1:
440                EXPECT_EQ(Rect(50, 50, 150, 150), state.computedState.clippedBounds);
441                EXPECT_EQ(SK_ColorWHITE, op.paint->getColor());
442                break;
443            default:
444                ADD_FAILURE();
445            }
446        }
447    };
448
449    auto child = TestUtils::createNode(10, 10, 110, 110,
450            [](RenderProperties& props, RecordingCanvas& canvas) {
451        SkPaint paint;
452        paint.setColor(SK_ColorWHITE);
453        canvas.drawRect(0, 0, 100, 100, paint);
454    });
455
456    auto parent = TestUtils::createNode(0, 0, 200, 200,
457            [&child](RenderProperties& props, RecordingCanvas& canvas) {
458        SkPaint paint;
459        paint.setColor(SK_ColorDKGRAY);
460        canvas.drawRect(0, 0, 200, 200, paint);
461
462        canvas.save(SaveFlags::MatrixClip);
463        canvas.translate(40, 40);
464        canvas.drawRenderNode(child.get());
465        canvas.restore();
466    });
467
468    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
469            TestUtils::createSyncedNodeList(parent), sLightGeometry, nullptr);
470    RenderNodeTestRenderer renderer;
471    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
472    EXPECT_EQ(2, renderer.getIndex());
473}
474
475TEST(FrameBuilder, clipped) {
476    class ClippedTestRenderer : public TestRendererBase {
477    public:
478        void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override {
479            EXPECT_EQ(0, mIndex++);
480            EXPECT_EQ(Rect(10, 20, 30, 40), state.computedState.clippedBounds);
481            EXPECT_EQ(Rect(10, 20, 30, 40), state.computedState.clipRect());
482            EXPECT_TRUE(state.computedState.transform.isIdentity());
483        }
484    };
485
486    auto node = TestUtils::createNode(0, 0, 200, 200,
487            [](RenderProperties& props, RecordingCanvas& canvas) {
488        SkBitmap bitmap = TestUtils::createSkBitmap(200, 200);
489        canvas.drawBitmap(bitmap, 0, 0, nullptr);
490    });
491
492    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue,
493            SkRect::MakeLTRB(10, 20, 30, 40), // clip to small area, should see in receiver
494            200, 200, TestUtils::createSyncedNodeList(node), sLightGeometry, nullptr);
495    ClippedTestRenderer renderer;
496    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
497}
498
499TEST(FrameBuilder, saveLayer_simple) {
500    class SaveLayerSimpleTestRenderer : public TestRendererBase {
501    public:
502        OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override {
503            EXPECT_EQ(0, mIndex++);
504            EXPECT_EQ(180u, width);
505            EXPECT_EQ(180u, height);
506            return nullptr;
507        }
508        void endLayer() override {
509            EXPECT_EQ(2, mIndex++);
510        }
511        void onRectOp(const RectOp& op, const BakedOpState& state) override {
512            EXPECT_EQ(1, mIndex++);
513            EXPECT_EQ(Rect(10, 10, 190, 190), op.unmappedBounds);
514            EXPECT_EQ(Rect(180, 180), state.computedState.clippedBounds);
515            EXPECT_EQ(Rect(180, 180), state.computedState.clipRect());
516
517            Matrix4 expectedTransform;
518            expectedTransform.loadTranslate(-10, -10, 0);
519            EXPECT_MATRIX_APPROX_EQ(expectedTransform, state.computedState.transform);
520        }
521        void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
522            EXPECT_EQ(3, mIndex++);
523            EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds);
524            EXPECT_EQ(Rect(200, 200), state.computedState.clipRect());
525            EXPECT_TRUE(state.computedState.transform.isIdentity());
526        }
527    };
528
529    auto node = TestUtils::createNode(0, 0, 200, 200,
530            [](RenderProperties& props, RecordingCanvas& canvas) {
531        canvas.saveLayerAlpha(10, 10, 190, 190, 128, SaveFlags::ClipToLayer);
532        canvas.drawRect(10, 10, 190, 190, SkPaint());
533        canvas.restore();
534    });
535    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
536            TestUtils::createSyncedNodeList(node), sLightGeometry, nullptr);
537    SaveLayerSimpleTestRenderer renderer;
538    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
539    EXPECT_EQ(4, renderer.getIndex());
540}
541
542TEST(FrameBuilder, saveLayer_nested) {
543    /* saveLayer1 { rect1, saveLayer2 { rect2 } } will play back as:
544     * - startTemporaryLayer2, rect2 endLayer2
545     * - startTemporaryLayer1, rect1, drawLayer2, endLayer1
546     * - startFrame, layerOp1, endFrame
547     */
548    class SaveLayerNestedTestRenderer : public TestRendererBase {
549    public:
550        OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override {
551            const int index = mIndex++;
552            if (index == 0) {
553                EXPECT_EQ(400u, width);
554                EXPECT_EQ(400u, height);
555                return (OffscreenBuffer*) 0x400;
556            } else if (index == 3) {
557                EXPECT_EQ(800u, width);
558                EXPECT_EQ(800u, height);
559                return (OffscreenBuffer*) 0x800;
560            } else { ADD_FAILURE(); }
561            return (OffscreenBuffer*) nullptr;
562        }
563        void endLayer() override {
564            int index = mIndex++;
565            EXPECT_TRUE(index == 2 || index == 6);
566        }
567        void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
568            EXPECT_EQ(7, mIndex++);
569        }
570        void endFrame(const Rect& repaintRect) override {
571            EXPECT_EQ(9, mIndex++);
572        }
573        void onRectOp(const RectOp& op, const BakedOpState& state) override {
574            const int index = mIndex++;
575            if (index == 1) {
576                EXPECT_EQ(Rect(400, 400), op.unmappedBounds); // inner rect
577            } else if (index == 4) {
578                EXPECT_EQ(Rect(800, 800), op.unmappedBounds); // outer rect
579            } else { ADD_FAILURE(); }
580        }
581        void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
582            const int index = mIndex++;
583            if (index == 5) {
584                EXPECT_EQ((OffscreenBuffer*)0x400, *op.layerHandle);
585                EXPECT_EQ(Rect(400, 400), op.unmappedBounds); // inner layer
586            } else if (index == 8) {
587                EXPECT_EQ((OffscreenBuffer*)0x800, *op.layerHandle);
588                EXPECT_EQ(Rect(800, 800), op.unmappedBounds); // outer layer
589            } else { ADD_FAILURE(); }
590        }
591    };
592
593    auto node = TestUtils::createNode(0, 0, 800, 800,
594            [](RenderProperties& props, RecordingCanvas& canvas) {
595        canvas.saveLayerAlpha(0, 0, 800, 800, 128, SaveFlags::ClipToLayer);
596        {
597            canvas.drawRect(0, 0, 800, 800, SkPaint());
598            canvas.saveLayerAlpha(0, 0, 400, 400, 128, SaveFlags::ClipToLayer);
599            {
600                canvas.drawRect(0, 0, 400, 400, SkPaint());
601            }
602            canvas.restore();
603        }
604        canvas.restore();
605    });
606
607    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(800, 800), 800, 800,
608            TestUtils::createSyncedNodeList(node), sLightGeometry, nullptr);
609    SaveLayerNestedTestRenderer renderer;
610    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
611    EXPECT_EQ(10, renderer.getIndex());
612}
613
614TEST(FrameBuilder, saveLayer_contentRejection) {
615        auto node = TestUtils::createNode(0, 0, 200, 200,
616                [](RenderProperties& props, RecordingCanvas& canvas) {
617        canvas.save(SaveFlags::MatrixClip);
618        canvas.clipRect(200, 200, 400, 400, SkRegion::kIntersect_Op);
619        canvas.saveLayerAlpha(200, 200, 400, 400, 128, SaveFlags::ClipToLayer);
620
621        // draw within save layer may still be recorded, but shouldn't be drawn
622        canvas.drawRect(200, 200, 400, 400, SkPaint());
623
624        canvas.restore();
625        canvas.restore();
626    });
627    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
628            TestUtils::createSyncedNodeList(node), sLightGeometry, nullptr);
629
630    FailRenderer renderer;
631    // should see no ops, even within the layer, since the layer should be rejected
632    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
633}
634
635TEST(FrameBuilder, saveLayerUnclipped_simple) {
636    class SaveLayerUnclippedSimpleTestRenderer : public TestRendererBase {
637    public:
638        void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override {
639            EXPECT_EQ(0, mIndex++);
640            EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds);
641            EXPECT_CLIP_RECT(Rect(200, 200), state.computedState.clipState);
642            EXPECT_TRUE(state.computedState.transform.isIdentity());
643        }
644        void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override {
645            EXPECT_EQ(1, mIndex++);
646            ASSERT_NE(nullptr, op.paint);
647            ASSERT_EQ(SkXfermode::kClear_Mode, PaintUtils::getXfermodeDirect(op.paint));
648        }
649        void onRectOp(const RectOp& op, const BakedOpState& state) override {
650            EXPECT_EQ(2, mIndex++);
651            EXPECT_EQ(Rect(200, 200), op.unmappedBounds);
652            EXPECT_EQ(Rect(200, 200), state.computedState.clippedBounds);
653            EXPECT_EQ(Rect(200, 200), state.computedState.clipRect());
654            EXPECT_TRUE(state.computedState.transform.isIdentity());
655        }
656        void onCopyFromLayerOp(const CopyFromLayerOp& op, const BakedOpState& state) override {
657            EXPECT_EQ(3, mIndex++);
658            EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds);
659            EXPECT_CLIP_RECT(Rect(200, 200), state.computedState.clipState);
660            EXPECT_TRUE(state.computedState.transform.isIdentity());
661        }
662    };
663
664    auto node = TestUtils::createNode(0, 0, 200, 200,
665            [](RenderProperties& props, RecordingCanvas& canvas) {
666        canvas.saveLayerAlpha(10, 10, 190, 190, 128, (SaveFlags::Flags)(0));
667        canvas.drawRect(0, 0, 200, 200, SkPaint());
668        canvas.restore();
669    });
670    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
671            TestUtils::createSyncedNodeList(node), sLightGeometry, nullptr);
672    SaveLayerUnclippedSimpleTestRenderer renderer;
673    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
674    EXPECT_EQ(4, renderer.getIndex());
675}
676
677TEST(FrameBuilder, saveLayerUnclipped_mergedClears) {
678    class SaveLayerUnclippedMergedClearsTestRenderer : public TestRendererBase {
679    public:
680        void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override {
681            int index = mIndex++;
682            EXPECT_GT(4, index);
683            EXPECT_EQ(5, op.unmappedBounds.getWidth());
684            EXPECT_EQ(5, op.unmappedBounds.getHeight());
685            if (index == 0) {
686                EXPECT_EQ(Rect(10, 10), state.computedState.clippedBounds);
687            } else if (index == 1) {
688                EXPECT_EQ(Rect(190, 0, 200, 10), state.computedState.clippedBounds);
689            } else if (index == 2) {
690                EXPECT_EQ(Rect(0, 190, 10, 200), state.computedState.clippedBounds);
691            } else if (index == 3) {
692                EXPECT_EQ(Rect(190, 190, 200, 200), state.computedState.clippedBounds);
693            }
694        }
695        void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override {
696            EXPECT_EQ(4, mIndex++);
697            ASSERT_EQ(op.vertexCount, 16u);
698            for (size_t i = 0; i < op.vertexCount; i++) {
699                auto v = op.vertices[i];
700                EXPECT_TRUE(v.x == 0 || v.x == 10 || v.x == 190 || v.x == 200);
701                EXPECT_TRUE(v.y == 0 || v.y == 10 || v.y == 190 || v.y == 200);
702            }
703        }
704        void onRectOp(const RectOp& op, const BakedOpState& state) override {
705            EXPECT_EQ(5, mIndex++);
706        }
707        void onCopyFromLayerOp(const CopyFromLayerOp& op, const BakedOpState& state) override {
708            EXPECT_LT(5, mIndex++);
709        }
710    };
711
712    auto node = TestUtils::createNode(0, 0, 200, 200,
713            [](RenderProperties& props, RecordingCanvas& canvas) {
714
715        int restoreTo = canvas.save(SaveFlags::MatrixClip);
716        canvas.scale(2, 2);
717        canvas.saveLayerAlpha(0, 0, 5, 5, 128, SaveFlags::MatrixClip);
718        canvas.saveLayerAlpha(95, 0, 100, 5, 128, SaveFlags::MatrixClip);
719        canvas.saveLayerAlpha(0, 95, 5, 100, 128, SaveFlags::MatrixClip);
720        canvas.saveLayerAlpha(95, 95, 100, 100, 128, SaveFlags::MatrixClip);
721        canvas.drawRect(0, 0, 100, 100, SkPaint());
722        canvas.restoreToCount(restoreTo);
723    });
724    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
725            TestUtils::createSyncedNodeList(node), sLightGeometry, nullptr);
726    SaveLayerUnclippedMergedClearsTestRenderer renderer;
727    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
728    EXPECT_EQ(10, renderer.getIndex())
729            << "Expect 4 copyTos, 4 copyFroms, 1 clear SimpleRects, and 1 rect.";
730}
731
732TEST(FrameBuilder, saveLayerUnclipped_clearClip) {
733    class SaveLayerUnclippedClearClipTestRenderer : public TestRendererBase {
734    public:
735        void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override {
736            EXPECT_EQ(0, mIndex++);
737        }
738        void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override {
739            EXPECT_EQ(1, mIndex++);
740            ASSERT_NE(nullptr, op.paint);
741            EXPECT_EQ(SkXfermode::kClear_Mode, PaintUtils::getXfermodeDirect(op.paint));
742            EXPECT_EQ(Rect(50, 50, 150, 150), state.computedState.clippedBounds)
743                    << "Expect dirty rect as clip";
744            ASSERT_NE(nullptr, state.computedState.clipState);
745            EXPECT_EQ(Rect(50, 50, 150, 150), state.computedState.clipState->rect);
746            EXPECT_EQ(ClipMode::Rectangle, state.computedState.clipState->mode);
747        }
748        void onRectOp(const RectOp& op, const BakedOpState& state) override {
749            EXPECT_EQ(2, mIndex++);
750        }
751        void onCopyFromLayerOp(const CopyFromLayerOp& op, const BakedOpState& state) override {
752            EXPECT_EQ(3, mIndex++);
753        }
754    };
755
756    auto node = TestUtils::createNode(0, 0, 200, 200,
757            [](RenderProperties& props, RecordingCanvas& canvas) {
758        // save smaller than clip, so we get unclipped behavior
759        canvas.saveLayerAlpha(10, 10, 190, 190, 128, (SaveFlags::Flags)(0));
760        canvas.drawRect(0, 0, 200, 200, SkPaint());
761        canvas.restore();
762    });
763
764    // draw with partial screen dirty, and assert we see that rect later
765    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeLTRB(50, 50, 150, 150), 200, 200,
766            TestUtils::createSyncedNodeList(node), sLightGeometry, nullptr);
767    SaveLayerUnclippedClearClipTestRenderer renderer;
768    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
769    EXPECT_EQ(4, renderer.getIndex());
770}
771
772TEST(FrameBuilder, saveLayerUnclipped_reject) {
773    auto node = TestUtils::createNode(0, 0, 200, 200,
774            [](RenderProperties& props, RecordingCanvas& canvas) {
775        // unclipped savelayer + rect both in area that won't intersect with dirty
776        canvas.saveLayerAlpha(100, 100, 200, 200, 128, (SaveFlags::Flags)(0));
777        canvas.drawRect(100, 100, 200, 200, SkPaint());
778        canvas.restore();
779    });
780
781    // draw with partial screen dirty that doesn't intersect with savelayer
782    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 200, 200,
783            TestUtils::createSyncedNodeList(node), sLightGeometry, nullptr);
784    FailRenderer renderer;
785    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
786}
787
788/* saveLayerUnclipped { saveLayer { saveLayerUnclipped { rect } } } will play back as:
789 * - startTemporaryLayer, onCopyToLayer, onSimpleRects, onRect, onCopyFromLayer, endLayer
790 * - startFrame, onCopyToLayer, onSimpleRects, drawLayer, onCopyFromLayer, endframe
791 */
792TEST(FrameBuilder, saveLayerUnclipped_complex) {
793    class SaveLayerUnclippedComplexTestRenderer : public TestRendererBase {
794    public:
795        OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) {
796            EXPECT_EQ(0, mIndex++); // savelayer first
797            return (OffscreenBuffer*)0xabcd;
798        }
799        void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override {
800            int index = mIndex++;
801            EXPECT_TRUE(index == 1 || index == 7);
802        }
803        void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override {
804            int index = mIndex++;
805            EXPECT_TRUE(index == 2 || index == 8);
806        }
807        void onRectOp(const RectOp& op, const BakedOpState& state) override {
808            EXPECT_EQ(3, mIndex++);
809            Matrix4 expected;
810            expected.loadTranslate(-100, -100, 0);
811            EXPECT_EQ(Rect(100, 100, 200, 200), state.computedState.clippedBounds);
812            EXPECT_MATRIX_APPROX_EQ(expected, state.computedState.transform);
813        }
814        void onCopyFromLayerOp(const CopyFromLayerOp& op, const BakedOpState& state) override {
815            int index = mIndex++;
816            EXPECT_TRUE(index == 4 || index == 10);
817        }
818        void endLayer() override {
819            EXPECT_EQ(5, mIndex++);
820        }
821        void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
822            EXPECT_EQ(6, mIndex++);
823        }
824        void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
825            EXPECT_EQ(9, mIndex++);
826        }
827        void endFrame(const Rect& repaintRect) override {
828            EXPECT_EQ(11, mIndex++);
829        }
830    };
831
832    auto node = TestUtils::createNode(0, 0, 600, 600, // 500x500 triggers clipping
833            [](RenderProperties& props, RecordingCanvas& canvas) {
834        canvas.saveLayerAlpha(0, 0, 500, 500, 128, (SaveFlags::Flags)0); // unclipped
835        canvas.saveLayerAlpha(100, 100, 400, 400, 128, SaveFlags::ClipToLayer); // clipped
836        canvas.saveLayerAlpha(200, 200, 300, 300, 128, (SaveFlags::Flags)0); // unclipped
837        canvas.drawRect(200, 200, 300, 300, SkPaint());
838        canvas.restore();
839        canvas.restore();
840        canvas.restore();
841    });
842    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(600, 600), 600, 600,
843            TestUtils::createSyncedNodeList(node), sLightGeometry, nullptr);
844    SaveLayerUnclippedComplexTestRenderer renderer;
845    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
846    EXPECT_EQ(12, renderer.getIndex());
847}
848
849RENDERTHREAD_TEST(FrameBuilder, hwLayer_simple) {
850    class HwLayerSimpleTestRenderer : public TestRendererBase {
851    public:
852        void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override {
853            EXPECT_EQ(0, mIndex++);
854            EXPECT_EQ(100u, offscreenBuffer->viewportWidth);
855            EXPECT_EQ(100u, offscreenBuffer->viewportHeight);
856            EXPECT_EQ(Rect(25, 25, 75, 75), repaintRect);
857        }
858        void onRectOp(const RectOp& op, const BakedOpState& state) override {
859            EXPECT_EQ(1, mIndex++);
860
861            EXPECT_TRUE(state.computedState.transform.isIdentity())
862                    << "Transform should be reset within layer";
863
864            EXPECT_EQ(Rect(25, 25, 75, 75), state.computedState.clipRect())
865                    << "Damage rect should be used to clip layer content";
866        }
867        void endLayer() override {
868            EXPECT_EQ(2, mIndex++);
869        }
870        void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
871            EXPECT_EQ(3, mIndex++);
872        }
873        void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
874            EXPECT_EQ(4, mIndex++);
875        }
876        void endFrame(const Rect& repaintRect) override {
877            EXPECT_EQ(5, mIndex++);
878        }
879    };
880
881    auto node = TestUtils::createNode(10, 10, 110, 110,
882            [](RenderProperties& props, RecordingCanvas& canvas) {
883        props.mutateLayerProperties().setType(LayerType::RenderLayer);
884        SkPaint paint;
885        paint.setColor(SK_ColorWHITE);
886        canvas.drawRect(0, 0, 100, 100, paint);
887    });
888    OffscreenBuffer** layerHandle = node->getLayerHandle();
889
890    // create RenderNode's layer here in same way prepareTree would
891    OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 100, 100);
892    *layerHandle = &layer;
893
894    auto syncedNodeList = TestUtils::createSyncedNodeList(node);
895
896    // only enqueue partial damage
897    LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
898    layerUpdateQueue.enqueueLayerWithDamage(node.get(), Rect(25, 25, 75, 75));
899
900    FrameBuilder frameBuilder(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
901            syncedNodeList, sLightGeometry, nullptr);
902    HwLayerSimpleTestRenderer renderer;
903    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
904    EXPECT_EQ(6, renderer.getIndex());
905
906    // clean up layer pointer, so we can safely destruct RenderNode
907    *layerHandle = nullptr;
908}
909
910RENDERTHREAD_TEST(FrameBuilder, hwLayer_complex) {
911    /* parentLayer { greyRect, saveLayer { childLayer { whiteRect } } } will play back as:
912     * - startRepaintLayer(child), rect(grey), endLayer
913     * - startTemporaryLayer, drawLayer(child), endLayer
914     * - startRepaintLayer(parent), rect(white), drawLayer(saveLayer), endLayer
915     * - startFrame, drawLayer(parent), endLayerb
916     */
917    class HwLayerComplexTestRenderer : public TestRendererBase {
918    public:
919        OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) {
920            EXPECT_EQ(3, mIndex++); // savelayer first
921            return (OffscreenBuffer*)0xabcd;
922        }
923        void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override {
924            int index = mIndex++;
925            if (index == 0) {
926                // starting inner layer
927                EXPECT_EQ(100u, offscreenBuffer->viewportWidth);
928                EXPECT_EQ(100u, offscreenBuffer->viewportHeight);
929            } else if (index == 6) {
930                // starting outer layer
931                EXPECT_EQ(200u, offscreenBuffer->viewportWidth);
932                EXPECT_EQ(200u, offscreenBuffer->viewportHeight);
933            } else { ADD_FAILURE(); }
934        }
935        void onRectOp(const RectOp& op, const BakedOpState& state) override {
936            int index = mIndex++;
937            if (index == 1) {
938                // inner layer's rect (white)
939                EXPECT_EQ(SK_ColorWHITE, op.paint->getColor());
940            } else if (index == 7) {
941                // outer layer's rect (grey)
942                EXPECT_EQ(SK_ColorDKGRAY, op.paint->getColor());
943            } else { ADD_FAILURE(); }
944        }
945        void endLayer() override {
946            int index = mIndex++;
947            EXPECT_TRUE(index == 2 || index == 5 || index == 9);
948        }
949        void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
950            EXPECT_EQ(10, mIndex++);
951        }
952        void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
953            OffscreenBuffer* layer = *op.layerHandle;
954            int index = mIndex++;
955            if (index == 4) {
956                EXPECT_EQ(100u, layer->viewportWidth);
957                EXPECT_EQ(100u, layer->viewportHeight);
958            } else if (index == 8) {
959                EXPECT_EQ((OffscreenBuffer*)0xabcd, *op.layerHandle);
960            } else if (index == 11) {
961                EXPECT_EQ(200u, layer->viewportWidth);
962                EXPECT_EQ(200u, layer->viewportHeight);
963            } else { ADD_FAILURE(); }
964        }
965        void endFrame(const Rect& repaintRect) override {
966            EXPECT_EQ(12, mIndex++);
967        }
968    };
969
970    auto child = TestUtils::createNode(50, 50, 150, 150,
971            [](RenderProperties& props, RecordingCanvas& canvas) {
972        props.mutateLayerProperties().setType(LayerType::RenderLayer);
973        SkPaint paint;
974        paint.setColor(SK_ColorWHITE);
975        canvas.drawRect(0, 0, 100, 100, paint);
976    });
977    OffscreenBuffer childLayer(renderThread.renderState(), Caches::getInstance(), 100, 100);
978    *(child->getLayerHandle()) = &childLayer;
979
980    RenderNode* childPtr = child.get();
981    auto parent = TestUtils::createNode(0, 0, 200, 200,
982            [childPtr](RenderProperties& props, RecordingCanvas& canvas) {
983        props.mutateLayerProperties().setType(LayerType::RenderLayer);
984        SkPaint paint;
985        paint.setColor(SK_ColorDKGRAY);
986        canvas.drawRect(0, 0, 200, 200, paint);
987
988        canvas.saveLayerAlpha(50, 50, 150, 150, 128, SaveFlags::ClipToLayer);
989        canvas.drawRenderNode(childPtr);
990        canvas.restore();
991    });
992    OffscreenBuffer parentLayer(renderThread.renderState(), Caches::getInstance(), 200, 200);
993    *(parent->getLayerHandle()) = &parentLayer;
994
995    auto syncedList = TestUtils::createSyncedNodeList(parent);
996
997    LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
998    layerUpdateQueue.enqueueLayerWithDamage(child.get(), Rect(100, 100));
999    layerUpdateQueue.enqueueLayerWithDamage(parent.get(), Rect(200, 200));
1000
1001    FrameBuilder frameBuilder(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
1002            syncedList, sLightGeometry, nullptr);
1003    HwLayerComplexTestRenderer renderer;
1004    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1005    EXPECT_EQ(13, renderer.getIndex());
1006
1007    // clean up layer pointers, so we can safely destruct RenderNodes
1008    *(child->getLayerHandle()) = nullptr;
1009    *(parent->getLayerHandle()) = nullptr;
1010}
1011
1012static void drawOrderedRect(RecordingCanvas* canvas, uint8_t expectedDrawOrder) {
1013    SkPaint paint;
1014    paint.setColor(SkColorSetARGB(256, 0, 0, expectedDrawOrder)); // order put in blue channel
1015    canvas->drawRect(0, 0, 100, 100, paint);
1016}
1017static void drawOrderedNode(RecordingCanvas* canvas, uint8_t expectedDrawOrder, float z) {
1018    auto node = TestUtils::createNode(0, 0, 100, 100,
1019            [expectedDrawOrder](RenderProperties& props, RecordingCanvas& canvas) {
1020        drawOrderedRect(&canvas, expectedDrawOrder);
1021    });
1022    node->mutateStagingProperties().setTranslationZ(z);
1023    node->setPropertyFieldsDirty(RenderNode::TRANSLATION_Z);
1024    canvas->drawRenderNode(node.get()); // canvas takes reference/sole ownership
1025}
1026TEST(FrameBuilder, zReorder) {
1027    class ZReorderTestRenderer : public TestRendererBase {
1028    public:
1029        void onRectOp(const RectOp& op, const BakedOpState& state) override {
1030            int expectedOrder = SkColorGetB(op.paint->getColor()); // extract order from blue channel
1031            EXPECT_EQ(expectedOrder, mIndex++) << "An op was drawn out of order";
1032        }
1033    };
1034
1035    auto parent = TestUtils::createNode(0, 0, 100, 100,
1036            [](RenderProperties& props, RecordingCanvas& canvas) {
1037        drawOrderedNode(&canvas, 0, 10.0f); // in reorder=false at this point, so played inorder
1038        drawOrderedRect(&canvas, 1);
1039        canvas.insertReorderBarrier(true);
1040        drawOrderedNode(&canvas, 6, 2.0f);
1041        drawOrderedRect(&canvas, 3);
1042        drawOrderedNode(&canvas, 4, 0.0f);
1043        drawOrderedRect(&canvas, 5);
1044        drawOrderedNode(&canvas, 2, -2.0f);
1045        drawOrderedNode(&canvas, 7, 2.0f);
1046        canvas.insertReorderBarrier(false);
1047        drawOrderedRect(&canvas, 8);
1048        drawOrderedNode(&canvas, 9, -10.0f); // in reorder=false at this point, so played inorder
1049    });
1050    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100,
1051            TestUtils::createSyncedNodeList(parent), sLightGeometry, nullptr);
1052    ZReorderTestRenderer renderer;
1053    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1054    EXPECT_EQ(10, renderer.getIndex());
1055};
1056
1057TEST(FrameBuilder, projectionReorder) {
1058    static const int scrollX = 5;
1059    static const int scrollY = 10;
1060    class ProjectionReorderTestRenderer : public TestRendererBase {
1061    public:
1062        void onRectOp(const RectOp& op, const BakedOpState& state) override {
1063            const int index = mIndex++;
1064
1065            Matrix4 expectedMatrix;
1066            switch (index) {
1067            case 0:
1068                EXPECT_EQ(Rect(100, 100), op.unmappedBounds);
1069                EXPECT_EQ(SK_ColorWHITE, op.paint->getColor());
1070                expectedMatrix.loadIdentity();
1071                EXPECT_EQ(nullptr, state.computedState.localProjectionPathMask);
1072                break;
1073            case 1:
1074                EXPECT_EQ(Rect(-10, -10, 60, 60), op.unmappedBounds);
1075                EXPECT_EQ(SK_ColorDKGRAY, op.paint->getColor());
1076                expectedMatrix.loadTranslate(50 - scrollX, 50 - scrollY, 0);
1077                ASSERT_NE(nullptr, state.computedState.localProjectionPathMask);
1078                EXPECT_EQ(Rect(-35, -30, 45, 50),
1079                        Rect(state.computedState.localProjectionPathMask->getBounds()));
1080                break;
1081            case 2:
1082                EXPECT_EQ(Rect(100, 50), op.unmappedBounds);
1083                EXPECT_EQ(SK_ColorBLUE, op.paint->getColor());
1084                expectedMatrix.loadTranslate(-scrollX, 50 - scrollY, 0);
1085                EXPECT_EQ(nullptr, state.computedState.localProjectionPathMask);
1086                break;
1087            default:
1088                ADD_FAILURE();
1089            }
1090            EXPECT_EQ(expectedMatrix, state.computedState.transform);
1091        }
1092    };
1093
1094    /**
1095     * Construct a tree of nodes, where the root (A) has a receiver background (B), and a child (C)
1096     * with a projecting child (P) of its own. P would normally draw between B and C's "background"
1097     * draw, but because it is projected backwards, it's drawn in between B and C.
1098     *
1099     * The parent is scrolled by scrollX/scrollY, but this does not affect the background
1100     * (which isn't affected by scroll).
1101     */
1102    auto receiverBackground = TestUtils::createNode(0, 0, 100, 100,
1103            [](RenderProperties& properties, RecordingCanvas& canvas) {
1104        properties.setProjectionReceiver(true);
1105        // scroll doesn't apply to background, so undone via translationX/Y
1106        // NOTE: translationX/Y only! no other transform properties may be set for a proj receiver!
1107        properties.setTranslationX(scrollX);
1108        properties.setTranslationY(scrollY);
1109
1110        SkPaint paint;
1111        paint.setColor(SK_ColorWHITE);
1112        canvas.drawRect(0, 0, 100, 100, paint);
1113    });
1114    auto projectingRipple = TestUtils::createNode(50, 0, 100, 50,
1115            [](RenderProperties& properties, RecordingCanvas& canvas) {
1116        properties.setProjectBackwards(true);
1117        properties.setClipToBounds(false);
1118        SkPaint paint;
1119        paint.setColor(SK_ColorDKGRAY);
1120        canvas.drawRect(-10, -10, 60, 60, paint);
1121    });
1122    auto child = TestUtils::createNode(0, 50, 100, 100,
1123            [&projectingRipple](RenderProperties& properties, RecordingCanvas& canvas) {
1124        SkPaint paint;
1125        paint.setColor(SK_ColorBLUE);
1126        canvas.drawRect(0, 0, 100, 50, paint);
1127        canvas.drawRenderNode(projectingRipple.get());
1128    });
1129    auto parent = TestUtils::createNode(0, 0, 100, 100,
1130            [&receiverBackground, &child](RenderProperties& properties, RecordingCanvas& canvas) {
1131        // Set a rect outline for the projecting ripple to be masked against.
1132        properties.mutableOutline().setRoundRect(10, 10, 90, 90, 5, 1.0f);
1133
1134        canvas.save(SaveFlags::MatrixClip);
1135        canvas.translate(-scrollX, -scrollY); // Apply scroll (note: bg undoes this internally)
1136        canvas.drawRenderNode(receiverBackground.get());
1137        canvas.drawRenderNode(child.get());
1138        canvas.restore();
1139    });
1140
1141    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100,
1142            TestUtils::createSyncedNodeList(parent), sLightGeometry, nullptr);
1143    ProjectionReorderTestRenderer renderer;
1144    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1145    EXPECT_EQ(3, renderer.getIndex());
1146}
1147
1148RENDERTHREAD_TEST(FrameBuilder, projectionHwLayer) {
1149    static const int scrollX = 5;
1150    static const int scrollY = 10;
1151    class ProjectionHwLayerTestRenderer : public TestRendererBase {
1152    public:
1153        void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override {
1154            EXPECT_EQ(0, mIndex++);
1155        }
1156        void onArcOp(const ArcOp& op, const BakedOpState& state) override {
1157            EXPECT_EQ(1, mIndex++);
1158            ASSERT_EQ(nullptr, state.computedState.localProjectionPathMask);
1159        }
1160        void endLayer() override {
1161            EXPECT_EQ(2, mIndex++);
1162        }
1163        void onRectOp(const RectOp& op, const BakedOpState& state) override {
1164            EXPECT_EQ(3, mIndex++);
1165            ASSERT_EQ(nullptr, state.computedState.localProjectionPathMask);
1166        }
1167        void onOvalOp(const OvalOp& op, const BakedOpState& state) override {
1168            EXPECT_EQ(4, mIndex++);
1169            ASSERT_NE(nullptr, state.computedState.localProjectionPathMask);
1170            Matrix4 expected;
1171            expected.loadTranslate(100 - scrollX, 100 - scrollY, 0);
1172            EXPECT_EQ(expected, state.computedState.transform);
1173            EXPECT_EQ(Rect(-85, -80, 295, 300),
1174                    Rect(state.computedState.localProjectionPathMask->getBounds()));
1175        }
1176        void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
1177            EXPECT_EQ(5, mIndex++);
1178            ASSERT_EQ(nullptr, state.computedState.localProjectionPathMask);
1179        }
1180    };
1181    auto receiverBackground = TestUtils::createNode(0, 0, 400, 400,
1182            [](RenderProperties& properties, RecordingCanvas& canvas) {
1183        properties.setProjectionReceiver(true);
1184        // scroll doesn't apply to background, so undone via translationX/Y
1185        // NOTE: translationX/Y only! no other transform properties may be set for a proj receiver!
1186        properties.setTranslationX(scrollX);
1187        properties.setTranslationY(scrollY);
1188
1189        canvas.drawRect(0, 0, 400, 400, SkPaint());
1190    });
1191    auto projectingRipple = TestUtils::createNode(0, 0, 200, 200,
1192            [](RenderProperties& properties, RecordingCanvas& canvas) {
1193        properties.setProjectBackwards(true);
1194        properties.setClipToBounds(false);
1195        canvas.drawOval(100, 100, 300, 300, SkPaint()); // drawn mostly out of layer bounds
1196    });
1197    auto child = TestUtils::createNode(100, 100, 300, 300,
1198            [&projectingRipple](RenderProperties& properties, RecordingCanvas& canvas) {
1199        properties.mutateLayerProperties().setType(LayerType::RenderLayer);
1200        canvas.drawRenderNode(projectingRipple.get());
1201        canvas.drawArc(0, 0, 200, 200, 0.0f, 280.0f, true, SkPaint());
1202    });
1203    auto parent = TestUtils::createNode(0, 0, 400, 400,
1204            [&receiverBackground, &child](RenderProperties& properties, RecordingCanvas& canvas) {
1205        // Set a rect outline for the projecting ripple to be masked against.
1206        properties.mutableOutline().setRoundRect(10, 10, 390, 390, 0, 1.0f);
1207        canvas.translate(-scrollX, -scrollY); // Apply scroll (note: bg undoes this internally)
1208        canvas.drawRenderNode(receiverBackground.get());
1209        canvas.drawRenderNode(child.get());
1210    });
1211
1212    OffscreenBuffer** layerHandle = child->getLayerHandle();
1213
1214    // create RenderNode's layer here in same way prepareTree would, setting windowTransform
1215    OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 200, 200);
1216    Matrix4 windowTransform;
1217    windowTransform.loadTranslate(100, 100, 0); // total transform of layer's origin
1218    layer.setWindowTransform(windowTransform);
1219    *layerHandle = &layer;
1220
1221    auto syncedList = TestUtils::createSyncedNodeList(parent);
1222    LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
1223    layerUpdateQueue.enqueueLayerWithDamage(child.get(), Rect(200, 200));
1224    FrameBuilder frameBuilder(layerUpdateQueue, SkRect::MakeWH(400, 400), 400, 400,
1225            syncedList, sLightGeometry, nullptr);
1226    ProjectionHwLayerTestRenderer renderer;
1227    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1228    EXPECT_EQ(6, renderer.getIndex());
1229
1230    // clean up layer pointer, so we can safely destruct RenderNode
1231    *layerHandle = nullptr;
1232}
1233
1234RENDERTHREAD_TEST(FrameBuilder, projectionChildScroll) {
1235    static const int scrollX = 500000;
1236    static const int scrollY = 0;
1237    class ProjectionChildScrollTestRenderer : public TestRendererBase {
1238    public:
1239        void onRectOp(const RectOp& op, const BakedOpState& state) override {
1240            EXPECT_EQ(0, mIndex++);
1241            EXPECT_TRUE(state.computedState.transform.isIdentity());
1242        }
1243        void onOvalOp(const OvalOp& op, const BakedOpState& state) override {
1244            EXPECT_EQ(1, mIndex++);
1245            ASSERT_NE(nullptr, state.computedState.clipState);
1246            ASSERT_EQ(ClipMode::Rectangle, state.computedState.clipState->mode);
1247            ASSERT_EQ(Rect(400, 400), state.computedState.clipState->rect);
1248            EXPECT_TRUE(state.computedState.transform.isIdentity());
1249        }
1250    };
1251    auto receiverBackground = TestUtils::createNode(0, 0, 400, 400,
1252            [](RenderProperties& properties, RecordingCanvas& canvas) {
1253        properties.setProjectionReceiver(true);
1254        canvas.drawRect(0, 0, 400, 400, SkPaint());
1255    });
1256    auto projectingRipple = TestUtils::createNode(0, 0, 200, 200,
1257            [](RenderProperties& properties, RecordingCanvas& canvas) {
1258        // scroll doesn't apply to background, so undone via translationX/Y
1259        // NOTE: translationX/Y only! no other transform properties may be set for a proj receiver!
1260        properties.setTranslationX(scrollX);
1261        properties.setTranslationY(scrollY);
1262        properties.setProjectBackwards(true);
1263        properties.setClipToBounds(false);
1264        canvas.drawOval(0, 0, 200, 200, SkPaint());
1265    });
1266    auto child = TestUtils::createNode(0, 0, 400, 400,
1267            [&projectingRipple](RenderProperties& properties, RecordingCanvas& canvas) {
1268        // Record time clip will be ignored by projectee
1269        canvas.clipRect(100, 100, 300, 300, SkRegion::kIntersect_Op);
1270
1271        canvas.translate(-scrollX, -scrollY); // Apply scroll (note: bg undoes this internally)
1272        canvas.drawRenderNode(projectingRipple.get());
1273    });
1274    auto parent = TestUtils::createNode(0, 0, 400, 400,
1275            [&receiverBackground, &child](RenderProperties& properties, RecordingCanvas& canvas) {
1276        canvas.drawRenderNode(receiverBackground.get());
1277        canvas.drawRenderNode(child.get());
1278    });
1279
1280    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(400, 400), 400, 400,
1281            TestUtils::createSyncedNodeList(parent), sLightGeometry, nullptr);
1282    ProjectionChildScrollTestRenderer renderer;
1283    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1284    EXPECT_EQ(2, renderer.getIndex());
1285}
1286
1287// creates a 100x100 shadow casting node with provided translationZ
1288static sp<RenderNode> createWhiteRectShadowCaster(float translationZ) {
1289    return TestUtils::createNode(0, 0, 100, 100,
1290            [translationZ](RenderProperties& properties, RecordingCanvas& canvas) {
1291        properties.setTranslationZ(translationZ);
1292        properties.mutableOutline().setRoundRect(0, 0, 100, 100, 0.0f, 1.0f);
1293        SkPaint paint;
1294        paint.setColor(SK_ColorWHITE);
1295        canvas.drawRect(0, 0, 100, 100, paint);
1296    });
1297}
1298
1299RENDERTHREAD_TEST(FrameBuilder, shadow) {
1300    class ShadowTestRenderer : public TestRendererBase {
1301    public:
1302        void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
1303            EXPECT_EQ(0, mIndex++);
1304            EXPECT_FLOAT_EQ(1.0f, op.casterAlpha);
1305            EXPECT_TRUE(op.shadowTask->casterPerimeter.isRect(nullptr));
1306            EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), op.shadowTask->transformXY);
1307
1308            Matrix4 expectedZ;
1309            expectedZ.loadTranslate(0, 0, 5);
1310            EXPECT_MATRIX_APPROX_EQ(expectedZ, op.shadowTask->transformZ);
1311        }
1312        void onRectOp(const RectOp& op, const BakedOpState& state) override {
1313            EXPECT_EQ(1, mIndex++);
1314        }
1315    };
1316
1317    auto parent = TestUtils::createNode(0, 0, 200, 200,
1318            [](RenderProperties& props, RecordingCanvas& canvas) {
1319        canvas.insertReorderBarrier(true);
1320        canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
1321    });
1322
1323    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
1324            TestUtils::createSyncedNodeList(parent), sLightGeometry, &Caches::getInstance());
1325    ShadowTestRenderer renderer;
1326    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1327    EXPECT_EQ(2, renderer.getIndex());
1328}
1329
1330RENDERTHREAD_TEST(FrameBuilder, shadowSaveLayer) {
1331    class ShadowSaveLayerTestRenderer : public TestRendererBase {
1332    public:
1333        OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override {
1334            EXPECT_EQ(0, mIndex++);
1335            return nullptr;
1336        }
1337        void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
1338            EXPECT_EQ(1, mIndex++);
1339            EXPECT_FLOAT_EQ(50, op.shadowTask->lightCenter.x);
1340            EXPECT_FLOAT_EQ(40, op.shadowTask->lightCenter.y);
1341        }
1342        void onRectOp(const RectOp& op, const BakedOpState& state) override {
1343            EXPECT_EQ(2, mIndex++);
1344        }
1345        void endLayer() override {
1346            EXPECT_EQ(3, mIndex++);
1347        }
1348        void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
1349            EXPECT_EQ(4, mIndex++);
1350        }
1351    };
1352
1353    auto parent = TestUtils::createNode(0, 0, 200, 200,
1354            [](RenderProperties& props, RecordingCanvas& canvas) {
1355        // save/restore outside of reorderBarrier, so they don't get moved out of place
1356        canvas.translate(20, 10);
1357        int count = canvas.saveLayerAlpha(30, 50, 130, 150, 128, SaveFlags::ClipToLayer);
1358        canvas.insertReorderBarrier(true);
1359        canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
1360        canvas.insertReorderBarrier(false);
1361        canvas.restoreToCount(count);
1362    });
1363
1364    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
1365            TestUtils::createSyncedNodeList(parent),
1366            (FrameBuilder::LightGeometry) {{ 100, 100, 100 }, 50},
1367            &Caches::getInstance());
1368    ShadowSaveLayerTestRenderer renderer;
1369    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1370    EXPECT_EQ(5, renderer.getIndex());
1371}
1372
1373RENDERTHREAD_TEST(FrameBuilder, shadowHwLayer) {
1374    class ShadowHwLayerTestRenderer : public TestRendererBase {
1375    public:
1376        void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override {
1377            EXPECT_EQ(0, mIndex++);
1378        }
1379        void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
1380            EXPECT_EQ(1, mIndex++);
1381            EXPECT_FLOAT_EQ(50, op.shadowTask->lightCenter.x);
1382            EXPECT_FLOAT_EQ(40, op.shadowTask->lightCenter.y);
1383            EXPECT_FLOAT_EQ(30, op.shadowTask->lightRadius);
1384        }
1385        void onRectOp(const RectOp& op, const BakedOpState& state) override {
1386            EXPECT_EQ(2, mIndex++);
1387        }
1388        void endLayer() override {
1389            EXPECT_EQ(3, mIndex++);
1390        }
1391        void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
1392            EXPECT_EQ(4, mIndex++);
1393        }
1394    };
1395
1396    auto parent = TestUtils::createNode(50, 60, 150, 160,
1397            [](RenderProperties& props, RecordingCanvas& canvas) {
1398        props.mutateLayerProperties().setType(LayerType::RenderLayer);
1399        canvas.insertReorderBarrier(true);
1400        canvas.save(SaveFlags::MatrixClip);
1401        canvas.translate(20, 10);
1402        canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
1403        canvas.restore();
1404    });
1405    OffscreenBuffer** layerHandle = parent->getLayerHandle();
1406
1407    // create RenderNode's layer here in same way prepareTree would, setting windowTransform
1408    OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 100, 100);
1409    Matrix4 windowTransform;
1410    windowTransform.loadTranslate(50, 60, 0); // total transform of layer's origin
1411    layer.setWindowTransform(windowTransform);
1412    *layerHandle = &layer;
1413
1414    auto syncedList = TestUtils::createSyncedNodeList(parent);
1415    LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
1416    layerUpdateQueue.enqueueLayerWithDamage(parent.get(), Rect(100, 100));
1417    FrameBuilder frameBuilder(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
1418            syncedList,
1419            (FrameBuilder::LightGeometry) {{ 100, 100, 100 }, 30},
1420            &Caches::getInstance());
1421    ShadowHwLayerTestRenderer renderer;
1422    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1423    EXPECT_EQ(5, renderer.getIndex());
1424
1425    // clean up layer pointer, so we can safely destruct RenderNode
1426    *layerHandle = nullptr;
1427}
1428
1429TEST(FrameBuilder, shadowLayering) {
1430    class ShadowLayeringTestRenderer : public TestRendererBase {
1431    public:
1432        void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
1433            int index = mIndex++;
1434            EXPECT_TRUE(index == 0 || index == 1);
1435        }
1436        void onRectOp(const RectOp& op, const BakedOpState& state) override {
1437            int index = mIndex++;
1438            EXPECT_TRUE(index == 2 || index == 3);
1439        }
1440    };
1441    auto parent = TestUtils::createNode(0, 0, 200, 200,
1442            [](RenderProperties& props, RecordingCanvas& canvas) {
1443        canvas.insertReorderBarrier(true);
1444        canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
1445        canvas.drawRenderNode(createWhiteRectShadowCaster(5.0001f).get());
1446    });
1447
1448    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
1449            TestUtils::createSyncedNodeList(parent),
1450            (FrameBuilder::LightGeometry) {{ 100, 100, 100 }, 50},
1451            &Caches::getInstance());
1452    ShadowLayeringTestRenderer renderer;
1453    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1454    EXPECT_EQ(4, renderer.getIndex());
1455}
1456
1457static void testProperty(std::function<void(RenderProperties&)> propSetupCallback,
1458        std::function<void(const RectOp&, const BakedOpState&)> opValidateCallback) {
1459    class PropertyTestRenderer : public TestRendererBase {
1460    public:
1461        PropertyTestRenderer(std::function<void(const RectOp&, const BakedOpState&)> callback)
1462                : mCallback(callback) {}
1463        void onRectOp(const RectOp& op, const BakedOpState& state) override {
1464            EXPECT_EQ(mIndex++, 0);
1465            mCallback(op, state);
1466        }
1467        std::function<void(const RectOp&, const BakedOpState&)> mCallback;
1468    };
1469
1470    auto node = TestUtils::createNode(0, 0, 100, 100,
1471            [propSetupCallback](RenderProperties& props, RecordingCanvas& canvas) {
1472        propSetupCallback(props);
1473        SkPaint paint;
1474        paint.setColor(SK_ColorWHITE);
1475        canvas.drawRect(0, 0, 100, 100, paint);
1476    });
1477
1478    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 200, 200,
1479            TestUtils::createSyncedNodeList(node), sLightGeometry, nullptr);
1480    PropertyTestRenderer renderer(opValidateCallback);
1481    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1482    EXPECT_EQ(1, renderer.getIndex()) << "Should have seen one op";
1483}
1484
1485TEST(FrameBuilder, renderPropOverlappingRenderingAlpha) {
1486    testProperty([](RenderProperties& properties) {
1487        properties.setAlpha(0.5f);
1488        properties.setHasOverlappingRendering(false);
1489    }, [](const RectOp& op, const BakedOpState& state) {
1490        EXPECT_EQ(0.5f, state.alpha) << "Alpha should be applied directly to op";
1491    });
1492}
1493
1494TEST(FrameBuilder, renderPropClipping) {
1495    testProperty([](RenderProperties& properties) {
1496        properties.setClipToBounds(true);
1497        properties.setClipBounds(Rect(10, 20, 300, 400));
1498    }, [](const RectOp& op, const BakedOpState& state) {
1499        EXPECT_EQ(Rect(10, 20, 100, 100), state.computedState.clippedBounds)
1500                << "Clip rect should be intersection of node bounds and clip bounds";
1501    });
1502}
1503
1504TEST(FrameBuilder, renderPropRevealClip) {
1505    testProperty([](RenderProperties& properties) {
1506        properties.mutableRevealClip().set(true, 50, 50, 25);
1507    }, [](const RectOp& op, const BakedOpState& state) {
1508        ASSERT_NE(nullptr, state.roundRectClipState);
1509        EXPECT_TRUE(state.roundRectClipState->highPriority);
1510        EXPECT_EQ(25, state.roundRectClipState->radius);
1511        EXPECT_EQ(Rect(50, 50, 50, 50), state.roundRectClipState->innerRect);
1512    });
1513}
1514
1515TEST(FrameBuilder, renderPropOutlineClip) {
1516    testProperty([](RenderProperties& properties) {
1517        properties.mutableOutline().setShouldClip(true);
1518        properties.mutableOutline().setRoundRect(10, 20, 30, 40, 5.0f, 0.5f);
1519    }, [](const RectOp& op, const BakedOpState& state) {
1520        ASSERT_NE(nullptr, state.roundRectClipState);
1521        EXPECT_FALSE(state.roundRectClipState->highPriority);
1522        EXPECT_EQ(5, state.roundRectClipState->radius);
1523        EXPECT_EQ(Rect(15, 25, 25, 35), state.roundRectClipState->innerRect);
1524    });
1525}
1526
1527TEST(FrameBuilder, renderPropTransform) {
1528    testProperty([](RenderProperties& properties) {
1529        properties.setLeftTopRightBottom(10, 10, 110, 110);
1530
1531        SkMatrix staticMatrix = SkMatrix::MakeScale(1.2f, 1.2f);
1532        properties.setStaticMatrix(&staticMatrix);
1533
1534        // ignored, since static overrides animation
1535        SkMatrix animationMatrix = SkMatrix::MakeTrans(15, 15);
1536        properties.setAnimationMatrix(&animationMatrix);
1537
1538        properties.setTranslationX(10);
1539        properties.setTranslationY(20);
1540        properties.setScaleX(0.5f);
1541        properties.setScaleY(0.7f);
1542    }, [](const RectOp& op, const BakedOpState& state) {
1543        Matrix4 matrix;
1544        matrix.loadTranslate(10, 10, 0); // left, top
1545        matrix.scale(1.2f, 1.2f, 1); // static matrix
1546        // ignore animation matrix, since static overrides it
1547
1548        // translation xy
1549        matrix.translate(10, 20);
1550
1551        // scale xy (from default pivot - center)
1552        matrix.translate(50, 50);
1553        matrix.scale(0.5f, 0.7f, 1);
1554        matrix.translate(-50, -50);
1555        EXPECT_MATRIX_APPROX_EQ(matrix, state.computedState.transform)
1556                << "Op draw matrix must match expected combination of transformation properties";
1557    });
1558}
1559
1560struct SaveLayerAlphaData {
1561    uint32_t layerWidth = 0;
1562    uint32_t layerHeight = 0;
1563    Rect rectClippedBounds;
1564    Matrix4 rectMatrix;
1565};
1566/**
1567 * Constructs a view to hit the temporary layer alpha property implementation:
1568 *     a) 0 < alpha < 1
1569 *     b) too big for layer (larger than maxTextureSize)
1570 *     c) overlapping rendering content
1571 * returning observed data about layer size and content clip/transform.
1572 *
1573 * Used to validate clipping behavior of temporary layer, where requested layer size is reduced
1574 * (for efficiency, and to fit in layer size constraints) based on parent clip.
1575 */
1576void testSaveLayerAlphaClip(SaveLayerAlphaData* outObservedData,
1577        std::function<void(RenderProperties&)> propSetupCallback) {
1578    class SaveLayerAlphaClipTestRenderer : public TestRendererBase {
1579    public:
1580        SaveLayerAlphaClipTestRenderer(SaveLayerAlphaData* outData)
1581                : mOutData(outData) {}
1582
1583        OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override {
1584            EXPECT_EQ(0, mIndex++);
1585            mOutData->layerWidth = width;
1586            mOutData->layerHeight = height;
1587            return nullptr;
1588        }
1589        void onRectOp(const RectOp& op, const BakedOpState& state) override {
1590            EXPECT_EQ(1, mIndex++);
1591
1592            mOutData->rectClippedBounds = state.computedState.clippedBounds;
1593            mOutData->rectMatrix = state.computedState.transform;
1594        }
1595        void endLayer() override {
1596            EXPECT_EQ(2, mIndex++);
1597        }
1598        void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
1599            EXPECT_EQ(3, mIndex++);
1600        }
1601    private:
1602        SaveLayerAlphaData* mOutData;
1603    };
1604
1605    ASSERT_GT(10000, DeviceInfo::get()->maxTextureSize())
1606            << "Node must be bigger than max texture size to exercise saveLayer codepath";
1607    auto node = TestUtils::createNode(0, 0, 10000, 10000,
1608            [&propSetupCallback](RenderProperties& properties, RecordingCanvas& canvas) {
1609        properties.setHasOverlappingRendering(true);
1610        properties.setAlpha(0.5f); // force saveLayer, since too big for HW layer
1611        // apply other properties
1612        propSetupCallback(properties);
1613
1614        SkPaint paint;
1615        paint.setColor(SK_ColorWHITE);
1616        canvas.drawRect(0, 0, 10000, 10000, paint);
1617    });
1618    auto nodes = TestUtils::createSyncedNodeList(node); // sync before querying height
1619
1620    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
1621            nodes, sLightGeometry, nullptr);
1622    SaveLayerAlphaClipTestRenderer renderer(outObservedData);
1623    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1624
1625    // assert, since output won't be valid if we haven't seen a save layer triggered
1626    ASSERT_EQ(4, renderer.getIndex()) << "Test must trigger saveLayer alpha behavior.";
1627}
1628
1629TEST(FrameBuilder, renderPropSaveLayerAlphaClipBig) {
1630    SaveLayerAlphaData observedData;
1631    testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) {
1632        properties.setTranslationX(10); // offset rendering content
1633        properties.setTranslationY(-2000); // offset rendering content
1634    });
1635    EXPECT_EQ(190u, observedData.layerWidth);
1636    EXPECT_EQ(200u, observedData.layerHeight);
1637    EXPECT_EQ(Rect(190, 200), observedData.rectClippedBounds)
1638            << "expect content to be clipped to screen area";
1639    Matrix4 expected;
1640    expected.loadTranslate(0, -2000, 0);
1641    EXPECT_MATRIX_APPROX_EQ(expected, observedData.rectMatrix)
1642            << "expect content to be translated as part of being clipped";
1643}
1644
1645TEST(FrameBuilder, renderPropSaveLayerAlphaRotate) {
1646    SaveLayerAlphaData observedData;
1647    testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) {
1648        // Translate and rotate the view so that the only visible part is the top left corner of
1649        // the view. It will form an isosceles right triangle with a long side length of 200 at the
1650        // bottom of the viewport.
1651        properties.setTranslationX(100);
1652        properties.setTranslationY(100);
1653        properties.setPivotX(0);
1654        properties.setPivotY(0);
1655        properties.setRotation(45);
1656    });
1657    // ceil(sqrt(2) / 2 * 200) = 142
1658    EXPECT_EQ(142u, observedData.layerWidth);
1659    EXPECT_EQ(142u, observedData.layerHeight);
1660    EXPECT_EQ(Rect(142, 142), observedData.rectClippedBounds);
1661    EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), observedData.rectMatrix);
1662}
1663
1664TEST(FrameBuilder, renderPropSaveLayerAlphaScale) {
1665    SaveLayerAlphaData observedData;
1666    testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) {
1667        properties.setPivotX(0);
1668        properties.setPivotY(0);
1669        properties.setScaleX(2);
1670        properties.setScaleY(0.5f);
1671    });
1672    EXPECT_EQ(100u, observedData.layerWidth);
1673    EXPECT_EQ(400u, observedData.layerHeight);
1674    EXPECT_EQ(Rect(100, 400), observedData.rectClippedBounds);
1675    EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), observedData.rectMatrix);
1676}
1677
1678} // namespace uirenderer
1679} // namespace android
1680