blob: dd97dec36bf4769b3a1eee7027d32efd172540e8 [file] [log] [blame]
Stan Iliev021693b2016-10-17 16:26:15 -04001/*
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"
Stan Ilievdb45a4b2016-11-08 14:18:31 -050024#include "pipeline/skia/SkiaPipeline.h"
Stan Iliev021693b2016-10-17 16:26:15 -040025#include "pipeline/skia/SkiaRecordingCanvas.h"
26#include "renderthread/CanvasContext.h"
27#include "tests/common/TestUtils.h"
28#include "SkiaCanvas.h"
Stan Ilievdb45a4b2016-11-08 14:18:31 -050029#include <SkSurface_Base.h>
Stan Iliev021693b2016-10-17 16:26:15 -040030#include <SkLiteRecorder.h>
Stan Ilievdb45a4b2016-11-08 14:18:31 -050031#include <SkClipStack.h>
Stan Iliev021693b2016-10-17 16:26:15 -040032#include <string.h>
33
34
35using namespace android;
36using namespace android::uirenderer;
37using namespace android::uirenderer::renderthread;
38using namespace android::uirenderer::skiapipeline;
39
Stan Iliev021693b2016-10-17 16:26:15 -040040TEST(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
Stan Ilievdb45a4b2016-11-08 14:18:31 -050057namespace {
58
Stan Iliev2f06e8a2016-11-02 15:29:03 -040059static 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);
Stan Iliev021693b2016-10-17 16:26:15 -040064}
65
Stan Iliev2f06e8a2016-11-02 15:29:03 -040066static 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}
Stan Iliev021693b2016-10-17 16:26:15 -040074
Stan Ilievdb45a4b2016-11-08 14:18:31 -050075static 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);
Stan Iliev2f06e8a2016-11-02 15:29:03 -040082 }
Stan Ilievdb45a4b2016-11-08 14:18:31 -050083 });
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) {
Stan Iliev021693b2016-10-17 16:26:15 -0400102
Stan Iliev2f06e8a2016-11-02 15:29:03 -0400103 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 });
Stan Iliev021693b2016-10-17 16:26:15 -0400118
Stan Iliev2f06e8a2016-11-02 15:29:03 -0400119 //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());
Stan Iliev021693b2016-10-17 16:26:15 -0400124}
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
Stan Iliev500a0c32016-10-26 10:30:09 -0400133 auto rootNode = TestUtils::createSkiaNode(0, 0, 1, 1,
Stan Iliev021693b2016-10-17 16:26:15 -0400134 [](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);
Stan Iliev500a0c32016-10-26 10:30:09 -0400142 rootNode->setLayerSurface(surfaceLayer);
Stan Iliev021693b2016-10-17 16:26:15 -0400143
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
Stan Iliev500a0c32016-10-26 10:30:09 -0400156 rootNode->setLayerSurface(sk_sp<SkSurface>());
Stan Iliev021693b2016-10-17 16:26:15 -0400157}
158
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500159namespace {
160class ContextFactory : public IContextFactory {
161public:
162 virtual AnimationContext* createAnimationContext(renderthread::TimeLord& clock) override {
163 return new AnimationContext(clock);
164 }
165};
Stan Iliev021693b2016-10-17 16:26:15 -0400166
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500167inline 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
Stan Iliev021693b2016-10-17 16:26:15 -0400182
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500183RENDERTHREAD_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 }
Stan Iliev021693b2016-10-17 16:26:15 -0400216
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500217 int getIndex() { return mIndex; }
218 protected:
219 int mIndex = 0;
220 };
Stan Iliev021693b2016-10-17 16:26:15 -0400221
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500222 /**
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);
Stan Iliev021693b2016-10-17 16:26:15 -0400237
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500238 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:
Mike Reed6acfe162016-11-18 17:21:09 -0500306 ProjectionTestCanvas(int* drawCounter)
307 : SkCanvas(CANVAS_WIDTH, CANVAS_HEIGHT)
308 , mDrawCounter(drawCounter)
309 {}
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500310 void onDrawArc(const SkRect&, SkScalar startAngle, SkScalar sweepAngle, bool useCenter,
311 const SkPaint&) override {
Mike Reed6acfe162016-11-18 17:21:09 -0500312 EXPECT_EQ(0, (*mDrawCounter)++); //part of painting the layer
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500313 EXPECT_EQ(SkRect::MakeLTRB(0, 0, LAYER_WIDTH, LAYER_HEIGHT), getBounds(this));
314 }
315 void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
Mike Reed6acfe162016-11-18 17:21:09 -0500316 EXPECT_EQ(1, (*mDrawCounter)++);
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500317 EXPECT_EQ(SkRect::MakeLTRB(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT), getBounds(this));
318 }
319 void onDrawOval(const SkRect&, const SkPaint&) override {
Mike Reed6acfe162016-11-18 17:21:09 -0500320 EXPECT_EQ(2, (*mDrawCounter)++);
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500321 SkMatrix expectedMatrix;
322 expectedMatrix.setTranslate(100 - SCROLL_X, 100 - SCROLL_Y);
323 EXPECT_EQ(expectedMatrix, getTotalMatrix());
324 EXPECT_EQ(SkRect::MakeLTRB(-85, -80, 295, 300), getLocalBounds(this));
325 }
Mike Reed6acfe162016-11-18 17:21:09 -0500326 int* mDrawCounter;
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500327 };
328
329 class ProjectionLayer : public SkSurface_Base {
330 public:
Mike Reed6acfe162016-11-18 17:21:09 -0500331 ProjectionLayer(int* drawCounter)
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500332 : SkSurface_Base(SkImageInfo::MakeN32Premul(LAYER_WIDTH, LAYER_HEIGHT), nullptr)
Mike Reed6acfe162016-11-18 17:21:09 -0500333 , mDrawCounter(drawCounter) {
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500334 }
335 void onDraw(SkCanvas*, SkScalar x, SkScalar y, const SkPaint*) override {
Mike Reed6acfe162016-11-18 17:21:09 -0500336 EXPECT_EQ(3, (*mDrawCounter)++);
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500337 EXPECT_EQ(SkRect::MakeLTRB(100 - SCROLL_X, 100 - SCROLL_Y, 300 - SCROLL_X,
Mike Reed6acfe162016-11-18 17:21:09 -0500338 300 - SCROLL_Y), getBounds(this->getCanvas()));
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500339 }
340 SkCanvas* onNewCanvas() override {
Mike Reed6acfe162016-11-18 17:21:09 -0500341 return new ProjectionTestCanvas(mDrawCounter);
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500342 }
343 sk_sp<SkSurface> onNewSurface(const SkImageInfo&) override {
344 return sk_sp<SkSurface>();
345 }
346 sk_sp<SkImage> onNewImageSnapshot(SkBudgeted, SkCopyPixelsMode) override {
347 return sk_sp<SkImage>();
348 }
349 void onCopyOnWrite(ContentChangeMode) override {}
Mike Reed6acfe162016-11-18 17:21:09 -0500350 int* mDrawCounter;
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500351 };
352
353 auto receiverBackground = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
354 [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
355 properties.setProjectionReceiver(true);
356 // scroll doesn't apply to background, so undone via translationX/Y
357 // NOTE: translationX/Y only! no other transform properties may be set for a proj receiver!
358 properties.setTranslationX(SCROLL_X);
359 properties.setTranslationY(SCROLL_Y);
360
361 canvas.drawRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, SkPaint());
362 }, "B"); //B
363 auto projectingRipple = TestUtils::createSkiaNode(0, 0, LAYER_WIDTH, LAYER_HEIGHT,
364 [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
365 properties.setProjectBackwards(true);
366 properties.setClipToBounds(false);
367 canvas.drawOval(100, 100, 300, 300, SkPaint()); // drawn mostly out of layer bounds
368 }, "R"); //R
369 auto child = TestUtils::createSkiaNode(100, 100, 300, 300,
370 [&projectingRipple](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
371 canvas.drawRenderNode(projectingRipple.get());
372 canvas.drawArc(0, 0, LAYER_WIDTH, LAYER_HEIGHT, 0.0f, 280.0f, true, SkPaint());
373 }, "C"); //C
374 auto parent = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
375 [&receiverBackground, &child](RenderProperties& properties,
376 SkiaRecordingCanvas& canvas) {
377 // Set a rect outline for the projecting ripple to be masked against.
378 properties.mutableOutline().setRoundRect(10, 10, 390, 390, 0, 1.0f);
379 canvas.translate(-SCROLL_X, -SCROLL_Y); // Apply scroll (note: bg undoes this internally)
380 canvas.drawRenderNode(receiverBackground.get());
381 canvas.drawRenderNode(child.get());
382 }, "A"); //A
383
384 //prepareTree is required to find, which receivers have backward projected nodes
385 ContextFactory contextFactory;
386 std::unique_ptr<CanvasContext> canvasContext(CanvasContext::create(
387 renderThread, false, parent.get(), &contextFactory));
388 TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
389 DamageAccumulator damageAccumulator;
390 info.damageAccumulator = &damageAccumulator;
391 info.observer = nullptr;
392 parent->prepareTree(info);
393
Mike Reed6acfe162016-11-18 17:21:09 -0500394 int drawCounter = 0;
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500395 //set a layer after prepareTree to avoid layer logic there
396 child->animatorProperties().mutateLayerProperties().setType(LayerType::RenderLayer);
Mike Reed6acfe162016-11-18 17:21:09 -0500397 sk_sp<SkSurface> surfaceLayer1(new ProjectionLayer(&drawCounter));
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500398 child->setLayerSurface(surfaceLayer1);
399 Matrix4 windowTransform;
400 windowTransform.loadTranslate(100, 100, 0);
401 child->getSkiaLayer()->inverseTransformInWindow.loadInverse(windowTransform);
402
403 LayerUpdateQueue layerUpdateQueue;
404 layerUpdateQueue.enqueueLayerWithDamage(child.get(),
405 android::uirenderer::Rect(LAYER_WIDTH, LAYER_HEIGHT));
406 SkiaPipeline::renderLayersImpl(layerUpdateQueue, true);
Mike Reed6acfe162016-11-18 17:21:09 -0500407 EXPECT_EQ(1, drawCounter); //assert index 0 is drawn on the layer
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500408
Mike Reed6acfe162016-11-18 17:21:09 -0500409 RenderNodeDrawable drawable(parent.get(), surfaceLayer1->getCanvas(), true);
410 surfaceLayer1->getCanvas()->drawDrawable(&drawable);
411 EXPECT_EQ(4, drawCounter);
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500412
413 // clean up layer pointer, so we can safely destruct RenderNode
414 child->setLayerSurface(nullptr);
415}
416
417RENDERTHREAD_TEST(RenderNodeDrawable, projectionChildScroll) {
418 /* R is backward projected on B.
419 A
420 / \
421 B C
422 |
423 R
424 */
425 static const int SCROLL_X = 500000;
426 static const int SCROLL_Y = 0;
427 static const int CANVAS_WIDTH = 400;
428 static const int CANVAS_HEIGHT = 400;
429 class ProjectionChildScrollTestCanvas : public SkCanvas {
430 public:
431 ProjectionChildScrollTestCanvas() : SkCanvas(CANVAS_WIDTH, CANVAS_HEIGHT) {}
432 void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
433 EXPECT_EQ(0, mIndex++);
434 EXPECT_TRUE(getTotalMatrix().isIdentity());
435 }
436 void onDrawOval(const SkRect&, const SkPaint&) override {
437 EXPECT_EQ(1, mIndex++);
438 EXPECT_EQ(SkRect::MakeWH(CANVAS_WIDTH, CANVAS_HEIGHT), getBounds(this));
439 EXPECT_TRUE(getTotalMatrix().isIdentity());
440 }
441 int mIndex = 0;
442 };
443
444 auto receiverBackground = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
445 [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
446 properties.setProjectionReceiver(true);
447 canvas.drawRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, SkPaint());
448 }, "B"); //B
449 auto projectingRipple = TestUtils::createSkiaNode(0, 0, 200, 200,
450 [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
451 // scroll doesn't apply to background, so undone via translationX/Y
452 // NOTE: translationX/Y only! no other transform properties may be set for a proj receiver!
453 properties.setTranslationX(SCROLL_X);
454 properties.setTranslationY(SCROLL_Y);
455 properties.setProjectBackwards(true);
456 properties.setClipToBounds(false);
457 canvas.drawOval(0, 0, 200, 200, SkPaint());
458 }, "R"); //R
459 auto child = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
460 [&projectingRipple](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
461 // Record time clip will be ignored by projectee
462 canvas.clipRect(100, 100, 300, 300, SkRegion::kIntersect_Op);
463
464 canvas.translate(-SCROLL_X, -SCROLL_Y); // Apply scroll (note: bg undoes this internally)
465 canvas.drawRenderNode(projectingRipple.get());
466 }, "C"); //C
467 auto parent = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
468 [&receiverBackground, &child](RenderProperties& properties,
469 SkiaRecordingCanvas& canvas) {
470 canvas.drawRenderNode(receiverBackground.get());
471 canvas.drawRenderNode(child.get());
472 }, "A"); //A
473
474 //prepareTree is required to find, which receivers have backward projected nodes
475 ContextFactory contextFactory;
476 std::unique_ptr<CanvasContext> canvasContext(CanvasContext::create(
477 renderThread, false, parent.get(), &contextFactory));
478 TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
479 DamageAccumulator damageAccumulator;
480 info.damageAccumulator = &damageAccumulator;
481 info.observer = nullptr;
482 parent->prepareTree(info);
483
Mike Reed6acfe162016-11-18 17:21:09 -0500484 std::unique_ptr<ProjectionChildScrollTestCanvas> canvas(new ProjectionChildScrollTestCanvas());
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500485 RenderNodeDrawable drawable(parent.get(), canvas.get(), true);
486 canvas->drawDrawable(&drawable);
487 EXPECT_EQ(2, canvas->mIndex);
488}
489
490namespace {
491static int drawNode(RenderThread& renderThread, const sp<RenderNode>& renderNode)
492{
493 ContextFactory contextFactory;
494 std::unique_ptr<CanvasContext> canvasContext(CanvasContext::create(
495 renderThread, false, renderNode.get(), &contextFactory));
496 TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
497 DamageAccumulator damageAccumulator;
498 info.damageAccumulator = &damageAccumulator;
499 info.observer = nullptr;
500 renderNode->prepareTree(info);
501
502 //create a canvas not backed by any device/pixels, but with dimensions to avoid quick rejection
503 ZReorderCanvas canvas(100, 100);
504 RenderNodeDrawable drawable(renderNode.get(), &canvas, false);
505 canvas.drawDrawable(&drawable);
506 return canvas.getIndex();
507}
508}
509
510RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderProjectedInMiddle) {
511 /* R is backward projected on B
512 A
513 / \
514 B C
515 |
516 R
517 */
518 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
519 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
520 drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
521 props.setProjectionReceiver(true);
522 } ); //nodeB
523 drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
524 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
525 props.setProjectBackwards(true);
526 props.setClipToBounds(false);
527 } ); //nodeR
528 } ); //nodeC
529 }); //nodeA
530 EXPECT_EQ(3, drawNode(renderThread, nodeA));
531}
532
533RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderProjectLast) {
534 /* R is backward projected on E
535 A
536 / | \
537 / | \
538 B C E
539 |
540 R
541 */
542 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
543 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
544 drawOrderedNode(&canvas, 0, nullptr); //nodeB
545 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
546 drawOrderedNode(&canvas, 3, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //drawn as 2
547 props.setProjectBackwards(true);
548 props.setClipToBounds(false);
549 } ); //nodeR
550 } ); //nodeC
551 drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //drawn as 3
552 props.setProjectionReceiver(true);
553 } ); //nodeE
554 }); //nodeA
555 EXPECT_EQ(4, drawNode(renderThread, nodeA));
556}
557
558RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderNoReceivable) {
559 /* R is backward projected without receiver
560 A
561 / \
562 B C
563 |
564 R
565 */
566 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
567 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
568 drawOrderedNode(&canvas, 0, nullptr); //nodeB
569 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
570 drawOrderedNode(&canvas, 255, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
571 //not having a projection receiver is an undefined behavior
572 props.setProjectBackwards(true);
573 props.setClipToBounds(false);
574 } ); //nodeR
575 } ); //nodeC
576 }); //nodeA
577 EXPECT_EQ(2, drawNode(renderThread, nodeA));
578}
579
580RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderParentReceivable) {
581 /* R is backward projected on C
582 A
583 / \
584 B C
585 |
586 R
587 */
588 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
589 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
590 drawOrderedNode(&canvas, 0, nullptr); //nodeB
591 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
592 props.setProjectionReceiver(true);
593 drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
594 props.setProjectBackwards(true);
595 props.setClipToBounds(false);
596 } ); //nodeR
597 } ); //nodeC
598 }); //nodeA
599 EXPECT_EQ(3, drawNode(renderThread, nodeA));
600}
601
602RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderSameNodeReceivable) {
603 /* R is backward projected on R
604 A
605 / \
606 B C
607 |
608 R
609 */
610 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
611 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
612 drawOrderedNode(&canvas, 0, nullptr); //nodeB
613 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
614 drawOrderedNode(&canvas, 255, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
615 //having a node that is projected on itself is an undefined/unexpected behavior
616 props.setProjectionReceiver(true);
617 props.setProjectBackwards(true);
618 props.setClipToBounds(false);
619 } ); //nodeR
620 } ); //nodeC
621 }); //nodeA
622 EXPECT_EQ(2, drawNode(renderThread, nodeA));
623}
624
625//Note: the outcome for this test is different in HWUI
626RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderProjectedSibling) {
627 /* R is set to project on B, but R is not drawn because projecting on a sibling is not allowed.
628 A
629 /|\
630 / | \
631 B C R
632 */
633 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
634 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
635 drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
636 props.setProjectionReceiver(true);
637 } ); //nodeB
638 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
639 } ); //nodeC
640 drawOrderedNode(&canvas, 255, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
641 props.setProjectBackwards(true);
642 props.setClipToBounds(false);
643 } ); //nodeR
644 }); //nodeA
645 EXPECT_EQ(2, drawNode(renderThread, nodeA));
646}
647
648RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderProjectedSibling2) {
649 /* R is set to project on B, but R is not drawn because projecting on a sibling is not allowed.
650 A
651 |
652 G
653 /|\
654 / | \
655 B C R
656 */
657 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
658 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
659 drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
660 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
661 props.setProjectionReceiver(true);
662 } ); //nodeB
663 drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
664 } ); //nodeC
665 drawOrderedNode(&canvas, 255, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
666 props.setProjectBackwards(true);
667 props.setClipToBounds(false);
668 } ); //nodeR
669 } ); //nodeG
670 }); //nodeA
671 EXPECT_EQ(3, drawNode(renderThread, nodeA));
672}
673
674RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderGrandparentReceivable) {
675 /* R is backward projected on B
676 A
677 |
678 B
679 |
680 C
681 |
682 R
683 */
684 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
685 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
686 drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
687 props.setProjectionReceiver(true);
688 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
689 drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
690 props.setProjectBackwards(true);
691 props.setClipToBounds(false);
692 } ); //nodeR
693 } ); //nodeC
694 } ); //nodeB
695 }); //nodeA
696 EXPECT_EQ(3, drawNode(renderThread, nodeA));
697}
698
699RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderTwoReceivables) {
700 /* B and G are receivables, R is backward projected
701 A
702 / \
703 B C
704 / \
705 G R
706 */
707 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
708 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
709 drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //B
710 props.setProjectionReceiver(true);
711 } ); //nodeB
712 drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //C
713 drawOrderedNode(&canvas, 3, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //G
714 props.setProjectionReceiver(true);
715 } ); //nodeG
716 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //R
717 props.setProjectBackwards(true);
718 props.setClipToBounds(false);
719 } ); //nodeR
720 } ); //nodeC
721 }); //nodeA
722 EXPECT_EQ(4, drawNode(renderThread, nodeA));
723}
724
725RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderTwoReceivablesLikelyScenario) {
726 /* B and G are receivables, G is backward projected
727 A
728 / \
729 B C
730 / \
731 G R
732 */
733 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
734 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
735 drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //B
736 props.setProjectionReceiver(true);
737 } ); //nodeB
738 drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //C
739 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //G
740 props.setProjectionReceiver(true);
741 props.setProjectBackwards(true);
742 props.setClipToBounds(false);
743 } ); //nodeG
744 drawOrderedNode(&canvas, 3, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //R
745 } ); //nodeR
746 } ); //nodeC
747 }); //nodeA
748 EXPECT_EQ(4, drawNode(renderThread, nodeA));
749}
750
751RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderTwoReceivablesDeeper) {
752 /* B and G are receivables, R is backward projected
753 A
754 / \
755 B C
756 / \
757 G D
758 |
759 R
760 */
761 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
762 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
763 drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //B
764 props.setProjectionReceiver(true);
765 } ); //nodeB
766 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //C
767 drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //G
768 props.setProjectionReceiver(true);
769 } ); //nodeG
770 drawOrderedNode(&canvas, 4, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //D
771 drawOrderedNode(&canvas, 3, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //R
772 props.setProjectBackwards(true);
773 props.setClipToBounds(false);
774 } ); //nodeR
775 } ); //nodeD
776 } ); //nodeC
777 }); //nodeA
778 EXPECT_EQ(5, drawNode(renderThread, nodeA));
Stan Iliev021693b2016-10-17 16:26:15 -0400779}