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