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