RenderNodeDrawableTests.cpp revision 52771272f4f018f4fc6846224bf047497e784af1
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#include <VectorDrawable.h>
19
20#include "AnimationContext.h"
21#include "DamageAccumulator.h"
22#include "IContextFactory.h"
23#include "pipeline/skia/SkiaDisplayList.h"
24#include "pipeline/skia/SkiaPipeline.h"
25#include "pipeline/skia/SkiaRecordingCanvas.h"
26#include "renderthread/CanvasContext.h"
27#include "tests/common/TestUtils.h"
28#include "SkiaCanvas.h"
29#include <SkSurface_Base.h>
30#include <SkLiteRecorder.h>
31#include <SkClipStack.h>
32#include "FatalTestCanvas.h"
33#include <string.h>
34
35
36using namespace android;
37using namespace android::uirenderer;
38using namespace android::uirenderer::renderthread;
39using namespace android::uirenderer::skiapipeline;
40
41TEST(RenderNodeDrawable, create) {
42    auto rootNode = TestUtils::createNode(0, 0, 200, 400,
43            [](RenderProperties& props, Canvas& canvas) {
44                canvas.drawColor(Color::Red_500, SkBlendMode::kSrcOver);
45            });
46
47    auto skLiteDL = SkLiteDL::New(SkRect::MakeWH(1, 1));
48    SkLiteRecorder canvas;
49    canvas.reset(skLiteDL.get());
50    canvas.translate(100, 100);
51    RenderNodeDrawable drawable(rootNode.get(), &canvas);
52
53    ASSERT_EQ(drawable.getRenderNode(), rootNode.get());
54    ASSERT_EQ(&drawable.getNodeProperties(), &rootNode->properties());
55    ASSERT_EQ(drawable.getRecordedMatrix(), canvas.getTotalMatrix());
56}
57
58namespace {
59
60static void drawOrderedRect(Canvas* canvas, uint8_t expectedDrawOrder) {
61    SkPaint paint;
62    // order put in blue channel, transparent so overlapped content doesn't get rejected
63    paint.setColor(SkColorSetARGB(1, 0, 0, expectedDrawOrder));
64    canvas->drawRect(0, 0, 100, 100, paint);
65}
66
67static void drawOrderedNode(Canvas* canvas, uint8_t expectedDrawOrder, float z) {
68    auto node = TestUtils::createSkiaNode(0, 0, 100, 100,
69            [expectedDrawOrder, z](RenderProperties& props, SkiaRecordingCanvas& canvas) {
70        drawOrderedRect(&canvas, expectedDrawOrder);
71        props.setTranslationZ(z);
72    });
73    canvas->drawRenderNode(node.get()); // canvas takes reference/sole ownership
74}
75
76static void drawOrderedNode(Canvas* canvas, uint8_t expectedDrawOrder,
77        std::function<void(RenderProperties& props, SkiaRecordingCanvas& canvas)> setup) {
78    auto node = TestUtils::createSkiaNode(0, 0, 100, 100,
79            [expectedDrawOrder, setup](RenderProperties& props, SkiaRecordingCanvas& canvas) {
80        drawOrderedRect(&canvas, expectedDrawOrder);
81        if (setup) {
82             setup(props, canvas);
83        }
84    });
85    canvas->drawRenderNode(node.get()); // canvas takes reference/sole ownership
86}
87
88class ZReorderCanvas : public SkCanvas {
89public:
90    ZReorderCanvas(int width, int height) : SkCanvas(width, height) {}
91    void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
92        int expectedOrder = SkColorGetB(paint.getColor()); // extract order from blue channel
93        EXPECT_EQ(expectedOrder, mDrawCounter++) << "An op was drawn out of order";
94    }
95    int getIndex() { return mDrawCounter; }
96protected:
97    int mDrawCounter = 0;
98};
99
100} // end anonymous namespace
101
102TEST(RenderNodeDrawable, zReorder) {
103
104    auto parent = TestUtils::createSkiaNode(0, 0, 100, 100,
105            [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
106        canvas.insertReorderBarrier(true);
107        canvas.insertReorderBarrier(false);
108        drawOrderedNode(&canvas, 0, 10.0f); // in reorder=false at this point, so played inorder
109        drawOrderedRect(&canvas, 1);
110        canvas.insertReorderBarrier(true);
111        drawOrderedNode(&canvas, 6, 2.0f);
112        drawOrderedRect(&canvas, 3);
113        drawOrderedNode(&canvas, 4, 0.0f);
114        drawOrderedRect(&canvas, 5);
115        drawOrderedNode(&canvas, 2, -2.0f);
116        drawOrderedNode(&canvas, 7, 2.0f);
117        canvas.insertReorderBarrier(false);
118        drawOrderedRect(&canvas, 8);
119        drawOrderedNode(&canvas, 9, -10.0f); // in reorder=false at this point, so played inorder
120        canvas.insertReorderBarrier(true); //reorder a node ahead of drawrect op
121        drawOrderedRect(&canvas, 11);
122        drawOrderedNode(&canvas, 10, -1.0f);
123        canvas.insertReorderBarrier(false);
124        canvas.insertReorderBarrier(true); //test with two empty reorder sections
125        canvas.insertReorderBarrier(true);
126        canvas.insertReorderBarrier(false);
127        drawOrderedRect(&canvas, 12);
128    });
129
130    //create a canvas not backed by any device/pixels, but with dimensions to avoid quick rejection
131    ZReorderCanvas canvas(100, 100);
132    RenderNodeDrawable drawable(parent.get(), &canvas, false);
133    canvas.drawDrawable(&drawable);
134    EXPECT_EQ(13, canvas.getIndex());
135}
136
137TEST(RenderNodeDrawable, composeOnLayer)
138{
139    auto surface = SkSurface::MakeRasterN32Premul(1, 1);
140    SkCanvas& canvas = *surface->getCanvas();
141    canvas.drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver);
142    ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
143
144    auto rootNode = TestUtils::createSkiaNode(0, 0, 1, 1,
145        [](RenderProperties& props, SkiaRecordingCanvas& recorder) {
146            recorder.drawColor(SK_ColorRED, SkBlendMode::kSrcOver);
147        });
148
149    //attach a layer to the render node
150    auto surfaceLayer = SkSurface::MakeRasterN32Premul(1, 1);
151    auto canvas2 = surfaceLayer->getCanvas();
152    canvas2->drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver);
153    rootNode->setLayerSurface(surfaceLayer);
154
155    RenderNodeDrawable drawable1(rootNode.get(), &canvas, false);
156    canvas.drawDrawable(&drawable1);
157    ASSERT_EQ(SK_ColorRED, TestUtils::getColor(surface, 0, 0));
158
159    RenderNodeDrawable drawable2(rootNode.get(), &canvas, true);
160    canvas.drawDrawable(&drawable2);
161    ASSERT_EQ(SK_ColorWHITE, TestUtils::getColor(surface, 0, 0));
162
163    RenderNodeDrawable drawable3(rootNode.get(), &canvas, false);
164    canvas.drawDrawable(&drawable3);
165    ASSERT_EQ(SK_ColorRED, TestUtils::getColor(surface, 0, 0));
166
167    rootNode->setLayerSurface(sk_sp<SkSurface>());
168}
169
170namespace {
171class ContextFactory : public IContextFactory {
172public:
173    virtual AnimationContext* createAnimationContext(renderthread::TimeLord& clock) override {
174        return new AnimationContext(clock);
175    }
176};
177} // end anonymous namespace
178
179RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorder) {
180    static const int SCROLL_X = 5;
181    static const int SCROLL_Y = 10;
182    class ProjectionTestCanvas : public SkCanvas {
183    public:
184        ProjectionTestCanvas(int width, int height) : SkCanvas(width, height) {}
185        void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
186            const int index = mDrawCounter++;
187            SkMatrix expectedMatrix;;
188            switch (index) {
189            case 0:  //this is node "B"
190                EXPECT_EQ(SkRect::MakeWH(100, 100), rect);
191                EXPECT_EQ(SK_ColorWHITE, paint.getColor());
192                expectedMatrix.reset();
193                EXPECT_EQ(SkRect::MakeLTRB(0, 0, 100, 100), TestUtils::getClipBounds(this));
194                break;
195            case 1:  //this is node "P"
196                EXPECT_EQ(SkRect::MakeLTRB(-10, -10, 60, 60), rect);
197                EXPECT_EQ(SK_ColorDKGRAY, paint.getColor());
198                expectedMatrix.setTranslate(50 - SCROLL_X, 50 - SCROLL_Y);
199                EXPECT_EQ(SkRect::MakeLTRB(-35, -30, 45, 50), TestUtils::getLocalClipBounds(this));
200                break;
201            case 2:  //this is node "C"
202                EXPECT_EQ(SkRect::MakeWH(100, 50), rect);
203                EXPECT_EQ(SK_ColorBLUE, paint.getColor());
204                expectedMatrix.setTranslate(-SCROLL_X, 50 - SCROLL_Y);
205                EXPECT_EQ(SkRect::MakeLTRB(0, 40, 95, 90), TestUtils::getClipBounds(this));
206                break;
207            default:
208                ADD_FAILURE();
209            }
210            EXPECT_EQ(expectedMatrix, getTotalMatrix());
211        }
212
213        int getIndex() { return mDrawCounter; }
214    protected:
215        int mDrawCounter = 0;
216    };
217
218    /**
219     * Construct a tree of nodes, where the root (A) has a receiver background (B), and a child (C)
220     * with a projecting child (P) of its own. P would normally draw between B and C's "background"
221     * draw, but because it is projected backwards, it's drawn in between B and C.
222     *
223     * The parent is scrolled by SCROLL_X/SCROLL_Y, but this does not affect the background
224     * (which isn't affected by scroll).
225     */
226    auto receiverBackground = TestUtils::createSkiaNode(0, 0, 100, 100,
227            [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
228        properties.setProjectionReceiver(true);
229        // scroll doesn't apply to background, so undone via translationX/Y
230        // NOTE: translationX/Y only! no other transform properties may be set for a proj receiver!
231        properties.setTranslationX(SCROLL_X);
232        properties.setTranslationY(SCROLL_Y);
233
234        SkPaint paint;
235        paint.setColor(SK_ColorWHITE);
236        canvas.drawRect(0, 0, 100, 100, paint);
237    }, "B");
238
239    auto projectingRipple = TestUtils::createSkiaNode(50, 0, 100, 50,
240            [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
241        properties.setProjectBackwards(true);
242        properties.setClipToBounds(false);
243        SkPaint paint;
244        paint.setColor(SK_ColorDKGRAY);
245        canvas.drawRect(-10, -10, 60, 60, paint);
246    }, "P");
247    auto child = TestUtils::createSkiaNode(0, 50, 100, 100,
248            [&projectingRipple](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
249        SkPaint paint;
250        paint.setColor(SK_ColorBLUE);
251        canvas.drawRect(0, 0, 100, 50, paint);
252        canvas.drawRenderNode(projectingRipple.get());
253    }, "C");
254    auto parent = TestUtils::createSkiaNode(0, 0, 100, 100,
255            [&receiverBackground, &child](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
256        // Set a rect outline for the projecting ripple to be masked against.
257        properties.mutableOutline().setRoundRect(10, 10, 90, 90, 5, 1.0f);
258
259        canvas.save(SaveFlags::MatrixClip);
260        canvas.translate(-SCROLL_X, -SCROLL_Y); // Apply scroll (note: bg undoes this internally)
261        canvas.drawRenderNode(receiverBackground.get());
262        canvas.drawRenderNode(child.get());
263        canvas.restore();
264    }, "A");
265    ContextFactory contextFactory;
266    std::unique_ptr<CanvasContext> canvasContext(CanvasContext::create(
267            renderThread, false, parent.get(), &contextFactory));
268    TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
269    DamageAccumulator damageAccumulator;
270    info.damageAccumulator = &damageAccumulator;
271    info.observer = nullptr;
272    parent->prepareTree(info);
273
274    //parent(A)             -> (receiverBackground, child)
275    //child(C)              -> (rect[0, 0, 100, 50], projectingRipple)
276    //projectingRipple(P)   -> (rect[-10, -10, 60, 60]) -> projects backwards
277    //receiverBackground(B) -> (rect[0, 0, 100, 100]) -> projection receiver
278
279    //create a canvas not backed by any device/pixels, but with dimensions to avoid quick rejection
280    ProjectionTestCanvas canvas(100, 100);
281    RenderNodeDrawable drawable(parent.get(), &canvas, true);
282    canvas.drawDrawable(&drawable);
283    EXPECT_EQ(3, canvas.getIndex());
284}
285
286RENDERTHREAD_TEST(RenderNodeDrawable, projectionHwLayer) {
287    /* R is backward projected on B and C is a layer.
288                A
289               / \
290              B   C
291                  |
292                  R
293    */
294    static const int SCROLL_X = 5;
295    static const int SCROLL_Y = 10;
296    static const int CANVAS_WIDTH = 400;
297    static const int CANVAS_HEIGHT = 400;
298    static const int LAYER_WIDTH = 200;
299    static const int LAYER_HEIGHT = 200;
300    class ProjectionTestCanvas : public SkCanvas {
301    public:
302        ProjectionTestCanvas(int* drawCounter)
303            : SkCanvas(CANVAS_WIDTH, CANVAS_HEIGHT)
304            , mDrawCounter(drawCounter)
305        {}
306        void onDrawArc(const SkRect&, SkScalar startAngle, SkScalar sweepAngle, bool useCenter,
307                const SkPaint&) override {
308            EXPECT_EQ(0, (*mDrawCounter)++); //part of painting the layer
309            EXPECT_EQ(SkRect::MakeLTRB(0, 0, LAYER_WIDTH, LAYER_HEIGHT), TestUtils::getClipBounds(this));
310        }
311        void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
312            EXPECT_EQ(1, (*mDrawCounter)++);
313            EXPECT_EQ(SkRect::MakeLTRB(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT), TestUtils::getClipBounds(this));
314        }
315        void onDrawOval(const SkRect&, const SkPaint&) override {
316            EXPECT_EQ(2, (*mDrawCounter)++);
317            SkMatrix expectedMatrix;
318            expectedMatrix.setTranslate(100 - SCROLL_X, 100 - SCROLL_Y);
319            EXPECT_EQ(expectedMatrix, getTotalMatrix());
320            EXPECT_EQ(SkRect::MakeLTRB(-85, -80, 295, 300), TestUtils::getLocalClipBounds(this));
321        }
322        int* mDrawCounter;
323    };
324
325    class ProjectionLayer : public SkSurface_Base {
326    public:
327        ProjectionLayer(int* drawCounter)
328            : SkSurface_Base(SkImageInfo::MakeN32Premul(LAYER_WIDTH, LAYER_HEIGHT), nullptr)
329            , mDrawCounter(drawCounter) {
330        }
331        void onDraw(SkCanvas*, SkScalar x, SkScalar y, const SkPaint*) override {
332            EXPECT_EQ(3, (*mDrawCounter)++);
333            EXPECT_EQ(SkRect::MakeLTRB(100 - SCROLL_X, 100 - SCROLL_Y, 300 - SCROLL_X,
334                   300 - SCROLL_Y), TestUtils::getClipBounds(this->getCanvas()));
335        }
336        SkCanvas* onNewCanvas() override {
337            return new ProjectionTestCanvas(mDrawCounter);
338        }
339        sk_sp<SkSurface> onNewSurface(const SkImageInfo&) override {
340            return sk_sp<SkSurface>();
341        }
342        sk_sp<SkImage> onNewImageSnapshot(SkBudgeted, SkCopyPixelsMode) override {
343            return sk_sp<SkImage>();
344        }
345        void onCopyOnWrite(ContentChangeMode) override {}
346        int* mDrawCounter;
347    };
348
349    auto receiverBackground = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
350            [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
351        properties.setProjectionReceiver(true);
352        // scroll doesn't apply to background, so undone via translationX/Y
353        // NOTE: translationX/Y only! no other transform properties may be set for a proj receiver!
354        properties.setTranslationX(SCROLL_X);
355        properties.setTranslationY(SCROLL_Y);
356
357        canvas.drawRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, SkPaint());
358    }, "B"); //B
359    auto projectingRipple = TestUtils::createSkiaNode(0, 0, LAYER_WIDTH, LAYER_HEIGHT,
360            [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
361        properties.setProjectBackwards(true);
362        properties.setClipToBounds(false);
363        canvas.drawOval(100, 100, 300, 300, SkPaint()); // drawn mostly out of layer bounds
364    }, "R"); //R
365    auto child = TestUtils::createSkiaNode(100, 100, 300, 300,
366            [&projectingRipple](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
367        canvas.drawRenderNode(projectingRipple.get());
368        canvas.drawArc(0, 0, LAYER_WIDTH, LAYER_HEIGHT, 0.0f, 280.0f, true, SkPaint());
369    }, "C"); //C
370    auto parent = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
371            [&receiverBackground, &child](RenderProperties& properties,
372            SkiaRecordingCanvas& canvas) {
373        // Set a rect outline for the projecting ripple to be masked against.
374        properties.mutableOutline().setRoundRect(10, 10, 390, 390, 0, 1.0f);
375        canvas.translate(-SCROLL_X, -SCROLL_Y); // Apply scroll (note: bg undoes this internally)
376        canvas.drawRenderNode(receiverBackground.get());
377        canvas.drawRenderNode(child.get());
378    }, "A"); //A
379
380    //prepareTree is required to find, which receivers have backward projected nodes
381    ContextFactory contextFactory;
382    std::unique_ptr<CanvasContext> canvasContext(CanvasContext::create(
383            renderThread, false, parent.get(), &contextFactory));
384    TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
385    DamageAccumulator damageAccumulator;
386    info.damageAccumulator = &damageAccumulator;
387    info.observer = nullptr;
388    parent->prepareTree(info);
389
390    int drawCounter = 0;
391    //set a layer after prepareTree to avoid layer logic there
392    child->animatorProperties().mutateLayerProperties().setType(LayerType::RenderLayer);
393    sk_sp<SkSurface> surfaceLayer1(new ProjectionLayer(&drawCounter));
394    child->setLayerSurface(surfaceLayer1);
395    Matrix4 windowTransform;
396    windowTransform.loadTranslate(100, 100, 0);
397    child->getSkiaLayer()->inverseTransformInWindow.loadInverse(windowTransform);
398
399    LayerUpdateQueue layerUpdateQueue;
400    layerUpdateQueue.enqueueLayerWithDamage(child.get(),
401            android::uirenderer::Rect(LAYER_WIDTH, LAYER_HEIGHT));
402    SkiaPipeline::renderLayersImpl(layerUpdateQueue, true);
403    EXPECT_EQ(1, drawCounter);  //assert index 0 is drawn on the layer
404
405    RenderNodeDrawable drawable(parent.get(), surfaceLayer1->getCanvas(), true);
406    surfaceLayer1->getCanvas()->drawDrawable(&drawable);
407    EXPECT_EQ(4, drawCounter);
408
409    // clean up layer pointer, so we can safely destruct RenderNode
410    child->setLayerSurface(nullptr);
411}
412
413RENDERTHREAD_TEST(RenderNodeDrawable, projectionChildScroll) {
414    /* R is backward projected on B.
415                A
416               / \
417              B   C
418                  |
419                  R
420    */
421    static const int SCROLL_X = 500000;
422    static const int SCROLL_Y = 0;
423    static const int CANVAS_WIDTH = 400;
424    static const int CANVAS_HEIGHT = 400;
425    class ProjectionChildScrollTestCanvas : public SkCanvas {
426    public:
427        ProjectionChildScrollTestCanvas() : SkCanvas(CANVAS_WIDTH, CANVAS_HEIGHT) {}
428        void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
429            EXPECT_EQ(0, mDrawCounter++);
430            EXPECT_TRUE(getTotalMatrix().isIdentity());
431        }
432        void onDrawOval(const SkRect&, const SkPaint&) override {
433            EXPECT_EQ(1, mDrawCounter++);
434            EXPECT_EQ(SkRect::MakeWH(CANVAS_WIDTH, CANVAS_HEIGHT), TestUtils::getClipBounds(this));
435            EXPECT_TRUE(getTotalMatrix().isIdentity());
436        }
437        int mDrawCounter = 0;
438    };
439
440    auto receiverBackground = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
441            [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
442        properties.setProjectionReceiver(true);
443        canvas.drawRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, SkPaint());
444    }, "B"); //B
445    auto projectingRipple = TestUtils::createSkiaNode(0, 0, 200, 200,
446            [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
447        // scroll doesn't apply to background, so undone via translationX/Y
448        // NOTE: translationX/Y only! no other transform properties may be set for a proj receiver!
449        properties.setTranslationX(SCROLL_X);
450        properties.setTranslationY(SCROLL_Y);
451        properties.setProjectBackwards(true);
452        properties.setClipToBounds(false);
453        canvas.drawOval(0, 0, 200, 200, SkPaint());
454    }, "R"); //R
455    auto child = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
456            [&projectingRipple](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
457        // Record time clip will be ignored by projectee
458        canvas.clipRect(100, 100, 300, 300, kIntersect_SkClipOp);
459
460        canvas.translate(-SCROLL_X, -SCROLL_Y); // Apply scroll (note: bg undoes this internally)
461        canvas.drawRenderNode(projectingRipple.get());
462    }, "C"); //C
463    auto parent = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
464            [&receiverBackground, &child](RenderProperties& properties,
465            SkiaRecordingCanvas& canvas) {
466        canvas.drawRenderNode(receiverBackground.get());
467        canvas.drawRenderNode(child.get());
468    }, "A"); //A
469
470    //prepareTree is required to find, which receivers have backward projected nodes
471    ContextFactory contextFactory;
472    std::unique_ptr<CanvasContext> canvasContext(CanvasContext::create(
473            renderThread, false, parent.get(), &contextFactory));
474    TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
475    DamageAccumulator damageAccumulator;
476    info.damageAccumulator = &damageAccumulator;
477    info.observer = nullptr;
478    parent->prepareTree(info);
479
480    std::unique_ptr<ProjectionChildScrollTestCanvas> canvas(new ProjectionChildScrollTestCanvas());
481    RenderNodeDrawable drawable(parent.get(), canvas.get(), true);
482    canvas->drawDrawable(&drawable);
483    EXPECT_EQ(2, canvas->mDrawCounter);
484}
485
486namespace {
487static int drawNode(RenderThread& renderThread, const sp<RenderNode>& renderNode)
488{
489    ContextFactory contextFactory;
490    std::unique_ptr<CanvasContext> canvasContext(CanvasContext::create(
491            renderThread, false, renderNode.get(), &contextFactory));
492    TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
493    DamageAccumulator damageAccumulator;
494    info.damageAccumulator = &damageAccumulator;
495    info.observer = nullptr;
496    renderNode->prepareTree(info);
497
498    //create a canvas not backed by any device/pixels, but with dimensions to avoid quick rejection
499    ZReorderCanvas canvas(100, 100);
500    RenderNodeDrawable drawable(renderNode.get(), &canvas, false);
501    canvas.drawDrawable(&drawable);
502    return canvas.getIndex();
503}
504}
505
506RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderProjectedInMiddle) {
507    /* R is backward projected on B
508                A
509               / \
510              B   C
511                  |
512                  R
513    */
514    auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
515            [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
516        drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
517            props.setProjectionReceiver(true);
518        } ); //nodeB
519        drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
520            drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
521                props.setProjectBackwards(true);
522                props.setClipToBounds(false);
523            } ); //nodeR
524        } ); //nodeC
525    }); //nodeA
526    EXPECT_EQ(3, drawNode(renderThread, nodeA));
527}
528
529RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderProjectLast) {
530    /* R is backward projected on E
531                  A
532                / | \
533               /  |  \
534              B   C   E
535                  |
536                  R
537    */
538    auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
539            [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
540        drawOrderedNode(&canvas, 0, nullptr); //nodeB
541        drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
542            drawOrderedNode(&canvas, 3, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //drawn as 2
543                props.setProjectBackwards(true);
544                props.setClipToBounds(false);
545            } ); //nodeR
546        } ); //nodeC
547        drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //drawn as 3
548            props.setProjectionReceiver(true);
549        } ); //nodeE
550    }); //nodeA
551    EXPECT_EQ(4, drawNode(renderThread, nodeA));
552}
553
554RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderNoReceivable) {
555    /* R is backward projected without receiver
556                A
557               / \
558              B   C
559                  |
560                  R
561    */
562     auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
563            [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
564        drawOrderedNode(&canvas, 0, nullptr); //nodeB
565        drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
566            drawOrderedNode(&canvas, 255, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
567                //not having a projection receiver is an undefined behavior
568                props.setProjectBackwards(true);
569                props.setClipToBounds(false);
570            } ); //nodeR
571        } ); //nodeC
572    }); //nodeA
573    EXPECT_EQ(2, drawNode(renderThread, nodeA));
574}
575
576RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderParentReceivable) {
577    /* R is backward projected on C
578                A
579               / \
580              B   C
581                  |
582                  R
583    */
584     auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
585            [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
586        drawOrderedNode(&canvas, 0, nullptr); //nodeB
587        drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
588            props.setProjectionReceiver(true);
589            drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
590                props.setProjectBackwards(true);
591                props.setClipToBounds(false);
592            } ); //nodeR
593        } ); //nodeC
594    }); //nodeA
595    EXPECT_EQ(3, drawNode(renderThread, nodeA));
596}
597
598RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderSameNodeReceivable) {
599    /* R is backward projected on R
600                A
601               / \
602              B   C
603                  |
604                  R
605    */
606     auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
607            [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
608        drawOrderedNode(&canvas, 0, nullptr); //nodeB
609        drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
610            drawOrderedNode(&canvas, 255, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
611                //having a node that is projected on itself is an undefined/unexpected behavior
612                props.setProjectionReceiver(true);
613                props.setProjectBackwards(true);
614                props.setClipToBounds(false);
615            } ); //nodeR
616        } ); //nodeC
617    }); //nodeA
618    EXPECT_EQ(2, drawNode(renderThread, nodeA));
619}
620
621//Note: the outcome for this test is different in HWUI
622RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderProjectedSibling) {
623    /* R is set to project on B, but R is not drawn because projecting on a sibling is not allowed.
624                A
625               /|\
626              / | \
627             B  C  R
628    */
629    auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
630            [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
631        drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
632            props.setProjectionReceiver(true);
633        } ); //nodeB
634        drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
635        } ); //nodeC
636        drawOrderedNode(&canvas, 255, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
637            props.setProjectBackwards(true);
638            props.setClipToBounds(false);
639        } ); //nodeR
640    }); //nodeA
641    EXPECT_EQ(2, drawNode(renderThread, nodeA));
642}
643
644RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderProjectedSibling2) {
645    /* R is set to project on B, but R is not drawn because projecting on a sibling is not allowed.
646                A
647                |
648                G
649               /|\
650              / | \
651             B  C  R
652    */
653    auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
654            [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
655        drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
656            drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
657                props.setProjectionReceiver(true);
658            } ); //nodeB
659            drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
660            } ); //nodeC
661            drawOrderedNode(&canvas, 255, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
662                props.setProjectBackwards(true);
663                props.setClipToBounds(false);
664            } ); //nodeR
665        } ); //nodeG
666    }); //nodeA
667    EXPECT_EQ(3, drawNode(renderThread, nodeA));
668}
669
670RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderGrandparentReceivable) {
671    /* R is backward projected on B
672                A
673                |
674                B
675                |
676                C
677                |
678                R
679    */
680    auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
681            [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
682        drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
683            props.setProjectionReceiver(true);
684            drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
685                drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
686                    props.setProjectBackwards(true);
687                    props.setClipToBounds(false);
688                } ); //nodeR
689            } ); //nodeC
690        } ); //nodeB
691    }); //nodeA
692    EXPECT_EQ(3, drawNode(renderThread, nodeA));
693}
694
695RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderTwoReceivables) {
696    /* B and G are receivables, R is backward projected
697                A
698               / \
699              B   C
700                 / \
701                G   R
702    */
703    auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
704            [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
705        drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //B
706            props.setProjectionReceiver(true);
707        } ); //nodeB
708        drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //C
709            drawOrderedNode(&canvas, 3, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //G
710                props.setProjectionReceiver(true);
711            } ); //nodeG
712            drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //R
713                props.setProjectBackwards(true);
714                props.setClipToBounds(false);
715            } ); //nodeR
716        } ); //nodeC
717    }); //nodeA
718    EXPECT_EQ(4, drawNode(renderThread, nodeA));
719}
720
721RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderTwoReceivablesLikelyScenario) {
722    /* B and G are receivables, G is backward projected
723                A
724               / \
725              B   C
726                 / \
727                G   R
728    */
729    auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
730            [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
731        drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //B
732            props.setProjectionReceiver(true);
733        } ); //nodeB
734        drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //C
735            drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //G
736                props.setProjectionReceiver(true);
737                props.setProjectBackwards(true);
738                props.setClipToBounds(false);
739            } ); //nodeG
740            drawOrderedNode(&canvas, 3, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //R
741            } ); //nodeR
742        } ); //nodeC
743    }); //nodeA
744    EXPECT_EQ(4, drawNode(renderThread, nodeA));
745}
746
747RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderTwoReceivablesDeeper) {
748    /* B and G are receivables, R is backward projected
749                A
750               / \
751              B   C
752                 / \
753                G   D
754                    |
755                    R
756    */
757    auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
758            [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
759        drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //B
760            props.setProjectionReceiver(true);
761        } ); //nodeB
762        drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //C
763            drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //G
764                props.setProjectionReceiver(true);
765            } ); //nodeG
766            drawOrderedNode(&canvas, 4, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //D
767                drawOrderedNode(&canvas, 3, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //R
768                    props.setProjectBackwards(true);
769                    props.setClipToBounds(false);
770                } ); //nodeR
771            } ); //nodeD
772        } ); //nodeC
773    }); //nodeA
774    EXPECT_EQ(5, drawNode(renderThread, nodeA));
775}
776
777RENDERTHREAD_TEST(RenderNodeDrawable, simple) {
778    static const int CANVAS_WIDTH = 100;
779    static const int CANVAS_HEIGHT = 200;
780    class SimpleTestCanvas : public TestCanvasBase {
781    public:
782        SimpleTestCanvas() : TestCanvasBase(CANVAS_WIDTH, CANVAS_HEIGHT) {
783        }
784        void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
785            EXPECT_EQ(0, mDrawCounter++);
786        }
787        void onDrawImage(const SkImage*, SkScalar dx, SkScalar dy, const SkPaint*) override {
788            EXPECT_EQ(1, mDrawCounter++);
789        }
790    };
791
792    auto node = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
793            [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
794        sk_sp<Bitmap> bitmap(TestUtils::createBitmap(25, 25));
795        canvas.drawRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, SkPaint());
796        canvas.drawBitmap(*bitmap, 10, 10, nullptr);
797    });
798
799    SimpleTestCanvas canvas;
800    RenderNodeDrawable drawable(node.get(), &canvas, true);
801    canvas.drawDrawable(&drawable);
802    EXPECT_EQ(2, canvas.mDrawCounter);
803}
804
805RENDERTHREAD_TEST(RenderNodeDrawable, colorOp_unbounded) {
806    static const int CANVAS_WIDTH = 200;
807    static const int CANVAS_HEIGHT = 200;
808    class ColorTestCanvas : public TestCanvasBase {
809    public:
810        ColorTestCanvas() : TestCanvasBase(CANVAS_WIDTH, CANVAS_HEIGHT) {
811        }
812        void onDrawPaint(const SkPaint&) {
813            switch (mDrawCounter++) {
814            case 0:
815                // While this mirrors FrameBuilder::colorOp_unbounded, this value is different
816                // because there is no root (root is clipped in SkiaPipeline::renderFrame).
817                // SkiaPipeline.clipped and clip_replace verify the root clip.
818                EXPECT_TRUE(TestUtils::getClipBounds(this).isEmpty());
819                break;
820            case 1:
821                EXPECT_EQ(SkRect::MakeWH(10, 10), TestUtils::getClipBounds(this));
822                break;
823            default:
824                ADD_FAILURE();
825            }
826        }
827    };
828
829    auto unclippedColorView = TestUtils::createSkiaNode(0, 0, 10, 10,
830            [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
831        props.setClipToBounds(false);
832        canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver);
833    });
834
835    auto clippedColorView = TestUtils::createSkiaNode(0, 0, 10, 10,
836            [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
837        canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver);
838    });
839
840    ColorTestCanvas canvas;
841    RenderNodeDrawable drawable(unclippedColorView.get(), &canvas, true);
842    canvas.drawDrawable(&drawable);
843    EXPECT_EQ(1, canvas.mDrawCounter);
844    RenderNodeDrawable drawable2(clippedColorView.get(), &canvas, true);
845    canvas.drawDrawable(&drawable2);
846    EXPECT_EQ(2, canvas.mDrawCounter);
847}
848
849TEST(RenderNodeDrawable, renderNode) {
850    static const int CANVAS_WIDTH = 200;
851    static const int CANVAS_HEIGHT = 200;
852    class RenderNodeTestCanvas : public TestCanvasBase {
853    public:
854        RenderNodeTestCanvas() : TestCanvasBase(CANVAS_WIDTH, CANVAS_HEIGHT) {
855        }
856        void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
857            switch(mDrawCounter++) {
858            case 0:
859                EXPECT_EQ(SkRect::MakeWH(CANVAS_WIDTH, CANVAS_HEIGHT), TestUtils::getClipBounds(this));
860                EXPECT_EQ(SK_ColorDKGRAY, paint.getColor());
861                break;
862            case 1:
863                EXPECT_EQ(SkRect::MakeLTRB(50, 50, 150, 150), TestUtils::getClipBounds(this));
864                EXPECT_EQ(SK_ColorWHITE, paint.getColor());
865                break;
866            default:
867                ADD_FAILURE();
868            }
869        }
870    };
871
872    auto child = TestUtils::createSkiaNode(10, 10, 110, 110,
873            [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
874        SkPaint paint;
875        paint.setColor(SK_ColorWHITE);
876        canvas.drawRect(0, 0, 100, 100, paint);
877    });
878
879    auto parent = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
880            [&child](RenderProperties& props, SkiaRecordingCanvas& canvas) {
881        SkPaint paint;
882        paint.setColor(SK_ColorDKGRAY);
883        canvas.drawRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, paint);
884
885        canvas.save(SaveFlags::MatrixClip);
886        canvas.translate(40, 40);
887        canvas.drawRenderNode(child.get());
888        canvas.restore();
889    });
890
891    RenderNodeTestCanvas canvas;
892    RenderNodeDrawable drawable(parent.get(), &canvas, true);
893    canvas.drawDrawable(&drawable);
894    EXPECT_EQ(2, canvas.mDrawCounter);
895}
896
897