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