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