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