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