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