FrameBuilderTests.cpp revision f6c20e4b4232e30901676bcdf1aed64801d50c7e
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, renderNode) {
353    class RenderNodeTestRenderer : public TestRendererBase {
354    public:
355        void onRectOp(const RectOp& op, const BakedOpState& state) override {
356            switch(mIndex++) {
357            case 0:
358                EXPECT_EQ(Rect(200, 200), state.computedState.clippedBounds);
359                EXPECT_EQ(SK_ColorDKGRAY, op.paint->getColor());
360                break;
361            case 1:
362                EXPECT_EQ(Rect(50, 50, 150, 150), state.computedState.clippedBounds);
363                EXPECT_EQ(SK_ColorWHITE, op.paint->getColor());
364                break;
365            default:
366                ADD_FAILURE();
367            }
368        }
369    };
370
371    auto child = TestUtils::createNode(10, 10, 110, 110,
372            [](RenderProperties& props, RecordingCanvas& canvas) {
373        SkPaint paint;
374        paint.setColor(SK_ColorWHITE);
375        canvas.drawRect(0, 0, 100, 100, paint);
376    });
377
378    auto parent = TestUtils::createNode(0, 0, 200, 200,
379            [&child](RenderProperties& props, RecordingCanvas& canvas) {
380        SkPaint paint;
381        paint.setColor(SK_ColorDKGRAY);
382        canvas.drawRect(0, 0, 200, 200, paint);
383
384        canvas.save(SaveFlags::MatrixClip);
385        canvas.translate(40, 40);
386        canvas.drawRenderNode(child.get());
387        canvas.restore();
388    });
389
390    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
391            TestUtils::createSyncedNodeList(parent), sLightGeometry, nullptr);
392    RenderNodeTestRenderer renderer;
393    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
394}
395
396TEST(FrameBuilder, clipped) {
397    class ClippedTestRenderer : public TestRendererBase {
398    public:
399        void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override {
400            EXPECT_EQ(0, mIndex++);
401            EXPECT_EQ(Rect(10, 20, 30, 40), state.computedState.clippedBounds);
402            EXPECT_EQ(Rect(10, 20, 30, 40), state.computedState.clipRect());
403            EXPECT_TRUE(state.computedState.transform.isIdentity());
404        }
405    };
406
407    auto node = TestUtils::createNode(0, 0, 200, 200,
408            [](RenderProperties& props, RecordingCanvas& canvas) {
409        SkBitmap bitmap = TestUtils::createSkBitmap(200, 200);
410        canvas.drawBitmap(bitmap, 0, 0, nullptr);
411    });
412
413    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue,
414            SkRect::MakeLTRB(10, 20, 30, 40), // clip to small area, should see in receiver
415            200, 200, TestUtils::createSyncedNodeList(node), sLightGeometry, nullptr);
416    ClippedTestRenderer renderer;
417    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
418}
419
420TEST(FrameBuilder, saveLayer_simple) {
421    class SaveLayerSimpleTestRenderer : public TestRendererBase {
422    public:
423        OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override {
424            EXPECT_EQ(0, mIndex++);
425            EXPECT_EQ(180u, width);
426            EXPECT_EQ(180u, height);
427            return nullptr;
428        }
429        void endLayer() override {
430            EXPECT_EQ(2, mIndex++);
431        }
432        void onRectOp(const RectOp& op, const BakedOpState& state) override {
433            EXPECT_EQ(1, mIndex++);
434            EXPECT_EQ(Rect(10, 10, 190, 190), op.unmappedBounds);
435            EXPECT_EQ(Rect(180, 180), state.computedState.clippedBounds);
436            EXPECT_EQ(Rect(180, 180), state.computedState.clipRect());
437
438            Matrix4 expectedTransform;
439            expectedTransform.loadTranslate(-10, -10, 0);
440            EXPECT_MATRIX_APPROX_EQ(expectedTransform, state.computedState.transform);
441        }
442        void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
443            EXPECT_EQ(3, mIndex++);
444            EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds);
445            EXPECT_EQ(Rect(200, 200), state.computedState.clipRect());
446            EXPECT_TRUE(state.computedState.transform.isIdentity());
447        }
448    };
449
450    auto node = TestUtils::createNode(0, 0, 200, 200,
451            [](RenderProperties& props, RecordingCanvas& canvas) {
452        canvas.saveLayerAlpha(10, 10, 190, 190, 128, SaveFlags::ClipToLayer);
453        canvas.drawRect(10, 10, 190, 190, SkPaint());
454        canvas.restore();
455    });
456    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
457            TestUtils::createSyncedNodeList(node), sLightGeometry, nullptr);
458    SaveLayerSimpleTestRenderer renderer;
459    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
460    EXPECT_EQ(4, renderer.getIndex());
461}
462
463TEST(FrameBuilder, saveLayer_nested) {
464    /* saveLayer1 { rect1, saveLayer2 { rect2 } } will play back as:
465     * - startTemporaryLayer2, rect2 endLayer2
466     * - startTemporaryLayer1, rect1, drawLayer2, endLayer1
467     * - startFrame, layerOp1, endFrame
468     */
469    class SaveLayerNestedTestRenderer : public TestRendererBase {
470    public:
471        OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override {
472            const int index = mIndex++;
473            if (index == 0) {
474                EXPECT_EQ(400u, width);
475                EXPECT_EQ(400u, height);
476                return (OffscreenBuffer*) 0x400;
477            } else if (index == 3) {
478                EXPECT_EQ(800u, width);
479                EXPECT_EQ(800u, height);
480                return (OffscreenBuffer*) 0x800;
481            } else { ADD_FAILURE(); }
482            return (OffscreenBuffer*) nullptr;
483        }
484        void endLayer() override {
485            int index = mIndex++;
486            EXPECT_TRUE(index == 2 || index == 6);
487        }
488        void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
489            EXPECT_EQ(7, mIndex++);
490        }
491        void endFrame(const Rect& repaintRect) override {
492            EXPECT_EQ(9, mIndex++);
493        }
494        void onRectOp(const RectOp& op, const BakedOpState& state) override {
495            const int index = mIndex++;
496            if (index == 1) {
497                EXPECT_EQ(Rect(400, 400), op.unmappedBounds); // inner rect
498            } else if (index == 4) {
499                EXPECT_EQ(Rect(800, 800), op.unmappedBounds); // outer rect
500            } else { ADD_FAILURE(); }
501        }
502        void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
503            const int index = mIndex++;
504            if (index == 5) {
505                EXPECT_EQ((OffscreenBuffer*)0x400, *op.layerHandle);
506                EXPECT_EQ(Rect(400, 400), op.unmappedBounds); // inner layer
507            } else if (index == 8) {
508                EXPECT_EQ((OffscreenBuffer*)0x800, *op.layerHandle);
509                EXPECT_EQ(Rect(800, 800), op.unmappedBounds); // outer layer
510            } else { ADD_FAILURE(); }
511        }
512    };
513
514    auto node = TestUtils::createNode(0, 0, 800, 800,
515            [](RenderProperties& props, RecordingCanvas& canvas) {
516        canvas.saveLayerAlpha(0, 0, 800, 800, 128, SaveFlags::ClipToLayer);
517        {
518            canvas.drawRect(0, 0, 800, 800, SkPaint());
519            canvas.saveLayerAlpha(0, 0, 400, 400, 128, SaveFlags::ClipToLayer);
520            {
521                canvas.drawRect(0, 0, 400, 400, SkPaint());
522            }
523            canvas.restore();
524        }
525        canvas.restore();
526    });
527
528    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(800, 800), 800, 800,
529            TestUtils::createSyncedNodeList(node), sLightGeometry, nullptr);
530    SaveLayerNestedTestRenderer renderer;
531    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
532    EXPECT_EQ(10, renderer.getIndex());
533}
534
535TEST(FrameBuilder, saveLayer_contentRejection) {
536        auto node = TestUtils::createNode(0, 0, 200, 200,
537                [](RenderProperties& props, RecordingCanvas& canvas) {
538        canvas.save(SaveFlags::MatrixClip);
539        canvas.clipRect(200, 200, 400, 400, SkRegion::kIntersect_Op);
540        canvas.saveLayerAlpha(200, 200, 400, 400, 128, SaveFlags::ClipToLayer);
541
542        // draw within save layer may still be recorded, but shouldn't be drawn
543        canvas.drawRect(200, 200, 400, 400, SkPaint());
544
545        canvas.restore();
546        canvas.restore();
547    });
548    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
549            TestUtils::createSyncedNodeList(node), sLightGeometry, nullptr);
550
551    FailRenderer renderer;
552    // should see no ops, even within the layer, since the layer should be rejected
553    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
554}
555
556TEST(FrameBuilder, saveLayerUnclipped_simple) {
557    class SaveLayerUnclippedSimpleTestRenderer : public TestRendererBase {
558    public:
559        void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override {
560            EXPECT_EQ(0, mIndex++);
561            EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds);
562            EXPECT_CLIP_RECT(Rect(200, 200), state.computedState.clipState);
563            EXPECT_TRUE(state.computedState.transform.isIdentity());
564        }
565        void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override {
566            EXPECT_EQ(1, mIndex++);
567            ASSERT_NE(nullptr, op.paint);
568            ASSERT_EQ(SkXfermode::kClear_Mode, PaintUtils::getXfermodeDirect(op.paint));
569        }
570        void onRectOp(const RectOp& op, const BakedOpState& state) override {
571            EXPECT_EQ(2, mIndex++);
572            EXPECT_EQ(Rect(200, 200), op.unmappedBounds);
573            EXPECT_EQ(Rect(200, 200), state.computedState.clippedBounds);
574            EXPECT_EQ(Rect(200, 200), state.computedState.clipRect());
575            EXPECT_TRUE(state.computedState.transform.isIdentity());
576        }
577        void onCopyFromLayerOp(const CopyFromLayerOp& op, const BakedOpState& state) override {
578            EXPECT_EQ(3, mIndex++);
579            EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds);
580            EXPECT_CLIP_RECT(Rect(200, 200), state.computedState.clipState);
581            EXPECT_TRUE(state.computedState.transform.isIdentity());
582        }
583    };
584
585    auto node = TestUtils::createNode(0, 0, 200, 200,
586            [](RenderProperties& props, RecordingCanvas& canvas) {
587        canvas.saveLayerAlpha(10, 10, 190, 190, 128, (SaveFlags::Flags)(0));
588        canvas.drawRect(0, 0, 200, 200, SkPaint());
589        canvas.restore();
590    });
591    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
592            TestUtils::createSyncedNodeList(node), sLightGeometry, nullptr);
593    SaveLayerUnclippedSimpleTestRenderer renderer;
594    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
595    EXPECT_EQ(4, renderer.getIndex());
596}
597
598TEST(FrameBuilder, saveLayerUnclipped_mergedClears) {
599    class SaveLayerUnclippedMergedClearsTestRenderer : public TestRendererBase {
600    public:
601        void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override {
602            int index = mIndex++;
603            EXPECT_GT(4, index);
604            EXPECT_EQ(5, op.unmappedBounds.getWidth());
605            EXPECT_EQ(5, op.unmappedBounds.getHeight());
606            if (index == 0) {
607                EXPECT_EQ(Rect(10, 10), state.computedState.clippedBounds);
608            } else if (index == 1) {
609                EXPECT_EQ(Rect(190, 0, 200, 10), state.computedState.clippedBounds);
610            } else if (index == 2) {
611                EXPECT_EQ(Rect(0, 190, 10, 200), state.computedState.clippedBounds);
612            } else if (index == 3) {
613                EXPECT_EQ(Rect(190, 190, 200, 200), state.computedState.clippedBounds);
614            }
615        }
616        void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override {
617            EXPECT_EQ(4, mIndex++);
618            ASSERT_EQ(op.vertexCount, 16u);
619            for (size_t i = 0; i < op.vertexCount; i++) {
620                auto v = op.vertices[i];
621                EXPECT_TRUE(v.x == 0 || v.x == 10 || v.x == 190 || v.x == 200);
622                EXPECT_TRUE(v.y == 0 || v.y == 10 || v.y == 190 || v.y == 200);
623            }
624        }
625        void onRectOp(const RectOp& op, const BakedOpState& state) override {
626            EXPECT_EQ(5, mIndex++);
627        }
628        void onCopyFromLayerOp(const CopyFromLayerOp& op, const BakedOpState& state) override {
629            EXPECT_LT(5, mIndex++);
630        }
631    };
632
633    auto node = TestUtils::createNode(0, 0, 200, 200,
634            [](RenderProperties& props, RecordingCanvas& canvas) {
635
636        int restoreTo = canvas.save(SaveFlags::MatrixClip);
637        canvas.scale(2, 2);
638        canvas.saveLayerAlpha(0, 0, 5, 5, 128, SaveFlags::MatrixClip);
639        canvas.saveLayerAlpha(95, 0, 100, 5, 128, SaveFlags::MatrixClip);
640        canvas.saveLayerAlpha(0, 95, 5, 100, 128, SaveFlags::MatrixClip);
641        canvas.saveLayerAlpha(95, 95, 100, 100, 128, SaveFlags::MatrixClip);
642        canvas.drawRect(0, 0, 100, 100, SkPaint());
643        canvas.restoreToCount(restoreTo);
644    });
645    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
646            TestUtils::createSyncedNodeList(node), sLightGeometry, nullptr);
647    SaveLayerUnclippedMergedClearsTestRenderer renderer;
648    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
649    EXPECT_EQ(10, renderer.getIndex())
650            << "Expect 4 copyTos, 4 copyFroms, 1 clear SimpleRects, and 1 rect.";
651}
652
653TEST(FrameBuilder, saveLayerUnclipped_clearClip) {
654    class SaveLayerUnclippedClearClipTestRenderer : public TestRendererBase {
655    public:
656        void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override {
657            EXPECT_EQ(0, mIndex++);
658        }
659        void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override {
660            EXPECT_EQ(1, mIndex++);
661            ASSERT_NE(nullptr, op.paint);
662            EXPECT_EQ(SkXfermode::kClear_Mode, PaintUtils::getXfermodeDirect(op.paint));
663            EXPECT_EQ(Rect(50, 50, 150, 150), state.computedState.clippedBounds)
664                    << "Expect dirty rect as clip";
665            ASSERT_NE(nullptr, state.computedState.clipState);
666            EXPECT_EQ(Rect(50, 50, 150, 150), state.computedState.clipState->rect);
667            EXPECT_EQ(ClipMode::Rectangle, state.computedState.clipState->mode);
668        }
669        void onRectOp(const RectOp& op, const BakedOpState& state) override {
670            EXPECT_EQ(2, mIndex++);
671        }
672        void onCopyFromLayerOp(const CopyFromLayerOp& op, const BakedOpState& state) override {
673            EXPECT_EQ(3, mIndex++);
674        }
675    };
676
677    auto node = TestUtils::createNode(0, 0, 200, 200,
678            [](RenderProperties& props, RecordingCanvas& canvas) {
679        // save smaller than clip, so we get unclipped behavior
680        canvas.saveLayerAlpha(10, 10, 190, 190, 128, (SaveFlags::Flags)(0));
681        canvas.drawRect(0, 0, 200, 200, SkPaint());
682        canvas.restore();
683    });
684
685    // draw with partial screen dirty, and assert we see that rect later
686    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeLTRB(50, 50, 150, 150), 200, 200,
687            TestUtils::createSyncedNodeList(node), sLightGeometry, nullptr);
688    SaveLayerUnclippedClearClipTestRenderer renderer;
689    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
690    EXPECT_EQ(4, renderer.getIndex());
691}
692
693TEST(FrameBuilder, saveLayerUnclipped_reject) {
694    auto node = TestUtils::createNode(0, 0, 200, 200,
695            [](RenderProperties& props, RecordingCanvas& canvas) {
696        // unclipped savelayer + rect both in area that won't intersect with dirty
697        canvas.saveLayerAlpha(100, 100, 200, 200, 128, (SaveFlags::Flags)(0));
698        canvas.drawRect(100, 100, 200, 200, SkPaint());
699        canvas.restore();
700    });
701
702    // draw with partial screen dirty that doesn't intersect with savelayer
703    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 200, 200,
704            TestUtils::createSyncedNodeList(node), sLightGeometry, nullptr);
705    FailRenderer renderer;
706    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
707}
708
709/* saveLayerUnclipped { saveLayer { saveLayerUnclipped { rect } } } will play back as:
710 * - startTemporaryLayer, onCopyToLayer, onSimpleRects, onRect, onCopyFromLayer, endLayer
711 * - startFrame, onCopyToLayer, onSimpleRects, drawLayer, onCopyFromLayer, endframe
712 */
713TEST(FrameBuilder, saveLayerUnclipped_complex) {
714    class SaveLayerUnclippedComplexTestRenderer : public TestRendererBase {
715    public:
716        OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) {
717            EXPECT_EQ(0, mIndex++); // savelayer first
718            return (OffscreenBuffer*)0xabcd;
719        }
720        void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override {
721            int index = mIndex++;
722            EXPECT_TRUE(index == 1 || index == 7);
723        }
724        void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override {
725            int index = mIndex++;
726            EXPECT_TRUE(index == 2 || index == 8);
727        }
728        void onRectOp(const RectOp& op, const BakedOpState& state) override {
729            EXPECT_EQ(3, mIndex++);
730            Matrix4 expected;
731            expected.loadTranslate(-100, -100, 0);
732            EXPECT_EQ(Rect(100, 100, 200, 200), state.computedState.clippedBounds);
733            EXPECT_MATRIX_APPROX_EQ(expected, state.computedState.transform);
734        }
735        void onCopyFromLayerOp(const CopyFromLayerOp& op, const BakedOpState& state) override {
736            int index = mIndex++;
737            EXPECT_TRUE(index == 4 || index == 10);
738        }
739        void endLayer() override {
740            EXPECT_EQ(5, mIndex++);
741        }
742        void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
743            EXPECT_EQ(6, mIndex++);
744        }
745        void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
746            EXPECT_EQ(9, mIndex++);
747        }
748        void endFrame(const Rect& repaintRect) override {
749            EXPECT_EQ(11, mIndex++);
750        }
751    };
752
753    auto node = TestUtils::createNode(0, 0, 600, 600, // 500x500 triggers clipping
754            [](RenderProperties& props, RecordingCanvas& canvas) {
755        canvas.saveLayerAlpha(0, 0, 500, 500, 128, (SaveFlags::Flags)0); // unclipped
756        canvas.saveLayerAlpha(100, 100, 400, 400, 128, SaveFlags::ClipToLayer); // clipped
757        canvas.saveLayerAlpha(200, 200, 300, 300, 128, (SaveFlags::Flags)0); // unclipped
758        canvas.drawRect(200, 200, 300, 300, SkPaint());
759        canvas.restore();
760        canvas.restore();
761        canvas.restore();
762    });
763    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(600, 600), 600, 600,
764            TestUtils::createSyncedNodeList(node), sLightGeometry, nullptr);
765    SaveLayerUnclippedComplexTestRenderer renderer;
766    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
767    EXPECT_EQ(12, renderer.getIndex());
768}
769
770RENDERTHREAD_TEST(FrameBuilder, hwLayer_simple) {
771    class HwLayerSimpleTestRenderer : public TestRendererBase {
772    public:
773        void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override {
774            EXPECT_EQ(0, mIndex++);
775            EXPECT_EQ(100u, offscreenBuffer->viewportWidth);
776            EXPECT_EQ(100u, offscreenBuffer->viewportHeight);
777            EXPECT_EQ(Rect(25, 25, 75, 75), repaintRect);
778        }
779        void onRectOp(const RectOp& op, const BakedOpState& state) override {
780            EXPECT_EQ(1, mIndex++);
781
782            EXPECT_TRUE(state.computedState.transform.isIdentity())
783                    << "Transform should be reset within layer";
784
785            EXPECT_EQ(Rect(25, 25, 75, 75), state.computedState.clipRect())
786                    << "Damage rect should be used to clip layer content";
787        }
788        void endLayer() override {
789            EXPECT_EQ(2, mIndex++);
790        }
791        void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
792            EXPECT_EQ(3, mIndex++);
793        }
794        void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
795            EXPECT_EQ(4, mIndex++);
796        }
797        void endFrame(const Rect& repaintRect) override {
798            EXPECT_EQ(5, mIndex++);
799        }
800    };
801
802    auto node = TestUtils::createNode(10, 10, 110, 110,
803            [](RenderProperties& props, RecordingCanvas& canvas) {
804        props.mutateLayerProperties().setType(LayerType::RenderLayer);
805        SkPaint paint;
806        paint.setColor(SK_ColorWHITE);
807        canvas.drawRect(0, 0, 100, 100, paint);
808    });
809    OffscreenBuffer** layerHandle = node->getLayerHandle();
810
811    // create RenderNode's layer here in same way prepareTree would
812    OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 100, 100);
813    *layerHandle = &layer;
814
815    auto syncedNodeList = TestUtils::createSyncedNodeList(node);
816
817    // only enqueue partial damage
818    LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
819    layerUpdateQueue.enqueueLayerWithDamage(node.get(), Rect(25, 25, 75, 75));
820
821    FrameBuilder frameBuilder(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
822            syncedNodeList, sLightGeometry, nullptr);
823    HwLayerSimpleTestRenderer renderer;
824    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
825    EXPECT_EQ(6, renderer.getIndex());
826
827    // clean up layer pointer, so we can safely destruct RenderNode
828    *layerHandle = nullptr;
829}
830
831RENDERTHREAD_TEST(FrameBuilder, hwLayer_complex) {
832    /* parentLayer { greyRect, saveLayer { childLayer { whiteRect } } } will play back as:
833     * - startRepaintLayer(child), rect(grey), endLayer
834     * - startTemporaryLayer, drawLayer(child), endLayer
835     * - startRepaintLayer(parent), rect(white), drawLayer(saveLayer), endLayer
836     * - startFrame, drawLayer(parent), endLayerb
837     */
838    class HwLayerComplexTestRenderer : public TestRendererBase {
839    public:
840        OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) {
841            EXPECT_EQ(3, mIndex++); // savelayer first
842            return (OffscreenBuffer*)0xabcd;
843        }
844        void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override {
845            int index = mIndex++;
846            if (index == 0) {
847                // starting inner layer
848                EXPECT_EQ(100u, offscreenBuffer->viewportWidth);
849                EXPECT_EQ(100u, offscreenBuffer->viewportHeight);
850            } else if (index == 6) {
851                // starting outer layer
852                EXPECT_EQ(200u, offscreenBuffer->viewportWidth);
853                EXPECT_EQ(200u, offscreenBuffer->viewportHeight);
854            } else { ADD_FAILURE(); }
855        }
856        void onRectOp(const RectOp& op, const BakedOpState& state) override {
857            int index = mIndex++;
858            if (index == 1) {
859                // inner layer's rect (white)
860                EXPECT_EQ(SK_ColorWHITE, op.paint->getColor());
861            } else if (index == 7) {
862                // outer layer's rect (grey)
863                EXPECT_EQ(SK_ColorDKGRAY, op.paint->getColor());
864            } else { ADD_FAILURE(); }
865        }
866        void endLayer() override {
867            int index = mIndex++;
868            EXPECT_TRUE(index == 2 || index == 5 || index == 9);
869        }
870        void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
871            EXPECT_EQ(10, mIndex++);
872        }
873        void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
874            OffscreenBuffer* layer = *op.layerHandle;
875            int index = mIndex++;
876            if (index == 4) {
877                EXPECT_EQ(100u, layer->viewportWidth);
878                EXPECT_EQ(100u, layer->viewportHeight);
879            } else if (index == 8) {
880                EXPECT_EQ((OffscreenBuffer*)0xabcd, *op.layerHandle);
881            } else if (index == 11) {
882                EXPECT_EQ(200u, layer->viewportWidth);
883                EXPECT_EQ(200u, layer->viewportHeight);
884            } else { ADD_FAILURE(); }
885        }
886        void endFrame(const Rect& repaintRect) override {
887            EXPECT_EQ(12, mIndex++);
888        }
889    };
890
891    auto child = TestUtils::createNode(50, 50, 150, 150,
892            [](RenderProperties& props, RecordingCanvas& canvas) {
893        props.mutateLayerProperties().setType(LayerType::RenderLayer);
894        SkPaint paint;
895        paint.setColor(SK_ColorWHITE);
896        canvas.drawRect(0, 0, 100, 100, paint);
897    });
898    OffscreenBuffer childLayer(renderThread.renderState(), Caches::getInstance(), 100, 100);
899    *(child->getLayerHandle()) = &childLayer;
900
901    RenderNode* childPtr = child.get();
902    auto parent = TestUtils::createNode(0, 0, 200, 200,
903            [childPtr](RenderProperties& props, RecordingCanvas& canvas) {
904        props.mutateLayerProperties().setType(LayerType::RenderLayer);
905        SkPaint paint;
906        paint.setColor(SK_ColorDKGRAY);
907        canvas.drawRect(0, 0, 200, 200, paint);
908
909        canvas.saveLayerAlpha(50, 50, 150, 150, 128, SaveFlags::ClipToLayer);
910        canvas.drawRenderNode(childPtr);
911        canvas.restore();
912    });
913    OffscreenBuffer parentLayer(renderThread.renderState(), Caches::getInstance(), 200, 200);
914    *(parent->getLayerHandle()) = &parentLayer;
915
916    auto syncedList = TestUtils::createSyncedNodeList(parent);
917
918    LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
919    layerUpdateQueue.enqueueLayerWithDamage(child.get(), Rect(100, 100));
920    layerUpdateQueue.enqueueLayerWithDamage(parent.get(), Rect(200, 200));
921
922    FrameBuilder frameBuilder(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
923            syncedList, sLightGeometry, nullptr);
924    HwLayerComplexTestRenderer renderer;
925    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
926    EXPECT_EQ(13, renderer.getIndex());
927
928    // clean up layer pointers, so we can safely destruct RenderNodes
929    *(child->getLayerHandle()) = nullptr;
930    *(parent->getLayerHandle()) = nullptr;
931}
932
933static void drawOrderedRect(RecordingCanvas* canvas, uint8_t expectedDrawOrder) {
934    SkPaint paint;
935    paint.setColor(SkColorSetARGB(256, 0, 0, expectedDrawOrder)); // order put in blue channel
936    canvas->drawRect(0, 0, 100, 100, paint);
937}
938static void drawOrderedNode(RecordingCanvas* canvas, uint8_t expectedDrawOrder, float z) {
939    auto node = TestUtils::createNode(0, 0, 100, 100,
940            [expectedDrawOrder](RenderProperties& props, RecordingCanvas& canvas) {
941        drawOrderedRect(&canvas, expectedDrawOrder);
942    });
943    node->mutateStagingProperties().setTranslationZ(z);
944    node->setPropertyFieldsDirty(RenderNode::TRANSLATION_Z);
945    canvas->drawRenderNode(node.get()); // canvas takes reference/sole ownership
946}
947TEST(FrameBuilder, zReorder) {
948    class ZReorderTestRenderer : public TestRendererBase {
949    public:
950        void onRectOp(const RectOp& op, const BakedOpState& state) override {
951            int expectedOrder = SkColorGetB(op.paint->getColor()); // extract order from blue channel
952            EXPECT_EQ(expectedOrder, mIndex++) << "An op was drawn out of order";
953        }
954    };
955
956    auto parent = TestUtils::createNode(0, 0, 100, 100,
957            [](RenderProperties& props, RecordingCanvas& canvas) {
958        drawOrderedNode(&canvas, 0, 10.0f); // in reorder=false at this point, so played inorder
959        drawOrderedRect(&canvas, 1);
960        canvas.insertReorderBarrier(true);
961        drawOrderedNode(&canvas, 6, 2.0f);
962        drawOrderedRect(&canvas, 3);
963        drawOrderedNode(&canvas, 4, 0.0f);
964        drawOrderedRect(&canvas, 5);
965        drawOrderedNode(&canvas, 2, -2.0f);
966        drawOrderedNode(&canvas, 7, 2.0f);
967        canvas.insertReorderBarrier(false);
968        drawOrderedRect(&canvas, 8);
969        drawOrderedNode(&canvas, 9, -10.0f); // in reorder=false at this point, so played inorder
970    });
971    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100,
972            TestUtils::createSyncedNodeList(parent), sLightGeometry, nullptr);
973    ZReorderTestRenderer renderer;
974    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
975    EXPECT_EQ(10, renderer.getIndex());
976};
977
978TEST(FrameBuilder, projectionReorder) {
979    static const int scrollX = 5;
980    static const int scrollY = 10;
981    class ProjectionReorderTestRenderer : public TestRendererBase {
982    public:
983        void onRectOp(const RectOp& op, const BakedOpState& state) override {
984            const int index = mIndex++;
985
986            Matrix4 expectedMatrix;
987            switch (index) {
988            case 0:
989                EXPECT_EQ(Rect(100, 100), op.unmappedBounds);
990                EXPECT_EQ(SK_ColorWHITE, op.paint->getColor());
991                expectedMatrix.loadIdentity();
992                EXPECT_EQ(nullptr, state.computedState.localProjectionPathMask);
993                break;
994            case 1:
995                EXPECT_EQ(Rect(-10, -10, 60, 60), op.unmappedBounds);
996                EXPECT_EQ(SK_ColorDKGRAY, op.paint->getColor());
997                expectedMatrix.loadTranslate(50 - scrollX, 50 - scrollY, 0);
998                ASSERT_NE(nullptr, state.computedState.localProjectionPathMask);
999                EXPECT_EQ(Rect(-35, -30, 45, 50),
1000                        Rect(state.computedState.localProjectionPathMask->getBounds()));
1001                break;
1002            case 2:
1003                EXPECT_EQ(Rect(100, 50), op.unmappedBounds);
1004                EXPECT_EQ(SK_ColorBLUE, op.paint->getColor());
1005                expectedMatrix.loadTranslate(-scrollX, 50 - scrollY, 0);
1006                EXPECT_EQ(nullptr, state.computedState.localProjectionPathMask);
1007                break;
1008            default:
1009                ADD_FAILURE();
1010            }
1011            EXPECT_EQ(expectedMatrix, state.computedState.transform);
1012        }
1013    };
1014
1015    /**
1016     * Construct a tree of nodes, where the root (A) has a receiver background (B), and a child (C)
1017     * with a projecting child (P) of its own. P would normally draw between B and C's "background"
1018     * draw, but because it is projected backwards, it's drawn in between B and C.
1019     *
1020     * The parent is scrolled by scrollX/scrollY, but this does not affect the background
1021     * (which isn't affected by scroll).
1022     */
1023    auto receiverBackground = TestUtils::createNode(0, 0, 100, 100,
1024            [](RenderProperties& properties, RecordingCanvas& canvas) {
1025        properties.setProjectionReceiver(true);
1026        // scroll doesn't apply to background, so undone via translationX/Y
1027        // NOTE: translationX/Y only! no other transform properties may be set for a proj receiver!
1028        properties.setTranslationX(scrollX);
1029        properties.setTranslationY(scrollY);
1030
1031        SkPaint paint;
1032        paint.setColor(SK_ColorWHITE);
1033        canvas.drawRect(0, 0, 100, 100, paint);
1034    });
1035    auto projectingRipple = TestUtils::createNode(50, 0, 100, 50,
1036            [](RenderProperties& properties, RecordingCanvas& canvas) {
1037        properties.setProjectBackwards(true);
1038        properties.setClipToBounds(false);
1039        SkPaint paint;
1040        paint.setColor(SK_ColorDKGRAY);
1041        canvas.drawRect(-10, -10, 60, 60, paint);
1042    });
1043    auto child = TestUtils::createNode(0, 50, 100, 100,
1044            [&projectingRipple](RenderProperties& properties, RecordingCanvas& canvas) {
1045        SkPaint paint;
1046        paint.setColor(SK_ColorBLUE);
1047        canvas.drawRect(0, 0, 100, 50, paint);
1048        canvas.drawRenderNode(projectingRipple.get());
1049    });
1050    auto parent = TestUtils::createNode(0, 0, 100, 100,
1051            [&receiverBackground, &child](RenderProperties& properties, RecordingCanvas& canvas) {
1052        // Set a rect outline for the projecting ripple to be masked against.
1053        properties.mutableOutline().setRoundRect(10, 10, 90, 90, 5, 1.0f);
1054
1055        canvas.save(SaveFlags::MatrixClip);
1056        canvas.translate(-scrollX, -scrollY); // Apply scroll (note: bg undoes this internally)
1057        canvas.drawRenderNode(receiverBackground.get());
1058        canvas.drawRenderNode(child.get());
1059        canvas.restore();
1060    });
1061
1062    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100,
1063            TestUtils::createSyncedNodeList(parent), sLightGeometry, nullptr);
1064    ProjectionReorderTestRenderer renderer;
1065    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1066    EXPECT_EQ(3, renderer.getIndex());
1067}
1068
1069RENDERTHREAD_TEST(FrameBuilder, projectionHwLayer) {
1070    static const int scrollX = 5;
1071    static const int scrollY = 10;
1072    class ProjectionHwLayerTestRenderer : public TestRendererBase {
1073    public:
1074        void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override {
1075            EXPECT_EQ(0, mIndex++);
1076        }
1077        void onArcOp(const ArcOp& op, const BakedOpState& state) override {
1078            EXPECT_EQ(1, mIndex++);
1079            ASSERT_EQ(nullptr, state.computedState.localProjectionPathMask);
1080        }
1081        void endLayer() override {
1082            EXPECT_EQ(2, mIndex++);
1083        }
1084        void onRectOp(const RectOp& op, const BakedOpState& state) override {
1085            EXPECT_EQ(3, mIndex++);
1086            ASSERT_EQ(nullptr, state.computedState.localProjectionPathMask);
1087        }
1088        void onOvalOp(const OvalOp& op, const BakedOpState& state) override {
1089            EXPECT_EQ(4, mIndex++);
1090            ASSERT_NE(nullptr, state.computedState.localProjectionPathMask);
1091            Matrix4 expected;
1092            expected.loadTranslate(100 - scrollX, 100 - scrollY, 0);
1093            EXPECT_EQ(expected, state.computedState.transform);
1094            EXPECT_EQ(Rect(-85, -80, 295, 300),
1095                    Rect(state.computedState.localProjectionPathMask->getBounds()));
1096        }
1097        void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
1098            EXPECT_EQ(5, mIndex++);
1099            ASSERT_EQ(nullptr, state.computedState.localProjectionPathMask);
1100        }
1101    };
1102    auto receiverBackground = TestUtils::createNode(0, 0, 400, 400,
1103            [](RenderProperties& properties, RecordingCanvas& canvas) {
1104        properties.setProjectionReceiver(true);
1105        // scroll doesn't apply to background, so undone via translationX/Y
1106        // NOTE: translationX/Y only! no other transform properties may be set for a proj receiver!
1107        properties.setTranslationX(scrollX);
1108        properties.setTranslationY(scrollY);
1109
1110        canvas.drawRect(0, 0, 400, 400, SkPaint());
1111    });
1112    auto projectingRipple = TestUtils::createNode(0, 0, 200, 200,
1113            [](RenderProperties& properties, RecordingCanvas& canvas) {
1114        properties.setProjectBackwards(true);
1115        properties.setClipToBounds(false);
1116        canvas.drawOval(100, 100, 300, 300, SkPaint()); // drawn mostly out of layer bounds
1117    });
1118    auto child = TestUtils::createNode(100, 100, 300, 300,
1119            [&projectingRipple](RenderProperties& properties, RecordingCanvas& canvas) {
1120        properties.mutateLayerProperties().setType(LayerType::RenderLayer);
1121        canvas.drawRenderNode(projectingRipple.get());
1122        canvas.drawArc(0, 0, 200, 200, 0.0f, 280.0f, true, SkPaint());
1123    });
1124    auto parent = TestUtils::createNode(0, 0, 400, 400,
1125            [&receiverBackground, &child](RenderProperties& properties, RecordingCanvas& canvas) {
1126        // Set a rect outline for the projecting ripple to be masked against.
1127        properties.mutableOutline().setRoundRect(10, 10, 390, 390, 0, 1.0f);
1128        canvas.translate(-scrollX, -scrollY); // Apply scroll (note: bg undoes this internally)
1129        canvas.drawRenderNode(receiverBackground.get());
1130        canvas.drawRenderNode(child.get());
1131    });
1132
1133    OffscreenBuffer** layerHandle = child->getLayerHandle();
1134
1135    // create RenderNode's layer here in same way prepareTree would, setting windowTransform
1136    OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 200, 200);
1137    Matrix4 windowTransform;
1138    windowTransform.loadTranslate(100, 100, 0); // total transform of layer's origin
1139    layer.setWindowTransform(windowTransform);
1140    *layerHandle = &layer;
1141
1142    auto syncedList = TestUtils::createSyncedNodeList(parent);
1143    LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
1144    layerUpdateQueue.enqueueLayerWithDamage(child.get(), Rect(200, 200));
1145    FrameBuilder frameBuilder(layerUpdateQueue, SkRect::MakeWH(400, 400), 400, 400,
1146            syncedList, sLightGeometry, nullptr);
1147    ProjectionHwLayerTestRenderer renderer;
1148    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1149    EXPECT_EQ(6, renderer.getIndex());
1150
1151    // clean up layer pointer, so we can safely destruct RenderNode
1152    *layerHandle = nullptr;
1153}
1154
1155RENDERTHREAD_TEST(FrameBuilder, projectionChildScroll) {
1156    static const int scrollX = 500000;
1157    static const int scrollY = 0;
1158    class ProjectionChildScrollTestRenderer : public TestRendererBase {
1159    public:
1160        void onRectOp(const RectOp& op, const BakedOpState& state) override {
1161            EXPECT_EQ(0, mIndex++);
1162            EXPECT_TRUE(state.computedState.transform.isIdentity());
1163        }
1164        void onOvalOp(const OvalOp& op, const BakedOpState& state) override {
1165            EXPECT_EQ(1, mIndex++);
1166            ASSERT_NE(nullptr, state.computedState.clipState);
1167            ASSERT_EQ(ClipMode::Rectangle, state.computedState.clipState->mode);
1168            ASSERT_EQ(Rect(400, 400), state.computedState.clipState->rect);
1169            EXPECT_TRUE(state.computedState.transform.isIdentity());
1170        }
1171    };
1172    auto receiverBackground = TestUtils::createNode(0, 0, 400, 400,
1173            [](RenderProperties& properties, RecordingCanvas& canvas) {
1174        properties.setProjectionReceiver(true);
1175        canvas.drawRect(0, 0, 400, 400, SkPaint());
1176    });
1177    auto projectingRipple = TestUtils::createNode(0, 0, 200, 200,
1178            [](RenderProperties& properties, RecordingCanvas& canvas) {
1179        // scroll doesn't apply to background, so undone via translationX/Y
1180        // NOTE: translationX/Y only! no other transform properties may be set for a proj receiver!
1181        properties.setTranslationX(scrollX);
1182        properties.setTranslationY(scrollY);
1183        properties.setProjectBackwards(true);
1184        properties.setClipToBounds(false);
1185        canvas.drawOval(0, 0, 200, 200, SkPaint());
1186    });
1187    auto child = TestUtils::createNode(0, 0, 400, 400,
1188            [&projectingRipple](RenderProperties& properties, RecordingCanvas& canvas) {
1189        // Record time clip will be ignored by projectee
1190        canvas.clipRect(100, 100, 300, 300, SkRegion::kIntersect_Op);
1191
1192        canvas.translate(-scrollX, -scrollY); // Apply scroll (note: bg undoes this internally)
1193        canvas.drawRenderNode(projectingRipple.get());
1194    });
1195    auto parent = TestUtils::createNode(0, 0, 400, 400,
1196            [&receiverBackground, &child](RenderProperties& properties, RecordingCanvas& canvas) {
1197        canvas.drawRenderNode(receiverBackground.get());
1198        canvas.drawRenderNode(child.get());
1199    });
1200
1201    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(400, 400), 400, 400,
1202            TestUtils::createSyncedNodeList(parent), sLightGeometry, nullptr);
1203    ProjectionChildScrollTestRenderer renderer;
1204    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1205    EXPECT_EQ(2, renderer.getIndex());
1206}
1207
1208// creates a 100x100 shadow casting node with provided translationZ
1209static sp<RenderNode> createWhiteRectShadowCaster(float translationZ) {
1210    return TestUtils::createNode(0, 0, 100, 100,
1211            [translationZ](RenderProperties& properties, RecordingCanvas& canvas) {
1212        properties.setTranslationZ(translationZ);
1213        properties.mutableOutline().setRoundRect(0, 0, 100, 100, 0.0f, 1.0f);
1214        SkPaint paint;
1215        paint.setColor(SK_ColorWHITE);
1216        canvas.drawRect(0, 0, 100, 100, paint);
1217    });
1218}
1219
1220RENDERTHREAD_TEST(FrameBuilder, shadow) {
1221    class ShadowTestRenderer : public TestRendererBase {
1222    public:
1223        void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
1224            EXPECT_EQ(0, mIndex++);
1225            EXPECT_FLOAT_EQ(1.0f, op.casterAlpha);
1226            EXPECT_TRUE(op.shadowTask->casterPerimeter.isRect(nullptr));
1227            EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), op.shadowTask->transformXY);
1228
1229            Matrix4 expectedZ;
1230            expectedZ.loadTranslate(0, 0, 5);
1231            EXPECT_MATRIX_APPROX_EQ(expectedZ, op.shadowTask->transformZ);
1232        }
1233        void onRectOp(const RectOp& op, const BakedOpState& state) override {
1234            EXPECT_EQ(1, mIndex++);
1235        }
1236    };
1237
1238    auto parent = TestUtils::createNode(0, 0, 200, 200,
1239            [](RenderProperties& props, RecordingCanvas& canvas) {
1240        canvas.insertReorderBarrier(true);
1241        canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
1242    });
1243
1244    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
1245            TestUtils::createSyncedNodeList(parent), sLightGeometry, &Caches::getInstance());
1246    ShadowTestRenderer renderer;
1247    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1248    EXPECT_EQ(2, renderer.getIndex());
1249}
1250
1251RENDERTHREAD_TEST(FrameBuilder, shadowSaveLayer) {
1252    class ShadowSaveLayerTestRenderer : public TestRendererBase {
1253    public:
1254        OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override {
1255            EXPECT_EQ(0, mIndex++);
1256            return nullptr;
1257        }
1258        void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
1259            EXPECT_EQ(1, mIndex++);
1260            EXPECT_FLOAT_EQ(50, op.shadowTask->lightCenter.x);
1261            EXPECT_FLOAT_EQ(40, op.shadowTask->lightCenter.y);
1262        }
1263        void onRectOp(const RectOp& op, const BakedOpState& state) override {
1264            EXPECT_EQ(2, mIndex++);
1265        }
1266        void endLayer() override {
1267            EXPECT_EQ(3, mIndex++);
1268        }
1269        void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
1270            EXPECT_EQ(4, mIndex++);
1271        }
1272    };
1273
1274    auto parent = TestUtils::createNode(0, 0, 200, 200,
1275            [](RenderProperties& props, RecordingCanvas& canvas) {
1276        // save/restore outside of reorderBarrier, so they don't get moved out of place
1277        canvas.translate(20, 10);
1278        int count = canvas.saveLayerAlpha(30, 50, 130, 150, 128, SaveFlags::ClipToLayer);
1279        canvas.insertReorderBarrier(true);
1280        canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
1281        canvas.insertReorderBarrier(false);
1282        canvas.restoreToCount(count);
1283    });
1284
1285    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
1286            TestUtils::createSyncedNodeList(parent),
1287            (FrameBuilder::LightGeometry) {{ 100, 100, 100 }, 50},
1288            &Caches::getInstance());
1289    ShadowSaveLayerTestRenderer renderer;
1290    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1291    EXPECT_EQ(5, renderer.getIndex());
1292}
1293
1294RENDERTHREAD_TEST(FrameBuilder, shadowHwLayer) {
1295    class ShadowHwLayerTestRenderer : public TestRendererBase {
1296    public:
1297        void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override {
1298            EXPECT_EQ(0, mIndex++);
1299        }
1300        void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
1301            EXPECT_EQ(1, mIndex++);
1302            EXPECT_FLOAT_EQ(50, op.shadowTask->lightCenter.x);
1303            EXPECT_FLOAT_EQ(40, op.shadowTask->lightCenter.y);
1304            EXPECT_FLOAT_EQ(30, op.shadowTask->lightRadius);
1305        }
1306        void onRectOp(const RectOp& op, const BakedOpState& state) override {
1307            EXPECT_EQ(2, mIndex++);
1308        }
1309        void endLayer() override {
1310            EXPECT_EQ(3, mIndex++);
1311        }
1312        void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
1313            EXPECT_EQ(4, mIndex++);
1314        }
1315    };
1316
1317    auto parent = TestUtils::createNode(50, 60, 150, 160,
1318            [](RenderProperties& props, RecordingCanvas& canvas) {
1319        props.mutateLayerProperties().setType(LayerType::RenderLayer);
1320        canvas.insertReorderBarrier(true);
1321        canvas.save(SaveFlags::MatrixClip);
1322        canvas.translate(20, 10);
1323        canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
1324        canvas.restore();
1325    });
1326    OffscreenBuffer** layerHandle = parent->getLayerHandle();
1327
1328    // create RenderNode's layer here in same way prepareTree would, setting windowTransform
1329    OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 100, 100);
1330    Matrix4 windowTransform;
1331    windowTransform.loadTranslate(50, 60, 0); // total transform of layer's origin
1332    layer.setWindowTransform(windowTransform);
1333    *layerHandle = &layer;
1334
1335    auto syncedList = TestUtils::createSyncedNodeList(parent);
1336    LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
1337    layerUpdateQueue.enqueueLayerWithDamage(parent.get(), Rect(100, 100));
1338    FrameBuilder frameBuilder(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
1339            syncedList,
1340            (FrameBuilder::LightGeometry) {{ 100, 100, 100 }, 30},
1341            &Caches::getInstance());
1342    ShadowHwLayerTestRenderer renderer;
1343    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1344    EXPECT_EQ(5, renderer.getIndex());
1345
1346    // clean up layer pointer, so we can safely destruct RenderNode
1347    *layerHandle = nullptr;
1348}
1349
1350TEST(FrameBuilder, shadowLayering) {
1351    class ShadowLayeringTestRenderer : public TestRendererBase {
1352    public:
1353        void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
1354            int index = mIndex++;
1355            EXPECT_TRUE(index == 0 || index == 1);
1356        }
1357        void onRectOp(const RectOp& op, const BakedOpState& state) override {
1358            int index = mIndex++;
1359            EXPECT_TRUE(index == 2 || index == 3);
1360        }
1361    };
1362    auto parent = TestUtils::createNode(0, 0, 200, 200,
1363            [](RenderProperties& props, RecordingCanvas& canvas) {
1364        canvas.insertReorderBarrier(true);
1365        canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
1366        canvas.drawRenderNode(createWhiteRectShadowCaster(5.0001f).get());
1367    });
1368
1369    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
1370            TestUtils::createSyncedNodeList(parent),
1371            (FrameBuilder::LightGeometry) {{ 100, 100, 100 }, 50},
1372            &Caches::getInstance());
1373    ShadowLayeringTestRenderer renderer;
1374    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1375    EXPECT_EQ(4, renderer.getIndex());
1376}
1377
1378static void testProperty(std::function<void(RenderProperties&)> propSetupCallback,
1379        std::function<void(const RectOp&, const BakedOpState&)> opValidateCallback) {
1380    class PropertyTestRenderer : public TestRendererBase {
1381    public:
1382        PropertyTestRenderer(std::function<void(const RectOp&, const BakedOpState&)> callback)
1383                : mCallback(callback) {}
1384        void onRectOp(const RectOp& op, const BakedOpState& state) override {
1385            EXPECT_EQ(mIndex++, 0);
1386            mCallback(op, state);
1387        }
1388        std::function<void(const RectOp&, const BakedOpState&)> mCallback;
1389    };
1390
1391    auto node = TestUtils::createNode(0, 0, 100, 100,
1392            [propSetupCallback](RenderProperties& props, RecordingCanvas& canvas) {
1393        propSetupCallback(props);
1394        SkPaint paint;
1395        paint.setColor(SK_ColorWHITE);
1396        canvas.drawRect(0, 0, 100, 100, paint);
1397    });
1398
1399    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 200, 200,
1400            TestUtils::createSyncedNodeList(node), sLightGeometry, nullptr);
1401    PropertyTestRenderer renderer(opValidateCallback);
1402    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1403    EXPECT_EQ(1, renderer.getIndex()) << "Should have seen one op";
1404}
1405
1406TEST(FrameBuilder, renderPropOverlappingRenderingAlpha) {
1407    testProperty([](RenderProperties& properties) {
1408        properties.setAlpha(0.5f);
1409        properties.setHasOverlappingRendering(false);
1410    }, [](const RectOp& op, const BakedOpState& state) {
1411        EXPECT_EQ(0.5f, state.alpha) << "Alpha should be applied directly to op";
1412    });
1413}
1414
1415TEST(FrameBuilder, renderPropClipping) {
1416    testProperty([](RenderProperties& properties) {
1417        properties.setClipToBounds(true);
1418        properties.setClipBounds(Rect(10, 20, 300, 400));
1419    }, [](const RectOp& op, const BakedOpState& state) {
1420        EXPECT_EQ(Rect(10, 20, 100, 100), state.computedState.clippedBounds)
1421                << "Clip rect should be intersection of node bounds and clip bounds";
1422    });
1423}
1424
1425TEST(FrameBuilder, renderPropRevealClip) {
1426    testProperty([](RenderProperties& properties) {
1427        properties.mutableRevealClip().set(true, 50, 50, 25);
1428    }, [](const RectOp& op, const BakedOpState& state) {
1429        ASSERT_NE(nullptr, state.roundRectClipState);
1430        EXPECT_TRUE(state.roundRectClipState->highPriority);
1431        EXPECT_EQ(25, state.roundRectClipState->radius);
1432        EXPECT_EQ(Rect(50, 50, 50, 50), state.roundRectClipState->innerRect);
1433    });
1434}
1435
1436TEST(FrameBuilder, renderPropOutlineClip) {
1437    testProperty([](RenderProperties& properties) {
1438        properties.mutableOutline().setShouldClip(true);
1439        properties.mutableOutline().setRoundRect(10, 20, 30, 40, 5.0f, 0.5f);
1440    }, [](const RectOp& op, const BakedOpState& state) {
1441        ASSERT_NE(nullptr, state.roundRectClipState);
1442        EXPECT_FALSE(state.roundRectClipState->highPriority);
1443        EXPECT_EQ(5, state.roundRectClipState->radius);
1444        EXPECT_EQ(Rect(15, 25, 25, 35), state.roundRectClipState->innerRect);
1445    });
1446}
1447
1448TEST(FrameBuilder, renderPropTransform) {
1449    testProperty([](RenderProperties& properties) {
1450        properties.setLeftTopRightBottom(10, 10, 110, 110);
1451
1452        SkMatrix staticMatrix = SkMatrix::MakeScale(1.2f, 1.2f);
1453        properties.setStaticMatrix(&staticMatrix);
1454
1455        // ignored, since static overrides animation
1456        SkMatrix animationMatrix = SkMatrix::MakeTrans(15, 15);
1457        properties.setAnimationMatrix(&animationMatrix);
1458
1459        properties.setTranslationX(10);
1460        properties.setTranslationY(20);
1461        properties.setScaleX(0.5f);
1462        properties.setScaleY(0.7f);
1463    }, [](const RectOp& op, const BakedOpState& state) {
1464        Matrix4 matrix;
1465        matrix.loadTranslate(10, 10, 0); // left, top
1466        matrix.scale(1.2f, 1.2f, 1); // static matrix
1467        // ignore animation matrix, since static overrides it
1468
1469        // translation xy
1470        matrix.translate(10, 20);
1471
1472        // scale xy (from default pivot - center)
1473        matrix.translate(50, 50);
1474        matrix.scale(0.5f, 0.7f, 1);
1475        matrix.translate(-50, -50);
1476        EXPECT_MATRIX_APPROX_EQ(matrix, state.computedState.transform)
1477                << "Op draw matrix must match expected combination of transformation properties";
1478    });
1479}
1480
1481struct SaveLayerAlphaData {
1482    uint32_t layerWidth = 0;
1483    uint32_t layerHeight = 0;
1484    Rect rectClippedBounds;
1485    Matrix4 rectMatrix;
1486};
1487/**
1488 * Constructs a view to hit the temporary layer alpha property implementation:
1489 *     a) 0 < alpha < 1
1490 *     b) too big for layer (larger than maxTextureSize)
1491 *     c) overlapping rendering content
1492 * returning observed data about layer size and content clip/transform.
1493 *
1494 * Used to validate clipping behavior of temporary layer, where requested layer size is reduced
1495 * (for efficiency, and to fit in layer size constraints) based on parent clip.
1496 */
1497void testSaveLayerAlphaClip(SaveLayerAlphaData* outObservedData,
1498        std::function<void(RenderProperties&)> propSetupCallback) {
1499    class SaveLayerAlphaClipTestRenderer : public TestRendererBase {
1500    public:
1501        SaveLayerAlphaClipTestRenderer(SaveLayerAlphaData* outData)
1502                : mOutData(outData) {}
1503
1504        OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override {
1505            EXPECT_EQ(0, mIndex++);
1506            mOutData->layerWidth = width;
1507            mOutData->layerHeight = height;
1508            return nullptr;
1509        }
1510        void onRectOp(const RectOp& op, const BakedOpState& state) override {
1511            EXPECT_EQ(1, mIndex++);
1512
1513            mOutData->rectClippedBounds = state.computedState.clippedBounds;
1514            mOutData->rectMatrix = state.computedState.transform;
1515        }
1516        void endLayer() override {
1517            EXPECT_EQ(2, mIndex++);
1518        }
1519        void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
1520            EXPECT_EQ(3, mIndex++);
1521        }
1522    private:
1523        SaveLayerAlphaData* mOutData;
1524    };
1525
1526    ASSERT_GT(10000, DeviceInfo::get()->maxTextureSize())
1527            << "Node must be bigger than max texture size to exercise saveLayer codepath";
1528    auto node = TestUtils::createNode(0, 0, 10000, 10000,
1529            [&propSetupCallback](RenderProperties& properties, RecordingCanvas& canvas) {
1530        properties.setHasOverlappingRendering(true);
1531        properties.setAlpha(0.5f); // force saveLayer, since too big for HW layer
1532        // apply other properties
1533        propSetupCallback(properties);
1534
1535        SkPaint paint;
1536        paint.setColor(SK_ColorWHITE);
1537        canvas.drawRect(0, 0, 10000, 10000, paint);
1538    });
1539    auto nodes = TestUtils::createSyncedNodeList(node); // sync before querying height
1540
1541    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
1542            nodes, sLightGeometry, nullptr);
1543    SaveLayerAlphaClipTestRenderer renderer(outObservedData);
1544    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1545
1546    // assert, since output won't be valid if we haven't seen a save layer triggered
1547    ASSERT_EQ(4, renderer.getIndex()) << "Test must trigger saveLayer alpha behavior.";
1548}
1549
1550TEST(FrameBuilder, renderPropSaveLayerAlphaClipBig) {
1551    SaveLayerAlphaData observedData;
1552    testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) {
1553        properties.setTranslationX(10); // offset rendering content
1554        properties.setTranslationY(-2000); // offset rendering content
1555    });
1556    EXPECT_EQ(190u, observedData.layerWidth);
1557    EXPECT_EQ(200u, observedData.layerHeight);
1558    EXPECT_EQ(Rect(190, 200), observedData.rectClippedBounds)
1559            << "expect content to be clipped to screen area";
1560    Matrix4 expected;
1561    expected.loadTranslate(0, -2000, 0);
1562    EXPECT_MATRIX_APPROX_EQ(expected, observedData.rectMatrix)
1563            << "expect content to be translated as part of being clipped";
1564}
1565
1566TEST(FrameBuilder, renderPropSaveLayerAlphaRotate) {
1567    SaveLayerAlphaData observedData;
1568    testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) {
1569        // Translate and rotate the view so that the only visible part is the top left corner of
1570        // the view. It will form an isosceles right triangle with a long side length of 200 at the
1571        // bottom of the viewport.
1572        properties.setTranslationX(100);
1573        properties.setTranslationY(100);
1574        properties.setPivotX(0);
1575        properties.setPivotY(0);
1576        properties.setRotation(45);
1577    });
1578    // ceil(sqrt(2) / 2 * 200) = 142
1579    EXPECT_EQ(142u, observedData.layerWidth);
1580    EXPECT_EQ(142u, observedData.layerHeight);
1581    EXPECT_EQ(Rect(142, 142), observedData.rectClippedBounds);
1582    EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), observedData.rectMatrix);
1583}
1584
1585TEST(FrameBuilder, renderPropSaveLayerAlphaScale) {
1586    SaveLayerAlphaData observedData;
1587    testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) {
1588        properties.setPivotX(0);
1589        properties.setPivotY(0);
1590        properties.setScaleX(2);
1591        properties.setScaleY(0.5f);
1592    });
1593    EXPECT_EQ(100u, observedData.layerWidth);
1594    EXPECT_EQ(400u, observedData.layerHeight);
1595    EXPECT_EQ(Rect(100, 400), observedData.rectClippedBounds);
1596    EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), observedData.rectMatrix);
1597}
1598
1599} // namespace uirenderer
1600} // namespace android
1601