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