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