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