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